aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/builtin.txt4
-rw-r--r--runtime/doc/change.txt10
-rw-r--r--runtime/doc/cmdline.txt2
-rw-r--r--runtime/doc/editing.txt6
-rw-r--r--runtime/doc/filetype.txt3
-rw-r--r--runtime/doc/quickfix.txt4
-rw-r--r--runtime/doc/remote.txt131
-rw-r--r--runtime/doc/syntax.txt11
-rw-r--r--runtime/doc/tabpage.txt10
-rw-r--r--runtime/doc/various.txt2
-rw-r--r--runtime/lua/vim/_editor.lua62
-rw-r--r--src/nvim/api/vim.c2
-rw-r--r--src/nvim/main.c103
-rw-r--r--src/nvim/main.h2
-rw-r--r--test/functional/core/remote_spec.lua142
15 files changed, 474 insertions, 20 deletions
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index 5b0c7918e0..ea7a53fa3b 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -6880,12 +6880,14 @@ setqflist({list} [, {action} [, {what}]]) *setqflist()*
filename name of a file; only used when "bufnr" is not
present or it is invalid.
module name of a module; if given it will be used in
- quickfix error window instead of the filename
+ quickfix error window instead of the filename.
lnum line number in the file
+ end_lnum end of lines, if the item spans multiple lines
pattern search pattern used to locate the error
col column number
vcol when non-zero: "col" is visual column
when zero: "col" is byte index
+ end_col end column, if the item spans multiple columns
nr error number
text description of the error
type single-character error type, 'E', 'W', etc.
diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt
index b170f7cf65..86e19c5ec5 100644
--- a/runtime/doc/change.txt
+++ b/runtime/doc/change.txt
@@ -355,14 +355,14 @@ CTRL-A Add [count] to the number or alphabetic character at
*v_CTRL-A*
{Visual}CTRL-A Add [count] to the number or alphabetic character in
- the highlighted text. {not in Vi}
+ the highlighted text.
*v_g_CTRL-A*
{Visual}g CTRL-A Add [count] to the number or alphabetic character in
the highlighted text. If several lines are
highlighted, each one will be incremented by an
additional [count] (so effectively creating a
- [count] incrementing sequence). {not in Vi}
+ [count] incrementing sequence).
For Example, if you have this list of numbers:
1. ~
1. ~
@@ -381,14 +381,14 @@ CTRL-X Subtract [count] from the number or alphabetic
*v_CTRL-X*
{Visual}CTRL-X Subtract [count] from the number or alphabetic
- character in the highlighted text. {not in Vi}
+ character in the highlighted text.
*v_g_CTRL-X*
{Visual}g CTRL-X Subtract [count] from the number or alphabetic
character in the highlighted text. If several lines
are highlighted, each value will be decremented by an
additional [count] (so effectively creating a [count]
- decrementing sequence). {not in Vi}
+ decrementing sequence).
The CTRL-A and CTRL-X commands work for (signed) decimal numbers, unsigned
binary/octal/hexadecimal numbers and alphabetic characters.
@@ -1018,7 +1018,7 @@ inside of strings can change! Also see 'softtabstop' option. >
in [range] (default: current line |cmdline-ranges|),
[into register x].
- *p* *put* *E353*
+ *p* *put* *E353* *E1240*
["x]p Put the text [from register x] after the cursor
[count] times.
diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt
index c6e4bf003f..9fa2034718 100644
--- a/runtime/doc/cmdline.txt
+++ b/runtime/doc/cmdline.txt
@@ -679,7 +679,7 @@ If more line specifiers are given than required for the command, the first
one(s) will be ignored.
Line numbers may be specified with: *:range* *{address}*
- {number} an absolute line number
+ {number} an absolute line number *E1247*
. the current line *:.*
$ the last line in the file *:$*
% equal to 1,$ (the entire file) *:%*
diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt
index bfa01f45a7..1cc8c21462 100644
--- a/runtime/doc/editing.txt
+++ b/runtime/doc/editing.txt
@@ -1575,10 +1575,8 @@ There are three different types of searching:
/u/user_x/include
< Note: If your 'path' setting includes a non-existing directory, Vim will
- skip the non-existing directory, but continues searching in the parent of
- the non-existing directory if upwards searching is used. E.g. when
- searching "../include" and that doesn't exist, and upward searching is
- used, also searches in "..".
+ skip the non-existing directory, and also does not search in the parent of
+ the non-existing directory if upwards searching is used.
3) Combined up/downward search:
If Vim's current path is /u/user_x/work/release and you do >
diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt
index 5486c87af9..bd3acfcac7 100644
--- a/runtime/doc/filetype.txt
+++ b/runtime/doc/filetype.txt
@@ -140,7 +140,8 @@ variables can be used to overrule the filetype used for certain extensions:
*.asm g:asmsyntax |ft-asm-syntax|
*.asp g:filetype_asp |ft-aspvbs-syntax| |ft-aspperl-syntax|
*.bas g:filetype_bas |ft-basic-syntax|
- *.fs g:filetype_fs |ft-forth-syntax|
+ *.frm g:filetype_frm |ft-form-syntax|
+ *.fs g:filetype_fs |ft-forth-syntax|
*.i g:filetype_i |ft-progress-syntax|
*.inc g:filetype_inc
*.m g:filetype_m |ft-mathematica-syntax|
diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt
index 0345b14b7a..31a96298d2 100644
--- a/runtime/doc/quickfix.txt
+++ b/runtime/doc/quickfix.txt
@@ -497,7 +497,6 @@ EXECUTE A COMMAND IN ALL THE BUFFERS IN QUICKFIX OR LOCATION LIST:
autocommand event is disabled by adding it to
'eventignore'. This considerably speeds up editing
each buffer.
- {not in Vi}
Also see |:bufdo|, |:tabdo|, |:argdo|, |:windo|,
|:ldo|, |:cfdo| and |:lfdo|.
@@ -510,7 +509,6 @@ EXECUTE A COMMAND IN ALL THE BUFFERS IN QUICKFIX OR LOCATION LIST:
:{cmd}
etc.
< Otherwise it works the same as `:cdo`.
- {not in Vi}
*:ldo*
:ld[o][!] {cmd} Execute {cmd} in each valid entry in the location list
@@ -523,7 +521,6 @@ EXECUTE A COMMAND IN ALL THE BUFFERS IN QUICKFIX OR LOCATION LIST:
etc.
< Only valid entries in the location list are used.
Otherwise it works the same as `:cdo`.
- {not in Vi}
*:lfdo*
:lfdo[!] {cmd} Execute {cmd} in each file in the location list for
@@ -535,7 +532,6 @@ EXECUTE A COMMAND IN ALL THE BUFFERS IN QUICKFIX OR LOCATION LIST:
:{cmd}
etc.
< Otherwise it works the same as `:ldo`.
- {not in Vi}
FILTERING A QUICKFIX OR LOCATION LIST:
*cfilter-plugin* *:Cfilter* *:Lfilter*
diff --git a/runtime/doc/remote.txt b/runtime/doc/remote.txt
new file mode 100644
index 0000000000..b8991be738
--- /dev/null
+++ b/runtime/doc/remote.txt
@@ -0,0 +1,131 @@
+*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*
+
+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:
+
+ argument meaning ~
+
+ --remote [+{cmd}] {file} ... *--remote*
+ Open the file list in a remote Vim. When
+ there is no Vim server, execute locally.
+ Vim allows one init command: +{cmd}.
+ This must be an Ex command that can be
+ 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 >
+ nvim --remote-send "<C-\><C-N>:n filename<CR>"
+<
+ --remote-silent [+{cmd}] {file} ... *--remote-silent*
+ As above, but don't complain if there is no
+ server and the file is edited locally.
+ *--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-send*
+ --remote-send {keys} Send {keys} to server and exit. The {keys}
+ are not mapped. Special key names are
+ recognized, e.g., "<CR>" results in a CR
+ character.
+ *--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 ~
+
+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 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: >
+ nvim --remote --server ~/.cache/nvim/server.pipe file.txt
+
+Tell the remote server to write all files and exit: >
+ nvim --server ~/.cache/nvim/server.pipe --remote-send '<C-\><C-N>:wqa<CR>'
+
+
+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.
+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!
+
+
+==============================================================================
+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
+yet. The following command line arguments are not yet available:
+
+ argument meaning ~
+
+ --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.
+
+ *--serverlist*
+ --serverlist Not yet supported by Nvim.
+ Output a list of server names.
+
+
+
+
+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. Nvim
+either listens on a named pipe or a socket and does not yet support this
+--servername functionality.
+
+ vim:tw=78:sw=4:ts=8:noet:ft=help:norl:
diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt
index c8280173fb..c5f93fd66f 100644
--- a/runtime/doc/syntax.txt
+++ b/runtime/doc/syntax.txt
@@ -188,7 +188,8 @@ A syntax group name doesn't specify any color or attributes itself.
The name for a highlight or syntax group must consist of ASCII letters, digits
and the underscore. As a regexp: "[a-zA-Z0-9_]*". However, Vim does not give
-an error when using other characters.
+an error when using other characters. The maxium length of a group name is
+about 200 bytes. *E1249*
To be able to allow each user to pick their favorite set of colors, there must
be preferred names for highlight groups that are common for many languages.
@@ -1500,6 +1501,14 @@ The enhanced mode also takes advantage of additional color features for a dark
gvim display. Here, statements are colored LightYellow instead of Yellow, and
conditionals are LightBlue for better distinction.
+Both Visual Basic and FORM use the extension ".frm". To detect which one
+should be used, Vim checks for the string "VB_Name" in the first five lines of
+the file. If it is found, filetype will be "vb", otherwise "form".
+
+If the automatic detection doesn't work for you or you only edit, for
+example, FORM files, use this in your startup vimrc: >
+ :let filetype_frm = "form"
+
FORTH *forth.vim* *ft-forth-syntax*
diff --git a/runtime/doc/tabpage.txt b/runtime/doc/tabpage.txt
index c5b61e3a35..78b5101da7 100644
--- a/runtime/doc/tabpage.txt
+++ b/runtime/doc/tabpage.txt
@@ -133,7 +133,10 @@ something else.
:tabclose + " close the next tab page
:tabclose 3 " close the third tab page
:tabclose $ " close the last tab page
-<
+ :tabclose # " close the last accessed tab page
+
+When a tab is closed the next tab page will become the current one.
+
*:tabo* *:tabonly*
:tabo[nly][!] Close all other tab pages.
When the 'hidden' option is set, all buffers in closed windows
@@ -159,6 +162,8 @@ something else.
" one
:tabonly 1 " close all tab pages except the first one
:tabonly $ " close all tab pages except the last one
+ :tabonly # " close all tab pages except the last
+ " accessed one
SWITCHING TO ANOTHER TAB PAGE:
@@ -181,6 +186,7 @@ gt *i_CTRL-<PageDown>* *i_<C-PageDown>*
:+2tabnext " go to the two next tab page
:1tabnext " go to the first tab page
:$tabnext " go to the last tab page
+ :tabnext # " go to the last accessed tab page
:tabnext $ " as above
:tabnext - " go to the previous tab page
:tabnext -1 " as above
@@ -245,6 +251,8 @@ REORDERING TAB PAGES:
:tabmove " move the tab page to the last
:$tabmove " as above
:tabmove $ " as above
+ :tabmove # " move the tab page after the last accessed
+ " tab page
:tabm[ove] +[N]
:tabm[ove] -[N]
diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt
index 19e429fde2..75ee0fdfdf 100644
--- a/runtime/doc/various.txt
+++ b/runtime/doc/various.txt
@@ -432,7 +432,7 @@ g8 Print the hex values of the bytes used in the
used. In this example |:silent| is used to avoid the
message about reading the file and |:unsilent| to be
able to list the first line of each file. >
- :silent argdo unsilent echo expand('%') .. ": " .. getline(1)
+ :silent argdo unsilent echo expand('%') .. ": " .. getline(1)
<
*:verb* *:verbose*
diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua
index 3136e36043..a0c60a7dcf 100644
--- a/runtime/lua/vim/_editor.lua
+++ b/runtime/lua/vim/_editor.lua
@@ -636,6 +636,68 @@ function vim.pretty_print(...)
return ...
end
+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
+ f_silent = 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 == 'send' then
+ if rcid == 0 then
+ 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 = connection_failure_errmsg('Send expression failed.') }
+ end
+ print(vim.fn.rpcrequest(rcid, 'nvim_eval', args[2]))
+ return { should_exit = true, tabbed = false }
+ elseif subcmd ~= '' then
+ return { errmsg='Unknown option argument: ' .. args[1] }
+ end
+
+ if rcid == 0 then
+ if not f_silent then
+ vim.notify(connection_failure_errmsg("Editing locally"), vim.log.levels.WARN)
+ end
+ 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 = rcid ~= 0,
+ tabbed = f_tab,
+ }
+end
require('vim._meta')
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index ebf4f65c91..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(1, ...))");
+ 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 a575aba50a..230be9d9b9 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(&params, params.remote,
+ params.server_addr, argc, argv);
+ }
if (GARGCOUNT > 0) {
fname = get_fname(&params, cwd);
@@ -803,6 +807,91 @@ 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 *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);
+ }
+
+ 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, 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);
+ } 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;
+ }
+ }
+ 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;
+ }
+}
+
/// 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 +957,8 @@ static void command_line_scan(mparm_T *parmp)
// "--version" give version message
// "--noplugin[s]" skip plugins
// "--cmd <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 +997,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 +1233,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 <file>" already handled
break;
@@ -1291,6 +1390,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 +2142,8 @@ static void usage(void)
mch_msg(_(" --headless Don't start a user interface\n"));
mch_msg(_(" --listen <address> 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 <address> Specify RPC server to send commands to\n"));
mch_msg(_(" --startuptime <file> Write startup timing messages to <file>\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
diff --git a/test/functional/core/remote_spec.lua b/test/functional/core/remote_spec.lua
new file mode 100644
index 0000000000..602a5a71eb
--- /dev/null
+++ b/test/functional/core/remote_spec.lua
@@ -0,0 +1,142 @@
+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 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)
+
+ local 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..'<CR><C-W>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.
+ 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)
+ it('wait subcommand', function()
+ run_and_check_exit_code('--remote-wait', fname)
+ end)
+ end)
+end)