diff options
-rw-r--r-- | runtime/doc/builtin.txt | 4 | ||||
-rw-r--r-- | runtime/doc/change.txt | 10 | ||||
-rw-r--r-- | runtime/doc/cmdline.txt | 2 | ||||
-rw-r--r-- | runtime/doc/editing.txt | 6 | ||||
-rw-r--r-- | runtime/doc/filetype.txt | 3 | ||||
-rw-r--r-- | runtime/doc/quickfix.txt | 4 | ||||
-rw-r--r-- | runtime/doc/remote.txt | 131 | ||||
-rw-r--r-- | runtime/doc/syntax.txt | 11 | ||||
-rw-r--r-- | runtime/doc/tabpage.txt | 10 | ||||
-rw-r--r-- | runtime/doc/various.txt | 2 | ||||
-rw-r--r-- | runtime/lua/vim/_editor.lua | 62 | ||||
-rw-r--r-- | src/nvim/api/vim.c | 2 | ||||
-rw-r--r-- | src/nvim/main.c | 103 | ||||
-rw-r--r-- | src/nvim/main.h | 2 | ||||
-rw-r--r-- | test/functional/core/remote_spec.lua | 142 |
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(¶ms, params.remote, + params.server_addr, argc, argv); + } if (GARGCOUNT > 0) { fname = get_fname(¶ms, 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) |