Merge #8247 'server: introduce --listen'

This commit is contained in:
Justin M. Keyes 2018-04-11 03:29:18 +02:00 committed by GitHub
commit f96d99ad11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 168 additions and 209 deletions

View File

@ -25,7 +25,7 @@ To enter commands in
type a colon type a colon
.Pq Sq \&: .Pq Sq \&:
which is also used in this manual to denote commands. which is also used in this manual to denote commands.
For more information, consult the on-line help system with the For more information, consult the online help system with the
.Ic :help .Ic :help
command. command.
.Bl -tag -width Fl .Bl -tag -width Fl
@ -329,6 +329,9 @@ Implies
.Fl -headless . .Fl -headless .
.It Fl -headless .It Fl -headless
Do not start a user interface. Do not start a user interface.
.Fl -listen .
.It Fl -listen Ar address
Start RPC server on this pipe or TCP socket.
.It Fl h , -help .It Fl h , -help
Print usage information and exit. Print usage information and exit.
.It Fl v , -version .It Fl v , -version
@ -337,12 +340,12 @@ Print version information and exit.
.Sh ENVIRONMENT .Sh ENVIRONMENT
.Bl -tag -width Fl .Bl -tag -width Fl
.It Ev VIM .It Ev VIM
Used to locate various user files, such as the user's init.vim. Used to locate various user files, such as init.vim.
.It Ev VIMRUNTIME .It Ev VIMRUNTIME
Used to locate run time files, such as on-line documentation and Used to locate runtime files, such as online documentation and
syntax highlighting definitions. syntax highlighting definitions.
.It Ev XDG_CONFIG_HOME .It Ev XDG_CONFIG_HOME
Path to use for the user-local configuration directory, see Path to the user-local configuration directory, see
.Sx FILES . .Sx FILES .
Defaults to Defaults to
.Pa ~/.config .Pa ~/.config
@ -356,7 +359,7 @@ Defaults to
.Pa ~/.local/share .Pa ~/.local/share
if not set. if not set.
.It Ev VIMINIT .It Ev VIMINIT
A string of Ex commands to be executed at startup. Ex commands to be executed at startup.
For example, the command to quit is For example, the command to quit is
.Ic :q , .Ic :q ,
so to have so to have
@ -375,41 +378,32 @@ command.
.Sh FILES .Sh FILES
.Bl -tag -width "~/.config/nvim/init.vim" .Bl -tag -width "~/.config/nvim/init.vim"
.It Pa ~/.config/nvim/init.vim .It Pa ~/.config/nvim/init.vim
The user-local User-local
.Nm .Nm
configuration file. configuration file.
See
.Ev XDG_CONFIG_HOME
above.
.It Pa ~/.config/nvim .It Pa ~/.config/nvim
The user-local User-local
.Nm .Nm
configuration directory. configuration directory.
See See also
.Ev XDG_CONFIG_HOME .Ev XDG_CONFIG_HOME .
above.
.It Pa $VIM/sysinit.vim .It Pa $VIM/sysinit.vim
The system-global System-global
.Nm .Nm
configuration file. configuration file.
.It Pa /usr/local/share/nvim .It Pa /usr/local/share/nvim
The system-global System-global
.Nm .Nm
runtime directory. runtime directory.
.El .El
.Sh AUTHORS .Sh AUTHORS
.Nm Nvim was started by
was started by .An Thiago de Arruda .
.An Thiago de Arruda ,
with a lot of help from others.
.Pp
Most of Vim was written by Most of Vim was written by
.An -nosplit .An -nosplit
.An Bram Moolenaar , .An Bram Moolenaar .
with a lot of help from others.
See See
.Ic :help credits . .Ic :help credits .
.Pp
Vim is based on Stevie, worked on by Vim is based on Stevie, worked on by
.An Tim Thompson , .An Tim Thompson ,
.An Tony Andrews , .An Tony Andrews ,

View File

@ -22,6 +22,10 @@ Commands ~
*:wv* *:wv*
*:wviminfo* Deprecated alias to |:wshada| command. *:wviminfo* Deprecated alias to |:wshada| command.
Environment Variables ~
*$NVIM_LISTEN_ADDRESS* Deprecated in favor of |--listen|. If both are given,
$NVIM_LISTEN_ADDRESS is ignored.
Events ~ Events ~
*EncodingChanged* Never fired; 'encoding' is always "utf-8". *EncodingChanged* Never fired; 'encoding' is always "utf-8".
*FileEncoding* Never fired; equivalent to |EncodingChanged|. *FileEncoding* Never fired; equivalent to |EncodingChanged|.

View File

@ -1788,9 +1788,9 @@ v:scrollstart String describing the script or function that caused the
hit-enter prompt. hit-enter prompt.
*v:servername* *servername-variable* *v:servername* *servername-variable*
*$NVIM_LISTEN_ADDRESS* v:servername Primary listen-address of the current Nvim instance, the first
v:servername Default Nvim server address. Equivalent to item returned by |serverlist()|. Can be set by |--listen| or
|$NVIM_LISTEN_ADDRESS| on startup. |serverstop()| |$NVIM_LISTEN_ADDRESS| at startup. |serverstart()| |serverstop()|
Read-only. Read-only.
@ -6638,15 +6638,11 @@ server2client({clientid}, {string}) *server2client()*
:echo server2client(expand("<client>"), "HELLO") :echo server2client(expand("<client>"), "HELLO")
< <
serverlist() *serverlist()* serverlist() *serverlist()*
Returns a list of available server names in a list. Returns a list of server addresses, or empty if all servers
When there are no servers an empty string is returned. were stopped. |serverstart()| |serverstop()|
Example: > Example: >
:echo serverlist() :echo serverlist()
< {Nvim} *--serverlist*
The Vim command-line option `--serverlist` was removed from
Nvim, but it can be imitated: >
nvim --cmd "echo serverlist()" --cmd "q"
<
serverstart([{address}]) *serverstart()* serverstart([{address}]) *serverstart()*
Opens a socket or named pipe at {address} and listens for Opens a socket or named pipe at {address} and listens for
|RPC| messages. Clients can send |API| commands to the address |RPC| messages. Clients can send |API| commands to the address
@ -6674,13 +6670,9 @@ serverstart([{address}]) *serverstart()*
< |$NVIM_LISTEN_ADDRESS| is set to {address} if not already set. < |$NVIM_LISTEN_ADDRESS| is set to {address} if not already set.
*--servername*
The Vim command-line option `--servername` can be imitated: >
nvim --cmd "let g:server_addr = serverstart('foo')"
<
serverstop({address}) *serverstop()* serverstop({address}) *serverstop()*
Closes the pipe or socket at {address}. Does nothing if Closes the pipe or socket at {address}.
{address} is empty or invalid. Returns TRUE if {address} is valid, else FALSE.
If |$NVIM_LISTEN_ADDRESS| is stopped it is unset. If |$NVIM_LISTEN_ADDRESS| is stopped it is unset.
If |v:servername| is stopped it is set to the next available If |v:servername| is stopped it is set to the next available
address returned by |serverlist()|. address returned by |serverlist()|.

View File

@ -70,9 +70,8 @@ An rpc socket is automatically created with each instance. The socket
location is stored in |v:servername|. By default this is a named pipe location is stored in |v:servername|. By default this is a named pipe
with an automatically generated address. See |XXX|. with an automatically generated address. See |XXX|.
To make Nvim listen on a TCP/IP socket instead, set the To make Nvim listen on a TCP/IP socket instead, specify |--listen|: >
|$NVIM_LISTEN_ADDRESS| environment variable before starting Nvim: > nvim --listen 127.0.0.1:6666
NVIM_LISTEN_ADDRESS=127.0.0.1:6666 nvim
<Also, more sockets and named pipes can be listened on using |serverstart()|. <Also, more sockets and named pipes can be listened on using |serverstart()|.
Note that localhost TCP sockets are generally less secure than named pipes, Note that localhost TCP sockets are generally less secure than named pipes,

View File

@ -355,6 +355,10 @@ argument.
instead. instead.
See also |silent-mode|, which does start a (limited) UI. See also |silent-mode|, which does start a (limited) UI.
--listen {addr} *--listen*
Start |RPC| server on socket or TCP address {addr}. Sets the
primary listen address |v:servername| to {addr}. |serverstart()|
============================================================================== ==============================================================================
2. Initialization *initialization* *startup* 2. Initialization *initialization* *startup*

View File

@ -1,50 +0,0 @@
#!/usr/bin/env expect
if {$argc < 2} {
puts "Need commands for running the tests and for starting nvim"
exit 1
}
set timeout 60
set run_tests [split [lindex $argv 0] " "]
set run_nvim [split [lindex $argv 1] " "]
# don't echo to stdout
log_user 0
# set NVIM_LISTEN_ADDRESS, so nvim will listen on a known socket
set env(NVIM_LISTEN_ADDRESS) "/tmp/nvim-[exec date +%s%N].sock"
# start nvim
spawn {*}$run_nvim
# save the job descriptor
set nvim_id $spawn_id
# Reset function that can be invoked by test runners to put nvim in a cleaner
# state
send {
:echo "read"."y"
}
# wait until nvim is ready
expect "ready"
# run tests
spawn {*}$run_tests
set tests_id $spawn_id
set status 1
# listen for test output in the background
expect_background {
* {
# show test output to the user
send_user -- $expect_out(buffer)
}
eof {
# collect the exit status code
set spawn_id $tests_id
catch wait result
set status [lindex $result 3]
set spawn_id $nvim_id
# quit nvim
send ":qa!\r"
}
}
# switch back nvim and wait until it exits
set spawn_id $nvim_id
expect eof
exit $status

View File

@ -14403,8 +14403,11 @@ static void f_serverstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return; return;
} }
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
if (argvars[0].vval.v_string) { if (argvars[0].vval.v_string) {
server_stop((char *) argvars[0].vval.v_string); bool rv = server_stop((char *)argvars[0].vval.v_string);
rettv->vval.v_number = (rv ? 1 : 0);
} }
} }

View File

@ -6975,12 +6975,10 @@ do_exedit (
ex_no_reprint = TRUE; ex_no_reprint = TRUE;
} }
/* /// ":gui" and ":gvim" when there is no GUI.
* ":gui" and ":gvim" when there is no GUI.
*/
static void ex_nogui(exarg_T *eap) static void ex_nogui(exarg_T *eap)
{ {
eap->errmsg = e_nogvim; eap->errmsg = (char_u *)N_("E25: Nvim does not have a built-in GUI");
} }

View File

@ -1070,7 +1070,6 @@ EXTERN char_u e_nesting[] INIT(= N_("E22: Scripts nested too deep"));
EXTERN char_u e_noalt[] INIT(= N_("E23: No alternate file")); EXTERN char_u e_noalt[] INIT(= N_("E23: No alternate file"));
EXTERN char_u e_noabbr[] INIT(= N_("E24: No such abbreviation")); EXTERN char_u e_noabbr[] INIT(= N_("E24: No such abbreviation"));
EXTERN char_u e_nobang[] INIT(= N_("E477: No ! allowed")); EXTERN char_u e_nobang[] INIT(= N_("E477: No ! allowed"));
EXTERN char_u e_nogvim[] INIT(= N_("E25: Nvim does not have a built-in GUI"));
EXTERN char_u e_nogroup[] INIT(= N_("E28: No such highlight group name: %s")); EXTERN char_u e_nogroup[] INIT(= N_("E28: No such highlight group name: %s"));
EXTERN char_u e_noinstext[] INIT(= N_("E29: No inserted text yet")); EXTERN char_u e_noinstext[] INIT(= N_("E29: No inserted text yet"));
EXTERN char_u e_nolastcmd[] INIT(= N_("E30: No previous command line")); EXTERN char_u e_nolastcmd[] INIT(= N_("E30: No previous command line"));

View File

@ -72,30 +72,30 @@
# include "nvim/os/pty_process_unix.h" # include "nvim/os/pty_process_unix.h"
#endif #endif
/* Maximum number of commands from + or -c arguments. */ // Maximum number of commands from + or -c arguments.
#define MAX_ARG_CMDS 10 #define MAX_ARG_CMDS 10
/* values for "window_layout" */ // values for "window_layout"
#define WIN_HOR 1 /* "-o" horizontally split windows */ #define WIN_HOR 1 // "-o" horizontally split windows
#define WIN_VER 2 /* "-O" vertically split windows */ #define WIN_VER 2 // "-O" vertically split windows
#define WIN_TABS 3 /* "-p" windows on tab pages */ #define WIN_TABS 3 // "-p" windows on tab pages
/* Struct for various parameters passed between main() and other functions. */ // Struct for various parameters passed between main() and other functions.
typedef struct { typedef struct {
int argc; int argc;
char **argv; char **argv;
char *use_vimrc; // vimrc from -u argument char *use_vimrc; // vimrc from -u argument
int n_commands; /* no. of commands from + or -c */ int n_commands; // no. of commands from + or -c
char *commands[MAX_ARG_CMDS]; // commands from + or -c arg char *commands[MAX_ARG_CMDS]; // commands from + or -c arg
char_u cmds_tofree[MAX_ARG_CMDS]; /* commands that need free() */ char_u cmds_tofree[MAX_ARG_CMDS]; // commands that need free()
int n_pre_commands; /* no. of commands from --cmd */ int n_pre_commands; // no. of commands from --cmd
char *pre_commands[MAX_ARG_CMDS]; // commands from --cmd argument char *pre_commands[MAX_ARG_CMDS]; // commands from --cmd argument
int edit_type; /* type of editing to do */ int edit_type; // type of editing to do
char_u *tagname; /* tag from -t argument */ char_u *tagname; // tag from -t argument
char_u *use_ef; /* 'errorfile' from -q argument */ char_u *use_ef; // 'errorfile' from -q argument
int want_full_screen; int want_full_screen;
bool input_isatty; // stdin is a terminal bool input_isatty; // stdin is a terminal
@ -103,13 +103,15 @@ typedef struct {
bool err_isatty; // stderr is a terminal bool err_isatty; // stderr is a terminal
int no_swap_file; // "-n" argument used int no_swap_file; // "-n" argument used
int use_debug_break_level; int use_debug_break_level;
int window_count; /* number of windows to use */ int window_count; // number of windows to use
int window_layout; /* 0, WIN_HOR, WIN_VER or WIN_TABS */ int window_layout; // 0, WIN_HOR, WIN_VER or WIN_TABS
#if !defined(UNIX) #if !defined(UNIX)
int literal; /* don't expand file names */ int literal; // don't expand file names
#endif #endif
int diff_mode; /* start with 'diff' set */ int diff_mode; // start with 'diff' set
char *listen_addr; // --listen {address}
} mparm_T; } mparm_T;
/* Values for edit_type. */ /* Values for edit_type. */
@ -150,7 +152,6 @@ void event_init(void)
signal_init(); signal_init();
// finish mspgack-rpc initialization // finish mspgack-rpc initialization
channel_init(); channel_init();
server_init();
terminal_init(); terminal_init();
} }
@ -241,9 +242,8 @@ int main(int argc, char **argv)
char_u *cwd = NULL; // current workding dir on startup char_u *cwd = NULL; // current workding dir on startup
time_init(); time_init();
/* Many variables are in "params" so that we can pass them to invoked // Many variables are in `params` so that we can pass them around easily.
* functions without a lot of arguments. "argc" and "argv" are also // `argc` and `argv` are also copied, so that they can be changed.
* copied, so that they can be changed. */
init_params(&params, argc, argv); init_params(&params, argc, argv);
init_startuptime(&params); init_startuptime(&params);
@ -254,11 +254,10 @@ int main(int argc, char **argv)
check_and_set_isatty(&params); check_and_set_isatty(&params);
event_init(); event_init();
/* // Process the command line arguments. File names are put in the global
* Process the command line arguments. File names are put in the global // argument list "global_alist".
* argument list "global_alist".
*/
command_line_scan(&params); command_line_scan(&params);
server_init(params.listen_addr);
if (GARGCOUNT > 0) { if (GARGCOUNT > 0) {
fname = get_fname(&params, cwd); fname = get_fname(&params, cwd);
@ -819,6 +818,9 @@ static void command_line_scan(mparm_T *parmp)
if (!channel_from_stdio(true, CALLBACK_READER_INIT, &err)) { if (!channel_from_stdio(true, CALLBACK_READER_INIT, &err)) {
abort(); abort();
} }
} else if (STRNICMP(argv[0] + argv_idx, "listen", 6) == 0) {
want_argument = true;
argv_idx += 6;
} else if (STRNICMP(argv[0] + argv_idx, "literal", 7) == 0) { } else if (STRNICMP(argv[0] + argv_idx, "literal", 7) == 0) {
#if !defined(UNIX) #if !defined(UNIX)
parmp->literal = TRUE; parmp->literal = TRUE;
@ -864,10 +866,6 @@ static void command_line_scan(mparm_T *parmp)
case 'f': /* "-f" GUI: run in foreground. */ case 'f': /* "-f" GUI: run in foreground. */
break; break;
case 'g': /* "-g" start GUI */
main_start_gui();
break;
case 'F': { // "-F" start in Farsi mode: rl + fkmap set. case 'F': { // "-F" start in Farsi mode: rl + fkmap set.
p_fkmap = true; p_fkmap = true;
set_option_value("rl", 1L, NULL, 0); set_option_value("rl", 1L, NULL, 0);
@ -906,18 +904,8 @@ static void command_line_scan(mparm_T *parmp)
parmp->no_swap_file = TRUE; parmp->no_swap_file = TRUE;
break; break;
case 'p': /* "-p[N]" open N tab pages */ case 'p': // "-p[N]" open N tab pages
#ifdef TARGET_API_MAC_OSX // default is 0: open window for each file
/* For some reason on MacOS X, an argument like:
-psn_0_10223617 is passed in when invoke from Finder
or with the 'open' command */
if (argv[0][argv_idx] == 's') {
argv_idx = -1; /* bypass full -psn */
main_start_gui();
break;
}
#endif
/* default is 0: open window for each file */
parmp->window_count = get_number_arg(argv[0], &argv_idx, 0); parmp->window_count = get_number_arg(argv[0], &argv_idx, 0);
parmp->window_layout = WIN_TABS; parmp->window_layout = WIN_TABS;
break; break;
@ -1030,15 +1018,12 @@ static void command_line_scan(mparm_T *parmp)
mainerr(err_opt_unknown, argv[0]); mainerr(err_opt_unknown, argv[0]);
} }
/* // Handle option arguments with argument.
* Handle option arguments with argument.
*/
if (want_argument) { if (want_argument) {
/* // Check for garbage immediately after the option letter.
* Check for garbage immediately after the option letter. if (argv[0][argv_idx] != NUL) {
*/
if (argv[0][argv_idx] != NUL)
mainerr(err_opt_garbage, argv[0]); mainerr(err_opt_garbage, argv[0]);
}
--argc; --argc;
if (argc < 1 && c != 'S') /* -S has an optional argument */ if (argc < 1 && c != 'S') /* -S has an optional argument */
@ -1077,13 +1062,17 @@ static void command_line_scan(mparm_T *parmp)
break; break;
case '-': case '-':
if (argv[-1][2] == 'c') { if (strequal(argv[-1], "--cmd")) {
/* "--cmd {command}" execute command */ // "--cmd {command}" execute command
if (parmp->n_pre_commands >= MAX_ARG_CMDS) if (parmp->n_pre_commands >= MAX_ARG_CMDS) {
mainerr(err_extra_cmd, NULL); mainerr(err_extra_cmd, NULL);
}
parmp->pre_commands[parmp->n_pre_commands++] = argv[0]; parmp->pre_commands[parmp->n_pre_commands++] = argv[0];
} else if (strequal(argv[-1], "--listen")) {
// "--listen {address}"
parmp->listen_addr = argv[0];
} }
/* "--startuptime <file>" already handled */ // "--startuptime <file>" already handled
break; break;
case 'q': /* "-q {errorfile}" QuickFix mode */ case 'q': /* "-q {errorfile}" QuickFix mode */
@ -1224,11 +1213,10 @@ static void init_params(mparm_T *paramp, int argc, char **argv)
paramp->want_full_screen = true; paramp->want_full_screen = true;
paramp->use_debug_break_level = -1; paramp->use_debug_break_level = -1;
paramp->window_count = -1; paramp->window_count = -1;
paramp->listen_addr = NULL;
} }
/* /// Initialize global startuptime file if "--startuptime" passed as an argument.
* Initialize global startuptime file if "--startuptime" passed as an argument.
*/
static void init_startuptime(mparm_T *paramp) static void init_startuptime(mparm_T *paramp)
{ {
for (int i = 1; i < paramp->argc; i++) { for (int i = 1; i < paramp->argc; i++) {
@ -1834,17 +1822,6 @@ static void source_startup_scripts(const mparm_T *const parmp)
TIME_MSG("sourcing vimrc file(s)"); TIME_MSG("sourcing vimrc file(s)");
} }
/*
* Setup to start using the GUI. Exit with an error when not available.
*/
static void main_start_gui(void)
{
mch_errmsg(_(e_nogvim));
mch_errmsg("\n");
mch_exit(2);
}
/// Get an environment variable, and execute it as Ex commands. /// Get an environment variable, and execute it as Ex commands.
/// ///
/// @param env environment variable to execute /// @param env environment variable to execute
@ -1968,6 +1945,7 @@ static void usage(void)
mch_msg(_(" --api-info Write msgpack-encoded API metadata to stdout\n")); mch_msg(_(" --api-info Write msgpack-encoded API metadata to stdout\n"));
mch_msg(_(" --embed Use stdin/stdout as a msgpack-rpc channel\n")); mch_msg(_(" --embed Use stdin/stdout as a msgpack-rpc channel\n"));
mch_msg(_(" --headless Don't start a user interface\n")); mch_msg(_(" --headless Don't start a user interface\n"));
mch_msg(_(" --listen <address> Start RPC server at this address\n"));
#if !defined(UNIX) #if !defined(UNIX)
mch_msg(_(" --literal Don't expand wildcards\n")); mch_msg(_(" --literal Don't expand wildcards\n"));
#endif #endif

View File

@ -32,26 +32,27 @@ static garray_T watchers = GA_EMPTY_INIT_VALUE;
#endif #endif
/// Initializes the module /// Initializes the module
bool server_init(void) bool server_init(const char *listen_addr)
{ {
ga_init(&watchers, sizeof(SocketWatcher *), 1); ga_init(&watchers, sizeof(SocketWatcher *), 1);
bool must_free = false; // $NVIM_LISTEN_ADDRESS
const char *listen_address = os_getenv(LISTEN_ADDRESS_ENV_VAR); const char *env_addr = os_getenv(LISTEN_ADDRESS_ENV_VAR);
if (listen_address == NULL) { int rv = listen_addr == NULL ? 1 : server_start(listen_addr);
must_free = true;
listen_address = server_address_new(); if (0 != rv) {
rv = env_addr == NULL ? 1 : server_start(env_addr);
if (0 != rv) {
listen_addr = server_address_new();
if (listen_addr == NULL) {
return false;
}
rv = server_start(listen_addr);
xfree((char *)listen_addr);
}
} }
if (!listen_address) { return rv == 0;
return false;
}
bool ok = (server_start(listen_address) == 0);
if (must_free) {
xfree((char *) listen_address);
}
return ok;
} }
/// Teardown a single server /// Teardown a single server
@ -120,8 +121,8 @@ bool server_owns_pipe_address(const char *path)
/// @param endpoint Address of the server. Either a 'ip:[port]' string or an /// @param endpoint Address of the server. Either a 'ip:[port]' string or an
/// arbitrary identifier (trimmed to 256 bytes) for the Unix /// arbitrary identifier (trimmed to 256 bytes) for the Unix
/// socket or named pipe. /// socket or named pipe.
/// @returns 0 on success, 1 on a regular error, and negative errno /// @returns 0: success, 1: validation error, 2: already listening,
/// on failure to bind or listen. /// -errno: failed to bind or listen.
int server_start(const char *endpoint) int server_start(const char *endpoint)
{ {
if (endpoint == NULL || endpoint[0] == '\0') { if (endpoint == NULL || endpoint[0] == '\0') {
@ -145,7 +146,7 @@ int server_start(const char *endpoint)
uv_freeaddrinfo(watcher->uv.tcp.addrinfo); uv_freeaddrinfo(watcher->uv.tcp.addrinfo);
} }
socket_watcher_close(watcher, free_server); socket_watcher_close(watcher, free_server);
return 1; return 2;
} }
} }
@ -177,7 +178,7 @@ int server_start(const char *endpoint)
/// Stops listening on the address specified by `endpoint`. /// Stops listening on the address specified by `endpoint`.
/// ///
/// @param endpoint Address of the server. /// @param endpoint Address of the server.
void server_stop(char *endpoint) bool server_stop(char *endpoint)
{ {
SocketWatcher *watcher; SocketWatcher *watcher;
bool watcher_found = false; bool watcher_found = false;
@ -196,8 +197,8 @@ void server_stop(char *endpoint)
} }
if (!watcher_found) { if (!watcher_found) {
ELOG("Not listening on %s", addr); WLOG("Not listening on %s", addr);
return; return false;
} }
// Unset $NVIM_LISTEN_ADDRESS if it is the stopped address. // Unset $NVIM_LISTEN_ADDRESS if it is the stopped address.
@ -219,6 +220,8 @@ void server_stop(char *endpoint)
if (STRCMP(addr, get_vim_var_str(VV_SEND_SERVER)) == 0) { if (STRCMP(addr, get_vim_var_str(VV_SEND_SERVER)) == 0) {
set_vservername(&watchers); set_vservername(&watchers);
} }
return true;
} }
/// Returns an allocated array of server addresses. /// Returns an allocated array of server addresses.

View File

@ -1,31 +1,40 @@
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local eq, neq, eval = helpers.eq, helpers.neq, helpers.eval local eq, neq, eval = helpers.eq, helpers.neq, helpers.eval
local command = helpers.command local command = helpers.command
local clear, funcs, meths = helpers.clear, helpers.funcs, helpers.meths local clear, funcs, meths = helpers.clear, helpers.funcs, helpers.meths
local os_name = helpers.os_name local iswin = helpers.iswin
local ok = helpers.ok
local matches = helpers.matches
local function clear_serverlist() local function clear_serverlist()
for _, server in pairs(funcs.serverlist()) do for _, server in pairs(funcs.serverlist()) do
funcs.serverstop(server) funcs.serverstop(server)
end end
end end
describe('serverstart(), serverstop()', function() describe('server', function()
before_each(clear) before_each(clear)
it('sets $NVIM_LISTEN_ADDRESS on first invocation', function() it('serverstart() sets $NVIM_LISTEN_ADDRESS on first invocation', function()
-- Unset $NVIM_LISTEN_ADDRESS -- Unset $NVIM_LISTEN_ADDRESS
command('let $NVIM_LISTEN_ADDRESS = ""') command('let $NVIM_LISTEN_ADDRESS = ""')
local s = eval('serverstart()') local s = eval('serverstart()')
assert(s ~= nil and s:len() > 0, "serverstart() returned empty") assert(s ~= nil and s:len() > 0, "serverstart() returned empty")
eq(s, eval('$NVIM_LISTEN_ADDRESS')) eq(s, eval('$NVIM_LISTEN_ADDRESS'))
command("call serverstop('"..s.."')") eq(1, eval("serverstop('"..s.."')"))
eq('', eval('$NVIM_LISTEN_ADDRESS')) eq('', eval('$NVIM_LISTEN_ADDRESS'))
end) end)
it('sets v:servername _only_ on nvim startup unless all servers are stopped', it('sets new v:servername if $NVIM_LISTEN_ADDRESS is invalid', function()
clear({env={NVIM_LISTEN_ADDRESS='.'}})
eq('.', eval('$NVIM_LISTEN_ADDRESS'))
local servers = funcs.serverlist()
eq(1, #servers)
ok(string.len(servers[1]) > 4) -- Like /tmp/nvim…/… or \\.\pipe\…
end)
it('sets v:servername at startup or if all servers were stopped',
function() function()
local initial_server = meths.get_vvar('servername') local initial_server = meths.get_vvar('servername')
assert(initial_server ~= nil and initial_server:len() > 0, assert(initial_server ~= nil and initial_server:len() > 0,
@ -38,24 +47,23 @@ describe('serverstart(), serverstop()', function()
neq(initial_server, s) neq(initial_server, s)
-- serverstop() does _not_ modify v:servername... -- serverstop() does _not_ modify v:servername...
funcs.serverstop(s) eq(1, funcs.serverstop(s))
eq(initial_server, meths.get_vvar('servername')) eq(initial_server, meths.get_vvar('servername'))
-- ...unless we stop _all_ servers. -- ...unless we stop _all_ servers.
funcs.serverstop(funcs.serverlist()[1]) eq(1, funcs.serverstop(funcs.serverlist()[1]))
eq('', meths.get_vvar('servername')) eq('', meths.get_vvar('servername'))
-- v:servername will take the next available server. -- v:servername will take the next available server.
local servername = (os_name() == 'windows' local servername = (iswin() and [[\\.\pipe\Xtest-functional-server-pipe]]
and [[\\.\pipe\Xtest-functional-server-pipe]] or 'Xtest-functional-server-socket')
or 'Xtest-functional-server-socket')
funcs.serverstart(servername) funcs.serverstart(servername)
eq(servername, meths.get_vvar('servername')) eq(servername, meths.get_vvar('servername'))
end) end)
it('serverstop() ignores invalid input', function() it('serverstop() returns false for invalid input', function()
command("call serverstop('')") eq(0, eval("serverstop('')"))
command("call serverstop('bogus-socket-name')") eq(0, eval("serverstop('bogus-socket-name')"))
end) end)
it('parses endpoints correctly', function() it('parses endpoints correctly', function()
@ -96,17 +104,13 @@ describe('serverstart(), serverstop()', function()
funcs.serverstart('127.0.0.1:65536') -- invalid port funcs.serverstart('127.0.0.1:65536') -- invalid port
eq({}, funcs.serverlist()) eq({}, funcs.serverlist())
end) end)
end)
describe('serverlist()', function() it('serverlist() returns the list of servers', function()
before_each(clear)
it('returns the list of servers', function()
-- There should already be at least one server. -- There should already be at least one server.
local n = eval('len(serverlist())') local n = eval('len(serverlist())')
-- Add a few -- Add some servers.
local servs = (os_name() == 'windows' local servs = (iswin()
and { [[\\.\pipe\Xtest-pipe0934]], [[\\.\pipe\Xtest-pipe4324]] } and { [[\\.\pipe\Xtest-pipe0934]], [[\\.\pipe\Xtest-pipe4324]] }
or { [[Xtest-pipe0934]], [[Xtest-pipe4324]] }) or { [[Xtest-pipe0934]], [[Xtest-pipe4324]] })
for _, s in ipairs(servs) do for _, s in ipairs(servs) do
@ -120,9 +124,31 @@ describe('serverlist()', function()
-- The new servers should be at the end of the list. -- The new servers should be at the end of the list.
for i = 1, #servs do for i = 1, #servs do
eq(servs[i], new_servs[i + n]) eq(servs[i], new_servs[i + n])
command("call serverstop('"..servs[i].."')") eq(1, eval("serverstop('"..servs[i].."')"))
end end
-- After serverstop() the servers should NOT be in the list. -- After serverstop() the servers should NOT be in the list.
eq(n, eval('len(serverlist())')) eq(n, eval('len(serverlist())'))
end) end)
end) end)
describe('startup --listen', function()
it('validates', function()
clear()
local cmd = { unpack(helpers.nvim_argv) }
table.insert(cmd, '--listen')
matches('nvim.*: Argument missing after: "%-%-listen"', funcs.system(cmd))
cmd = { unpack(helpers.nvim_argv) }
table.insert(cmd, '--listen2')
matches('nvim.*: Garbage after option argument: "%-%-listen2"', funcs.system(cmd))
end)
it('sets v:servername, overrides $NVIM_LISTEN_ADDRESS', function()
local addr = (iswin() and [[\\.\pipe\Xtest-listen-pipe]]
or 'Xtest-listen-pipe')
clear({ env={ NVIM_LISTEN_ADDRESS='Xtest-env-pipe' },
args={ '--listen', addr } })
eq(addr, meths.get_vvar('servername'))
end)
end)

View File

@ -16,6 +16,7 @@ local neq = global_helpers.neq
local eq = global_helpers.eq local eq = global_helpers.eq
local ok = global_helpers.ok local ok = global_helpers.ok
local map = global_helpers.map local map = global_helpers.map
local matches = global_helpers.matches
local filter = global_helpers.filter local filter = global_helpers.filter
local dedent = global_helpers.dedent local dedent = global_helpers.dedent
local table_flatten = global_helpers.table_flatten local table_flatten = global_helpers.table_flatten
@ -747,6 +748,7 @@ local module = {
insert = insert, insert = insert,
iswin = iswin, iswin = iswin,
map = map, map = map,
matches = matches,
merge_args = merge_args, merge_args = merge_args,
meth_pcall = meth_pcall, meth_pcall = meth_pcall,
meths = meths, meths = meths,

View File

@ -16,6 +16,12 @@ end
local function ok(res) local function ok(res)
return assert.is_true(res) return assert.is_true(res)
end end
local function matches(pat, actual)
if nil ~= string.match(actual, pat) then
return true
end
error(string.format('Pattern does not match.\nPattern:\n%s\nActual:\n%s', pat, actual))
end
-- initial_path: directory to recurse into -- initial_path: directory to recurse into
-- re: include pattern (string) -- re: include pattern (string)
@ -572,6 +578,7 @@ return {
hasenv = hasenv, hasenv = hasenv,
intchar2lua = intchar2lua, intchar2lua = intchar2lua,
map = map, map = map,
matches = matches,
mergedicts_copy = mergedicts_copy, mergedicts_copy = mergedicts_copy,
neq = neq, neq = neq,
ok = ok, ok = ok,