From 5862176764c7a86d5fdd2685122810e14a3d5b02 Mon Sep 17 00:00:00 2001 From: Charlie Groves Date: Wed, 16 Feb 2022 17:11:50 -0500 Subject: [PATCH 1/9] feat(remote): add basic --remote support This is starting from @geekodour's work at https://github.com/neovim/neovim/pull/8326 --- runtime/lua/vim/_editor.lua | 72 +++++++++++++++++++++++++++++++++ src/nvim/api/vim.c | 2 +- src/nvim/main.c | 79 +++++++++++++++++++++++++++++++++++++ src/nvim/main.h | 2 + 4 files changed, 154 insertions(+), 1 deletion(-) diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 3136e36043..5f3d66e108 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -636,6 +636,78 @@ function vim.pretty_print(...) return ... end +local function __rpcrequest(...) + return vim.api.nvim_call_function("rpcrequest", {...}) +end + +function vim._cs_remote(rcid, args) + + local f_silent = false + local f_wait = false + local f_tab = false + local should_exit = true + local command = 'edit ' + + local subcmd = string.sub(args[1],10) + + if subcmd == '' then + -- no flags to set + elseif subcmd == 'tab' then + f_tab = true + elseif subcmd == 'silent' then + f_silent = true + elseif subcmd == 'wait' then + f_wait = true + elseif subcmd == 'wait-silent' then + f_wait = true + f_silent = true + elseif subcmd == 'tab-wait' then + f_tab = true + f_wait = true + elseif subcmd == 'tab-silent' then + f_tab = true + f_silent = true + elseif subcmd == 'tab-wait-silent' then + f_tab = true + f_wait = true + f_silent = true + elseif subcmd == 'send' then + __rpcrequest(rcid, 'nvim_input', args[2]) + return { should_exit = should_exit, tabbed = f_tab, files = 0 } + -- should we show warning if --server doesn't exist in --send and --expr? + elseif subcmd == 'expr' then + local res = __rpcrequest(rcid, 'vim_eval', args[2]) + print(res) + return { should_exit = should_exit, tabbed = f_tab, files = 0 } + else + print('--remote subcommand not found') + end + + table.remove(args,1) + + if not f_silent and rcid == 0 then + print('Remote server does not exist.') + end + + if f_silent and rcid == 0 then + print('Remote server does not exist. starting new server') + should_exit = false + end + + if f_tab then command = 'tabedit ' end + + if rcid ~= 0 then + for _, key in ipairs(args) do + __rpcrequest(rcid, 'nvim_command', command .. key) + end + end + + return { + should_exit = should_exit, + tabbed = f_tab, + files = table.getn(args) + } +end require('vim._meta') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index ebf4f65c91..b82a7553cb 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1997,7 +1997,7 @@ 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(select(1, ...))"); + String s = cstr_to_string("return vim._os_proc_children(select(...))"); Object o = nlua_exec(s, a, err); api_free_string(s); api_free_array(a); diff --git a/src/nvim/main.c b/src/nvim/main.c index a575aba50a..3a1ca2988a 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -268,6 +268,10 @@ int main(int argc, char **argv) } server_init(params.listen_addr); + if (params.remote) { + handle_remote_client(¶ms, params.remote, + params.server_addr, argc, argv); + } if (GARGCOUNT > 0) { fname = get_fname(¶ms, cwd); @@ -803,6 +807,67 @@ 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) +{ + Object rvobj = OBJECT_INIT; + rvobj.data.dictionary = (Dictionary)ARRAY_DICT_INIT; + rvobj.type = kObjectTypeDictionary; + CallbackReader on_data = CALLBACK_READER_INIT; + const char *error = NULL; + uint64_t rc_id = server_addr == NULL ? 0 : channel_connect(false, + server_addr, true, on_data, 50, &error); + + Boolean should_exit = true; + Boolean tabbed; + int files; + + 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)rc_id)); + ADD(a, ARRAY_OBJ(args)); + String s = cstr_to_string("return vim._cs_remote(...)"); + Object o = executor_exec_lua_api(s, a, &err); + api_free_string(s); + api_free_array(a); + + if (o.type == kObjectTypeDictionary) { + rvobj.data.dictionary = o.data.dictionary; + } else if (!ERROR_SET(&err)) { + api_set_error(&err, kErrorTypeException, + "Function returned unexpected value"); + } + + for (size_t i = 0; i < rvobj.data.dictionary.size ; i++) { + if (strcmp(rvobj.data.dictionary.items[i].key.data, "tabbed") == 0) { + // should we check items[i].value.type here? + tabbed = rvobj.data.dictionary.items[i].value.data.boolean; + } else if (strcmp(rvobj.data.dictionary.items[i].key.data, "should_exit") == 0) { + should_exit = rvobj.data.dictionary.items[i].value.data.boolean; + } else if (strcmp(rvobj.data.dictionary.items[i].key.data, "files") == 0) { + files = (int)rvobj.data.dictionary.items[i].value.data.integer; + } + } + + if (should_exit) { + mch_exit(0); + } else { + if (tabbed) { + params->window_count = files; + 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) @@ -868,6 +933,8 @@ static void command_line_scan(mparm_T *parmp) // "--version" give version message // "--noplugin[s]" skip plugins // "--cmd " execute cmd before vimrc + // "--remote" execute commands remotey on a server + // "--server" name of vim server to send remote commands to if (STRICMP(argv[0] + argv_idx, "help") == 0) { usage(); os_exit(0); @@ -906,6 +973,11 @@ static void command_line_scan(mparm_T *parmp) argv_idx += 6; } else if (STRNICMP(argv[0] + argv_idx, "literal", 7) == 0) { // Do nothing: file args are always literal. #7679 + } else if (STRNICMP(argv[0] + argv_idx, "remote", 6) == 0) { + parmp->remote = parmp->argc - argc; + } else if (STRNICMP(argv[0] + argv_idx, "server", 6) == 0) { + want_argument = true; + argv_idx += 6; } else if (STRNICMP(argv[0] + argv_idx, "noplugin", 8) == 0) { p_lpl = false; } else if (STRNICMP(argv[0] + argv_idx, "cmd", 3) == 0) { @@ -1137,6 +1209,9 @@ static void command_line_scan(mparm_T *parmp) } else if (strequal(argv[-1], "--listen")) { // "--listen {address}" parmp->listen_addr = argv[0]; + } else if (strequal(argv[-1], "--server")) { + // "--server {address}" + parmp->server_addr = argv[0]; } // "--startuptime " already handled break; @@ -1291,6 +1366,8 @@ static void init_params(mparm_T *paramp, int argc, char **argv) paramp->use_debug_break_level = -1; paramp->window_count = -1; paramp->listen_addr = NULL; + paramp->server_addr = NULL; + paramp->remote = 0; } /// Initialize global startuptime file if "--startuptime" passed as an argument. @@ -2041,6 +2118,8 @@ static void usage(void) mch_msg(_(" --headless Don't start a user interface\n")); mch_msg(_(" --listen
Serve RPC API from this address\n")); mch_msg(_(" --noplugin Don't load plugins\n")); + mch_msg(_(" --remote[-subcommand] Execute commands remotely on a server\n")); + mch_msg(_(" --server
Specify RPC server to send commands to\n")); mch_msg(_(" --startuptime Write startup timing messages to \n")); mch_msg(_("\nSee \":help startup-options\" for all options.\n")); } diff --git a/src/nvim/main.h b/src/nvim/main.h index f73af5c288..e55bef6e33 100644 --- a/src/nvim/main.h +++ b/src/nvim/main.h @@ -39,6 +39,8 @@ typedef struct { int diff_mode; // start with 'diff' set char *listen_addr; // --listen {address} + int remote; // --remote-[subcmd] {file1} {file2} + char *server_addr; // --server {address} } mparm_T; #ifdef INCLUDE_GENERATED_DECLARATIONS From 70d2ab158320e72542dc2a845858d6f97da86e00 Mon Sep 17 00:00:00 2001 From: Charlie Groves Date: Wed, 16 Feb 2022 17:19:41 -0500 Subject: [PATCH 2/9] fix(remote): make compile again --- src/nvim/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nvim/main.c b/src/nvim/main.c index 3a1ca2988a..843ca5f855 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -836,7 +836,7 @@ static void handle_remote_client(mparm_T *params, int remote_args, ADD(a, INTEGER_OBJ((int)rc_id)); ADD(a, ARRAY_OBJ(args)); String s = cstr_to_string("return vim._cs_remote(...)"); - Object o = executor_exec_lua_api(s, a, &err); + Object o = nlua_exec(s, a, &err); api_free_string(s); api_free_array(a); @@ -859,7 +859,7 @@ static void handle_remote_client(mparm_T *params, int remote_args, } if (should_exit) { - mch_exit(0); + os_exit(0); } else { if (tabbed) { params->window_count = files; From 039e94f491d2f8576cbef1aeacd5ea1f7bc0982a Mon Sep 17 00:00:00 2001 From: Charlie Groves Date: Thu, 24 Feb 2022 10:47:41 -0500 Subject: [PATCH 3/9] test(remote): add tests for --remote This also fixes a fair number of issues found in running the tests --- runtime/lua/vim/_editor.lua | 67 ++++++------- src/nvim/api/vim.c | 2 +- src/nvim/main.c | 23 ++--- test/functional/core/remote_spec.lua | 140 +++++++++++++++++++++++++++ 4 files changed, 184 insertions(+), 48 deletions(-) create mode 100644 test/functional/core/remote_spec.lua diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 5f3d66e108..869a2706ac 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -636,17 +636,10 @@ function vim.pretty_print(...) return ... end -local function __rpcrequest(...) - return vim.api.nvim_call_function("rpcrequest", {...}) -end - function vim._cs_remote(rcid, args) - local f_silent = false local f_wait = false local f_tab = false - local should_exit = true - local command = 'edit ' local subcmd = string.sub(args[1],10) @@ -672,40 +665,42 @@ function vim._cs_remote(rcid, args) f_wait = true f_silent = true elseif subcmd == 'send' then - __rpcrequest(rcid, 'nvim_input', args[2]) - return { should_exit = should_exit, tabbed = f_tab, files = 0 } - -- should we show warning if --server doesn't exist in --send and --expr? - elseif subcmd == 'expr' then - local res = __rpcrequest(rcid, 'vim_eval', args[2]) - print(res) - return { should_exit = should_exit, tabbed = f_tab, files = 0 } - else - print('--remote subcommand not found') - end - - table.remove(args,1) - - if not f_silent and rcid == 0 then - print('Remote server does not exist.') - end - - if f_silent and rcid == 0 then - print('Remote server does not exist. starting new server') - should_exit = false - end - - if f_tab then command = 'tabedit ' end - - if rcid ~= 0 then - for _, key in ipairs(args) do - __rpcrequest(rcid, 'nvim_command', command .. key) + if rcid == 0 then + vim.cmd('echoerr "E247: Remote server does not exist. Send failed."') + return end + vim.fn.rpcrequest(rcid, 'nvim_input', args[2]) + return { should_exit = true, tabbed = false } + elseif subcmd == 'expr' then + if rcid == 0 then + vim.cmd('echoerr "E247: Remote server does not exist. Send expression failed."') + return + end + vim.fn.rpcrequest(rcid, 'nvim_eval', args[2]) + return { should_exit = true, tabbed = false } + else + vim.cmd('echoerr "Unknown option argument: ' .. args[1] .. '"') + return + end + + if rcid == 0 then + if not f_silent then + vim.cmd('echohl WarningMsg | echomsg "E247: Remote server does not exist. Editing locally" | echohl None') + end + should_exit = false + else + local command = {} + if f_tab then table.insert(command, 'tab') end + table.insert(command, 'drop') + for i = 2, #args do + table.insert(command, vim.fn.fnameescape(args[i])) + end + vim.fn.rpcrequest(rcid, 'nvim_command', table.concat(command, ' ')) end return { - should_exit = should_exit, + should_exit = rcid ~= 0, tabbed = f_tab, - files = table.getn(args) } end diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index b82a7553cb..b691dee2ef 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1997,7 +1997,7 @@ 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(select(...))"); + String s = cstr_to_string("return vim._os_proc_children(...)"); Object o = nlua_exec(s, a, err); api_free_string(s); api_free_array(a); diff --git a/src/nvim/main.c b/src/nvim/main.c index 843ca5f855..a3588ac5df 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -821,7 +821,6 @@ static void handle_remote_client(mparm_T *params, int remote_args, Boolean should_exit = true; Boolean tabbed; - int files; int t_argc = remote_args; Array args = ARRAY_DICT_INIT; @@ -839,12 +838,17 @@ static void handle_remote_client(mparm_T *params, int remote_args, Object o = nlua_exec(s, a, &err); api_free_string(s); 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 if (!ERROR_SET(&err)) { - api_set_error(&err, kErrorTypeException, - "Function returned unexpected value"); + } else { + mch_errmsg("vim._cs_remote returned unexpected value\n"); + os_exit(3); } for (size_t i = 0; i < rvobj.data.dictionary.size ; i++) { @@ -853,18 +857,15 @@ static void handle_remote_client(mparm_T *params, int remote_args, tabbed = rvobj.data.dictionary.items[i].value.data.boolean; } else if (strcmp(rvobj.data.dictionary.items[i].key.data, "should_exit") == 0) { should_exit = rvobj.data.dictionary.items[i].value.data.boolean; - } else if (strcmp(rvobj.data.dictionary.items[i].key.data, "files") == 0) { - files = (int)rvobj.data.dictionary.items[i].value.data.integer; } } if (should_exit) { os_exit(0); - } else { - if (tabbed) { - params->window_count = files; - params->window_layout = WIN_TABS; - } + } + if (tabbed) { + params->window_count = argc - remote_args - 1; + params->window_layout = WIN_TABS; } } diff --git a/test/functional/core/remote_spec.lua b/test/functional/core/remote_spec.lua new file mode 100644 index 0000000000..e86d15ec69 --- /dev/null +++ b/test/functional/core/remote_spec.lua @@ -0,0 +1,140 @@ +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local command = helpers.command +local eq = helpers.eq +local expect = helpers.expect +local funcs = helpers.funcs +local insert = helpers.insert +local meths = helpers.meths +local new_argv = helpers.new_argv +local neq = helpers.neq +local run = helpers.run +local set_session = helpers.set_session +local spawn = helpers.spawn +local tmpname = helpers.tmpname +local write_file = helpers.write_file + +describe('Remote', function() + local fname, other_fname + local contents = 'The call is coming from outside the process' + local other_contents = "A second file's contents" + + before_each(function() + fname = tmpname() .. ' with spaces in the filename' + other_fname = tmpname() + write_file(fname, contents) + write_file(other_fname, other_contents) + end) + + describe('connect to server and', function() + local server + before_each(function() + server = spawn(new_argv(), true) + set_session(server) + end) + + after_each(function() + server:close() + end) + + function run_remote(...) + set_session(server) + local addr = funcs.serverlist()[1] + local client_argv = new_argv({args={'--server', addr, ...}}) + + -- Create an nvim instance just to run the remote-invoking nvim. We want + -- to wait for the remote instance to exit and calling jobwait blocks + -- the event loop. If the server event loop is blocked, it can't process + -- our incoming --remote calls. + local client_starter = spawn(new_argv(), false, nil, true) + set_session(client_starter) + local client_job_id = funcs.jobstart(client_argv) + eq({ 0 }, funcs.jobwait({client_job_id})) + client_starter:close() + set_session(server) + end + + it('edit a single file', function() + run_remote('--remote', fname) + expect(contents) + eq(2, #funcs.getbufinfo()) + end) + + it('tab edit a single file with a non-changed buffer', function() + run_remote('--remote-tab', fname) + expect(contents) + eq(1, #funcs.gettabinfo()) + end) + + it('tab edit a single file with a changed buffer', function() + insert('hello') + run_remote('--remote-tab', fname) + expect(contents) + eq(2, #funcs.gettabinfo()) + end) + + it('edit multiple files', function() + run_remote('--remote', fname, other_fname) + expect(contents) + command('next') + expect(other_contents) + eq(3, #funcs.getbufinfo()) + end) + + it('send keys', function() + run_remote('--remote-send', ':edit '..fname..'v') + expect(contents) + eq(2, #funcs.getwininfo()) + -- Only a single buffer as we're using edit and not drop like --remote does + eq(1, #funcs.getbufinfo()) + end) + + it('evaluate expressions', function() + run_remote('--remote-expr', 'setline(1, "Yo")') + expect('Yo') + end) + end) + + it('creates server if not found', function() + clear('--remote', fname) + expect(contents) + eq(1, #funcs.getbufinfo()) + -- Since we didn't pass silent, we should get a complaint + neq(nil, string.find(meths.exec('messages', true), 'E247')) + end) + + it('creates server if not found with tabs', function() + clear('--remote-tab-silent', fname, other_fname) + expect(contents) + eq(2, #funcs.gettabinfo()) + eq(2, #funcs.getbufinfo()) + -- We passed silent, so no message should be issued about the server not being found + eq(nil, string.find(meths.exec('messages', true), 'E247')) + end) + + describe('exits with error on', function() + local function run_and_check_exit_code(...) + local bogus_argv = new_argv(...) + + -- Create an nvim instance just to run the remote-invoking nvim. We want + -- to wait for the remote instance to exit and calling jobwait blocks + -- the event loop. If the server event loop is blocked, it can't process + -- our incoming --remote calls. + local client_starter = clear() + local bogus_job_id = funcs.jobstart(bogus_argv) + eq({2}, funcs.jobwait({bogus_job_id})) + end + it('bogus subcommand', function() + run_and_check_exit_code('--remote-bogus') + end) + + it('send without server', function() + run_and_check_exit_code('--remote-send', 'i') + end) + + it('expr without server', function() + run_and_check_exit_code('--remote-expr', 'setline(1, "Yo")') + end) + end) +end) From fcdb1f372b4da5c33091ae2e69960b6168454695 Mon Sep 17 00:00:00 2001 From: Charlie Groves Date: Tue, 1 Mar 2022 16:23:57 -0500 Subject: [PATCH 4/9] docs(remote): restore remote.txt from removal in f2205b83c553367a76b6cad --- runtime/doc/remote.txt | 189 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 runtime/doc/remote.txt diff --git a/runtime/doc/remote.txt b/runtime/doc/remote.txt new file mode 100644 index 0000000000..6c2ceb45be --- /dev/null +++ b/runtime/doc/remote.txt @@ -0,0 +1,189 @@ +*remote.txt* Nvim + + + VIM REFERENCE MANUAL by Bram Moolenaar + + +Vim client-server communication *client-server* + + Type |gO| to see the table of contents. + +============================================================================== +1. Common functionality *clientserver* + +When compiled with the |+clientserver| option, Vim can act as a command +server. It accepts messages from a client and executes them. At the same +time, Vim can function as a client and send commands to a Vim server. + +The following command line arguments are available: + + argument meaning ~ + + --remote [+{cmd}] {file} ... *--remote* + Open the file list in a remote Vim. When + there is no Vim server, execute locally. + There is one optional init command: +{cmd}. + This must be an Ex command that can be + followed by "|". + The rest of the command line is taken as the + file list. Thus any non-file arguments must + come before this. + You cannot edit stdin this way |--|. + The remote Vim is raised. If you don't want + this use > + vim --remote-send ":n filename" +< + --remote-silent [+{cmd}] {file} ... *--remote-silent* + As above, but don't complain if there is no + server and the file is edited locally. + --remote-wait [+{cmd}] {file} ... *--remote-wait* + As --remote, but wait for files to complete + (unload) in remote Vim. + --remote-wait-silent [+{cmd}] {file} ... *--remote-wait-silent* + As --remote-wait, but don't complain if there + is no server. + *--remote-tab* + --remote-tab Like --remote but open each file in a new + tabpage. + *--remote-tab-silent* + --remote-tab-silent Like --remote-silent but open each file in a + new tabpage. + *--remote-tab-wait* + --remote-tab-wait Like --remote-wait but open each file in a new + tabpage. + + *--remote-tab-wait-silent* + --remote-tab-wait-silent Like --remote-wait-silent but open each file + in a new tabpage. + *--remote-send* + --remote-send {keys} Send {keys} to server and exit. The {keys} + are not mapped. Special key names are + recognized, e.g., "" results in a CR + character. + *--remote-expr* + --remote-expr {expr} Evaluate {expr} in server and print the result + on stdout. + +Examples ~ + +Edit "file.txt" in an already running GVIM server: > + gvim --remote file.txt + +Edit "file.txt" in an already running server called FOOBAR: > + gvim --servername FOOBAR --remote file.txt + +Edit "file.txt" in server "FILES" if it exists, become server "FILES" +otherwise: > + gvim --servername FILES --remote-silent file.txt + +This doesn't work, all arguments after --remote will be used as file names: > + gvim --remote --servername FOOBAR file.txt + +Edit file "+foo" in a remote server (note the use of "./" to avoid the special +meaning of the leading plus): > + vim --remote ./+foo + +Tell the remote server "BLA" to write all files and exit: > + vim --servername BLA --remote-send ':wqa' + + +SERVER NAME *client-server-name* + +By default Vim will try to register the name under which it was invoked (gvim, +egvim ...). This can be overridden with the --servername argument. If the +specified name is not available, a postfix is applied until a free name is +encountered, i.e. "gvim1" for the second invocation of gvim on a particular +X-server. The resulting name is available in the servername builtin variable +|v:servername|. The case of the server name is ignored, thus "gvim" and +"GVIM" are considered equal. + +When Vim is invoked with --remote, --remote-wait or --remote-send it will try +to locate the server name determined by the invocation name and --servername +argument as described above. If an exact match is not available, the first +server with the number postfix will be used. If a name with the number +postfix is specified with the --servername argument, it must match exactly. + +If no server can be located and --remote or --remote-wait was used, Vim will +start up according to the rest of the command line and do the editing by +itself. This way it is not necessary to know whether gvim is already started +when sending command to it. + +The --serverlist argument will cause Vim to print a list of registered command +servers on the standard output (stdout) and exit. + +Win32 Note: Making the Vim server go to the foreground doesn't always work, +because MS-Windows doesn't allow it. The client will move the server to the +foreground when using the --remote or --remote-wait argument and the server +name starts with "g". + + +REMOTE EDITING + +The --remote argument will cause a |:drop| command to be constructed from the +rest of the command line and sent as described above. +The --remote-wait argument does the same thing and additionally sets up to +wait for each of the files to have been edited. This uses the BufUnload +event, thus as soon as a file has been unloaded, Vim assumes you are done +editing it. +Note that the --remote and --remote-wait arguments will consume the rest of +the command line. I.e. all remaining arguments will be regarded as filenames. +You can not put options there! + + +FUNCTIONS + *E240* *E573* +There are a number of Vim functions for scripting the command server. See +the description in |eval.txt| or use CTRL-] on the function name to jump to +the full explanation. + + synopsis explanation ~ + remote_startserver( name) run a server + remote_expr( server, string, idvar) send expression + remote_send( server, string, idvar) send key sequence + serverlist() get a list of available servers + remote_peek( serverid, retvar) check for reply string + remote_read( serverid) read reply string + server2client( serverid, string) send reply string + remote_foreground( server) bring server to the front + +See also the explanation of |CTRL-\_CTRL-N|. Very useful as a leading key +sequence. +The {serverid} for server2client() can be obtained with expand("") + +============================================================================== +2. X11 specific items *x11-clientserver* + *E247* *E248* *E251* *E258* *E277* + +The communication between client and server goes through the X server. The +display of the Vim server must be specified. The usual protection of the X +server is used, you must be able to open a window on the X server for the +communication to work. It is possible to communicate between different +systems. + +By default, a GUI Vim will register a name on the X-server by which it can be +addressed for subsequent execution of injected strings. Vim can also act as +a client and send strings to other instances of Vim on the same X11 display. + +When an X11 GUI Vim (gvim) is started, it will try to register a send-server +name on the 'VimRegistry' property on the root window. + +An empty --servername argument will cause the command server to be disabled. + +To send commands to a Vim server from another application, read the source +file src/if_xcmdsrv.c, it contains some hints about the protocol used. + +============================================================================== +3. Win32 specific items *w32-clientserver* + +Every Win32 Vim can work as a server, also in the console. You do not need a +version compiled with OLE. Windows messages are used, this works on any +version of MS-Windows. But only communication within one system is possible. + +Since MS-Windows messages are used, any other application should be able to +communicate with a Vim server. + +When using gvim, the --remote-wait only works properly this way: > + + start /w gvim --remote-wait file.txt +< + vim:tw=78:sw=4:ts=8:noet:ft=help:norl: From e095a868cbc9ff504087b3df89e4d92d0421ab38 Mon Sep 17 00:00:00 2001 From: Charlie Groves Date: Wed, 2 Mar 2022 10:16:29 -0500 Subject: [PATCH 5/9] docs(remote): update remote.txt for current nvim implementation --- runtime/doc/remote.txt | 168 ++++++++++++++--------------------------- 1 file changed, 55 insertions(+), 113 deletions(-) diff --git a/runtime/doc/remote.txt b/runtime/doc/remote.txt index 6c2ceb45be..dd5d44387d 100644 --- a/runtime/doc/remote.txt +++ b/runtime/doc/remote.txt @@ -11,9 +11,10 @@ Vim client-server communication *client-server* ============================================================================== 1. Common functionality *clientserver* -When compiled with the |+clientserver| option, Vim can act as a command -server. It accepts messages from a client and executes them. At the same -time, Vim can function as a client and send commands to a Vim server. +Nvim's |RPC| functionality allows clients to programmatically control Nvim. Nvim +itself takes command-line arguments that cause it to become a client to another +Nvim running as a server. These arguments match those provided by Vim's +clientserver option. The following command line arguments are available: @@ -22,39 +23,27 @@ The following command line arguments are available: --remote [+{cmd}] {file} ... *--remote* Open the file list in a remote Vim. When there is no Vim server, execute locally. - There is one optional init command: +{cmd}. + Vim allows one init command: +{cmd}. This must be an Ex command that can be - followed by "|". + followed by "|". It's not yet supported by + Nvim. The rest of the command line is taken as the file list. Thus any non-file arguments must come before this. You cannot edit stdin this way |--|. The remote Vim is raised. If you don't want this use > - vim --remote-send ":n filename" + nvim --remote-send ":n filename" < --remote-silent [+{cmd}] {file} ... *--remote-silent* As above, but don't complain if there is no server and the file is edited locally. - --remote-wait [+{cmd}] {file} ... *--remote-wait* - As --remote, but wait for files to complete - (unload) in remote Vim. - --remote-wait-silent [+{cmd}] {file} ... *--remote-wait-silent* - As --remote-wait, but don't complain if there - is no server. *--remote-tab* --remote-tab Like --remote but open each file in a new tabpage. *--remote-tab-silent* --remote-tab-silent Like --remote-silent but open each file in a new tabpage. - *--remote-tab-wait* - --remote-tab-wait Like --remote-wait but open each file in a new - tabpage. - - *--remote-tab-wait-silent* - --remote-tab-wait-silent Like --remote-wait-silent but open each file - in a new tabpage. *--remote-send* --remote-send {keys} Send {keys} to server and exit. The {keys} are not mapped. Special key names are @@ -63,127 +52,80 @@ The following command line arguments are available: *--remote-expr* --remote-expr {expr} Evaluate {expr} in server and print the result on stdout. + *--server* + --server {addr} Connect to the named pipe or socket at the + given address for executing remote commands. + See |--listen| for specifying an address when + starting a server. Examples ~ -Edit "file.txt" in an already running GVIM server: > - gvim --remote file.txt +Start an Nvim server listening on a named pipe at '~/.cache/nvim/server.pipe': > + nvim --listen ~/.cache/nvim/server.pipe -Edit "file.txt" in an already running server called FOOBAR: > - gvim --servername FOOBAR --remote file.txt - -Edit "file.txt" in server "FILES" if it exists, become server "FILES" -otherwise: > - gvim --servername FILES --remote-silent file.txt +Edit "file.txt" in an Nvim server listening at '~/.cache/nvim/server.pipe': > + nvim --server ~/.cache/nvim/server.pipe --remote file.txt This doesn't work, all arguments after --remote will be used as file names: > - gvim --remote --servername FOOBAR file.txt + nvim --remote --server ~/.cache/nvim/server.pipe file.txt -Edit file "+foo" in a remote server (note the use of "./" to avoid the special -meaning of the leading plus): > - vim --remote ./+foo - -Tell the remote server "BLA" to write all files and exit: > - vim --servername BLA --remote-send ':wqa' - - -SERVER NAME *client-server-name* - -By default Vim will try to register the name under which it was invoked (gvim, -egvim ...). This can be overridden with the --servername argument. If the -specified name is not available, a postfix is applied until a free name is -encountered, i.e. "gvim1" for the second invocation of gvim on a particular -X-server. The resulting name is available in the servername builtin variable -|v:servername|. The case of the server name is ignored, thus "gvim" and -"GVIM" are considered equal. - -When Vim is invoked with --remote, --remote-wait or --remote-send it will try -to locate the server name determined by the invocation name and --servername -argument as described above. If an exact match is not available, the first -server with the number postfix will be used. If a name with the number -postfix is specified with the --servername argument, it must match exactly. - -If no server can be located and --remote or --remote-wait was used, Vim will -start up according to the rest of the command line and do the editing by -itself. This way it is not necessary to know whether gvim is already started -when sending command to it. - -The --serverlist argument will cause Vim to print a list of registered command -servers on the standard output (stdout) and exit. - -Win32 Note: Making the Vim server go to the foreground doesn't always work, -because MS-Windows doesn't allow it. The client will move the server to the -foreground when using the --remote or --remote-wait argument and the server -name starts with "g". +Tell the remote server to write all files and exit: > + nvim --server ~/.cache/nvim/server.pipe --remote-send ':wqa' REMOTE EDITING The --remote argument will cause a |:drop| command to be constructed from the rest of the command line and sent as described above. -The --remote-wait argument does the same thing and additionally sets up to -wait for each of the files to have been edited. This uses the BufUnload -event, thus as soon as a file has been unloaded, Vim assumes you are done -editing it. Note that the --remote and --remote-wait arguments will consume the rest of the command line. I.e. all remaining arguments will be regarded as filenames. You can not put options there! -FUNCTIONS - *E240* *E573* -There are a number of Vim functions for scripting the command server. See -the description in |eval.txt| or use CTRL-] on the function name to jump to -the full explanation. - - synopsis explanation ~ - remote_startserver( name) run a server - remote_expr( server, string, idvar) send expression - remote_send( server, string, idvar) send key sequence - serverlist() get a list of available servers - remote_peek( serverid, retvar) check for reply string - remote_read( serverid) read reply string - server2client( serverid, string) send reply string - remote_foreground( server) bring server to the front - -See also the explanation of |CTRL-\_CTRL-N|. Very useful as a leading key -sequence. -The {serverid} for server2client() can be obtained with expand("") - ============================================================================== -2. X11 specific items *x11-clientserver* - *E247* *E248* *E251* *E258* *E277* +2. Missing functionality *clientserver-missing* -The communication between client and server goes through the X server. The -display of the Vim server must be specified. The usual protection of the X -server is used, you must be able to open a window on the X server for the -communication to work. It is possible to communicate between different -systems. +Vim supports additional functionality in clientserver that's not yet +implemented in Nvim. In particular, none of the 'wait' variants are supported +yet. The following command line arguments are not yet available: -By default, a GUI Vim will register a name on the X-server by which it can be -addressed for subsequent execution of injected strings. Vim can also act as -a client and send strings to other instances of Vim on the same X11 display. + argument meaning ~ -When an X11 GUI Vim (gvim) is started, it will try to register a send-server -name on the 'VimRegistry' property on the root window. + --remote-wait [+{cmd}] {file} ... *--remote-wait* + Not yet supported by Nvim. + As --remote, but wait for files to complete + (unload) in remote Vim. + --remote-wait-silent [+{cmd}] {file} ... *--remote-wait-silent* + Not yet supported by Nvim. + As --remote-wait, but don't complain if there + is no server. + *--remote-tab-wait* + --remote-tab-wait Not yet supported by Nvim. + Like --remote-wait but open each file in a new + tabpage. + *--remote-tab-wait-silent* + --remote-tab-wait-silent Not yet supported by Nvim. + Like --remote-wait-silent but open each file + in a new tabpage. + *--servername* + --servername {name} Not yet supported by Nvim. + Become the server {name}. When used together + with one of the --remote commands: connect to + server {name} instead of the default (see + below). The name used will be uppercase. -An empty --servername argument will cause the command server to be disabled. + *--serverlist* + --serverlist Not yet supported by Nvim. + Output a list of server names. -To send commands to a Vim server from another application, read the source -file src/if_xcmdsrv.c, it contains some hints about the protocol used. -============================================================================== -3. Win32 specific items *w32-clientserver* -Every Win32 Vim can work as a server, also in the console. You do not need a -version compiled with OLE. Windows messages are used, this works on any -version of MS-Windows. But only communication within one system is possible. -Since MS-Windows messages are used, any other application should be able to -communicate with a Vim server. +SERVER NAME *client-server-name* -When using gvim, the --remote-wait only works properly this way: > +By default Vim will try to register the name under which it was invoked (gvim, +egvim ...). This can be overridden with the --servername argument. Nvim +either listens on a named pipe or a socket and does not yet support this +--servername functionality. - start /w gvim --remote-wait file.txt -< vim:tw=78:sw=4:ts=8:noet:ft=help:norl: From 29c36322857b37263b07eb1301d71ccd8a2ae044 Mon Sep 17 00:00:00 2001 From: Charlie Groves Date: Thu, 3 Mar 2022 16:33:27 -0500 Subject: [PATCH 6/9] fix(remote): report on missing wait commands, typecheck lua results Clean up lint errors, too --- runtime/doc/remote.txt | 2 +- runtime/lua/vim/_editor.lua | 33 +++++++--------------------- src/nvim/main.c | 28 ++++++++++++++++++----- test/functional/core/remote_spec.lua | 8 ++++--- 4 files changed, 37 insertions(+), 34 deletions(-) diff --git a/runtime/doc/remote.txt b/runtime/doc/remote.txt index dd5d44387d..b8991be738 100644 --- a/runtime/doc/remote.txt +++ b/runtime/doc/remote.txt @@ -83,7 +83,7 @@ You can not put options there! ============================================================================== -2. Missing functionality *clientserver-missing* +2. Missing functionality *E5600* *clientserver-missing* Vim supports additional functionality in clientserver that's not yet implemented in Nvim. In particular, none of the 'wait' variants are supported diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 869a2706ac..030c3b40e8 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -638,56 +638,39 @@ end function vim._cs_remote(rcid, args) local f_silent = false - local f_wait = false local f_tab = false local subcmd = string.sub(args[1],10) - if subcmd == '' then - -- no flags to set - elseif subcmd == 'tab' then + if subcmd == 'tab' then f_tab = true elseif subcmd == 'silent' then f_silent = true - elseif subcmd == 'wait' then - f_wait = true - elseif subcmd == 'wait-silent' then - f_wait = true - f_silent = true - elseif subcmd == 'tab-wait' then - f_tab = true - f_wait = true + elseif subcmd == 'wait' or subcmd == 'wait-silent' or subcmd == 'tab-wait' or subcmd == 'tab-wait-silent' then + return { errmsg = 'E5600: Wait commands not yet implemented in nvim' } elseif subcmd == 'tab-silent' then f_tab = true f_silent = true - elseif subcmd == 'tab-wait-silent' then - f_tab = true - f_wait = true - f_silent = true elseif subcmd == 'send' then if rcid == 0 then - vim.cmd('echoerr "E247: Remote server does not exist. Send failed."') - return + return { errmsg = 'E247: Remote server does not exist. Send failed.' } end vim.fn.rpcrequest(rcid, 'nvim_input', args[2]) return { should_exit = true, tabbed = false } elseif subcmd == 'expr' then if rcid == 0 then - vim.cmd('echoerr "E247: Remote server does not exist. Send expression failed."') - return + return { errmsg = 'E247: Remote server does not exist. Send expression failed.' } end - vim.fn.rpcrequest(rcid, 'nvim_eval', args[2]) + print(vim.fn.rpcrequest(rcid, 'nvim_eval', args[2])) return { should_exit = true, tabbed = false } - else - vim.cmd('echoerr "Unknown option argument: ' .. args[1] .. '"') - return + elseif subcmd ~= '' then + return { errmsg='Unknown option argument: ' .. args[1] } end if rcid == 0 then if not f_silent then vim.cmd('echohl WarningMsg | echomsg "E247: Remote server does not exist. Editing locally" | echohl None') end - should_exit = false else local command = {} if f_tab then table.insert(command, 'tab') end diff --git a/src/nvim/main.c b/src/nvim/main.c index a3588ac5df..eb60d51b9b 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -816,8 +816,10 @@ static void handle_remote_client(mparm_T *params, int remote_args, rvobj.type = kObjectTypeDictionary; CallbackReader on_data = CALLBACK_READER_INIT; const char *error = NULL; - uint64_t rc_id = server_addr == NULL ? 0 : channel_connect(false, - server_addr, true, on_data, 50, &error); + uint64_t rc_id = 0; + if (server_addr != NULL) { + rc_id = channel_connect(false, server_addr, true, on_data, 50, &error); + } Boolean should_exit = true; Boolean tabbed; @@ -848,17 +850,33 @@ static void handle_remote_client(mparm_T *params, int remote_args, rvobj.data.dictionary = o.data.dictionary; } else { mch_errmsg("vim._cs_remote returned unexpected value\n"); - os_exit(3); + os_exit(2); } for (size_t i = 0; i < rvobj.data.dictionary.size ; i++) { - if (strcmp(rvobj.data.dictionary.items[i].key.data, "tabbed") == 0) { - // should we check items[i].value.type here? + 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); + } 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; } 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; } } + api_free_object(o); if (should_exit) { os_exit(0); diff --git a/test/functional/core/remote_spec.lua b/test/functional/core/remote_spec.lua index e86d15ec69..602a5a71eb 100644 --- a/test/functional/core/remote_spec.lua +++ b/test/functional/core/remote_spec.lua @@ -9,7 +9,6 @@ local insert = helpers.insert local meths = helpers.meths local new_argv = helpers.new_argv local neq = helpers.neq -local run = helpers.run local set_session = helpers.set_session local spawn = helpers.spawn local tmpname = helpers.tmpname @@ -38,7 +37,7 @@ describe('Remote', function() server:close() end) - function run_remote(...) + local function run_remote(...) set_session(server) local addr = funcs.serverlist()[1] local client_argv = new_argv({args={'--server', addr, ...}}) @@ -121,7 +120,7 @@ describe('Remote', function() -- to wait for the remote instance to exit and calling jobwait blocks -- the event loop. If the server event loop is blocked, it can't process -- our incoming --remote calls. - local client_starter = clear() + clear() local bogus_job_id = funcs.jobstart(bogus_argv) eq({2}, funcs.jobwait({bogus_job_id})) end @@ -136,5 +135,8 @@ describe('Remote', function() it('expr without server', function() run_and_check_exit_code('--remote-expr', 'setline(1, "Yo")') end) + it('wait subcommand', function() + run_and_check_exit_code('--remote-wait', fname) + end) end) end) From 2be938a2513b1dd604ce0069667b89a64441cb2a Mon Sep 17 00:00:00 2001 From: Charlie Groves Date: Thu, 10 Mar 2022 17:26:20 -0500 Subject: [PATCH 7/9] fix(remote): report connection error, missing return values --- src/nvim/main.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/nvim/main.c b/src/nvim/main.c index eb60d51b9b..f08ede7197 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -820,9 +820,13 @@ static void handle_remote_client(mparm_T *params, int remote_args, if (server_addr != NULL) { rc_id = channel_connect(false, server_addr, true, on_data, 50, &error); } - - Boolean should_exit = true; - Boolean tabbed; + if (error) { + mch_msg("Failed to connect to server "); + mch_msg(server_addr); + mch_msg("\nReason: "); + mch_msg(error); + mch_msg("Continuing with remote command in case we can execute locally\n"); + } int t_argc = remote_args; Array args = ARRAY_DICT_INIT; @@ -853,6 +857,9 @@ static void handle_remote_client(mparm_T *params, int remote_args, 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) { @@ -867,21 +874,25 @@ static void handle_remote_client(mparm_T *params, int remote_args, mch_errmsg("vim._cs_remote returned an unexpected type for 'tabbed'\n"); os_exit(2); } - tabbed = rvobj.data.dictionary.items[i].value.data.boolean; + 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; + 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) { + if (should_exit == kTrue) { os_exit(0); } - if (tabbed) { + if (tabbed == kTrue) { params->window_count = argc - remote_args - 1; params->window_layout = WIN_TABS; } From 1dbf8675c71dc500ae7502085161cd56e311ccf6 Mon Sep 17 00:00:00 2001 From: Charlie Groves Date: Fri, 11 Mar 2022 11:16:34 -0500 Subject: [PATCH 8/9] fix(remote): respect silent in error reporting --- runtime/lua/vim/_editor.lua | 22 +++++++++++++++++----- src/nvim/main.c | 13 ++++--------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 030c3b40e8..a0c60a7dcf 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -636,12 +636,24 @@ function vim.pretty_print(...) return ... end -function vim._cs_remote(rcid, args) +function vim._cs_remote(rcid, server_addr, connect_error, args) + local function connection_failure_errmsg(consequence) + local explanation + if server_addr == '' then + explanation = "No server specified with --server" + else + explanation = "Failed to connect to '" .. server_addr .. "'" + if connect_error ~= "" then + explanation = explanation .. ": " .. connect_error + end + end + return "E247: " .. explanation .. ". " .. consequence + end + local f_silent = false local f_tab = false local subcmd = string.sub(args[1],10) - if subcmd == 'tab' then f_tab = true elseif subcmd == 'silent' then @@ -653,13 +665,13 @@ function vim._cs_remote(rcid, args) f_silent = true elseif subcmd == 'send' then if rcid == 0 then - return { errmsg = 'E247: Remote server does not exist. Send failed.' } + return { errmsg = connection_failure_errmsg('Send failed.') } end vim.fn.rpcrequest(rcid, 'nvim_input', args[2]) return { should_exit = true, tabbed = false } elseif subcmd == 'expr' then if rcid == 0 then - return { errmsg = 'E247: Remote server does not exist. Send expression failed.' } + return { errmsg = connection_failure_errmsg('Send expression failed.') } end print(vim.fn.rpcrequest(rcid, 'nvim_eval', args[2])) return { should_exit = true, tabbed = false } @@ -669,7 +681,7 @@ function vim._cs_remote(rcid, args) if rcid == 0 then if not f_silent then - vim.cmd('echohl WarningMsg | echomsg "E247: Remote server does not exist. Editing locally" | echohl None') + vim.notify(connection_failure_errmsg("Editing locally"), vim.log.levels.WARN) end else local command = {} diff --git a/src/nvim/main.c b/src/nvim/main.c index f08ede7197..33b13f4dc6 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -815,17 +815,10 @@ static void handle_remote_client(mparm_T *params, int remote_args, rvobj.data.dictionary = (Dictionary)ARRAY_DICT_INIT; rvobj.type = kObjectTypeDictionary; CallbackReader on_data = CALLBACK_READER_INIT; - const char *error = NULL; + 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, &error); - } - if (error) { - mch_msg("Failed to connect to server "); - mch_msg(server_addr); - mch_msg("\nReason: "); - mch_msg(error); - mch_msg("Continuing with remote command in case we can execute locally\n"); + rc_id = channel_connect(false, server_addr, true, on_data, 50, &connect_error); } int t_argc = remote_args; @@ -839,6 +832,8 @@ static void handle_remote_client(mparm_T *params, int remote_args, 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 = cstr_to_string("return vim._cs_remote(...)"); Object o = nlua_exec(s, a, &err); From 1201145b6893703fb351f51d9f2c742bd6946403 Mon Sep 17 00:00:00 2001 From: Charlie Groves Date: Fri, 11 Mar 2022 13:05:40 -0500 Subject: [PATCH 9/9] fix(remote): use STATIC_CSTR_AS_STRING --- src/nvim/main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/nvim/main.c b/src/nvim/main.c index 33b13f4dc6..230be9d9b9 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -835,9 +835,8 @@ static void handle_remote_client(mparm_T *params, int remote_args, ADD(a, CSTR_TO_OBJ(server_addr)); ADD(a, CSTR_TO_OBJ(connect_error)); ADD(a, ARRAY_OBJ(args)); - String s = cstr_to_string("return vim._cs_remote(...)"); + String s = STATIC_CSTR_AS_STRING("return vim._cs_remote(...)"); Object o = nlua_exec(s, a, &err); - api_free_string(s); api_free_array(a); if (ERROR_SET(&err)) { mch_errmsg(err.msg);