diff options
-rw-r--r-- | runtime/doc/eval.txt | 40 | ||||
-rw-r--r-- | runtime/doc/msgpack_rpc.txt | 2 | ||||
-rw-r--r-- | runtime/doc/remote.txt | 8 | ||||
-rw-r--r-- | src/nvim/eval.c | 6 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/server.c | 52 | ||||
-rw-r--r-- | test/functional/server/server_spec.lua | 53 |
6 files changed, 110 insertions, 51 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 3865175a1f..5eb8f62d8b 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1583,7 +1583,9 @@ v:scrollstart String describing the script or function that caused the hit-enter prompt. *v:servername* *servername-variable* -v:servername The resulting registered |x11-clientserver| name if any. +v:servername Default {Nvim} server address. Equivalent to + |$NVIM_LISTEN_ADDRESS| on startup, but may differ if the + latter is modified or unset. |serverstop()| Read-only. @@ -2366,8 +2368,8 @@ char2nr({expr}[, {utf8}]) *char2nr()* char2nr("ABC") returns 65 < When {utf8} is omitted or zero, the current 'encoding' is used. Example for "utf-8": > - char2nr("á") returns 225 - char2nr("á"[0]) returns 195 + char2nr("á") returns 225 + char2nr("á"[0]) returns 195 < With {utf8} set to 1, always treat as utf-8 characters. A combining character is a separate character. |nr2char()| does the opposite. @@ -5374,25 +5376,31 @@ server2client( {clientid}, {string}) *server2client()* Example: > :echo server2client(expand("<client>"), "HELLO") < -serverlist() *serverlist()* +serverlist() *serverlist()* Returns a list of available server names in a list. When there are no servers an empty string is returned. Example: > :echo serverlist() +< *--serverlist* + The Vim command-line option `--serverlist` can be imitated: > + nvim --cmd "echo serverlist()" --cmd "q" +< +serverstart([{address}]) *serverstart()* + Opens a named pipe or TCP socket at {address} for clients to + connect to and returns {address}. If no address is given, it + is equivalent to: > + :call serverstart(tempname()) +< |$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')" < -serverlisten([{address}]) *serverlisten()* - Opens a Unix or TCP socket at {address} for clients to connect - to and returns {address}. If no address is given, it is - equivalent to > - :call serverlisten(tempname()) -< If |$NVIM_LISTEN_ADDRESS| is not set, it will be set to - {address}. - serverstop({address}) *serverstop()* - Closes the Unix or TCP socket at {address}. Does nothing if - {address} is empty, or does not refer to a server. If - {address} equals |$NVIM_LISTEN_ADDRESS|, the listen address - will be unset. + Closes the pipe or socket at {address}. Does nothing if + {address} is empty or invalid. + If |$NVIM_LISTEN_ADDRESS| is stopped it is unset. + If |v:servername| is stopped it is set to the next available + address returned by |serverlist()|. setbufvar({expr}, {varname}, {val}) *setbufvar()* Set option or local variable {varname} in buffer {expr} to diff --git a/runtime/doc/msgpack_rpc.txt b/runtime/doc/msgpack_rpc.txt index b6142e2234..8304d79c17 100644 --- a/runtime/doc/msgpack_rpc.txt +++ b/runtime/doc/msgpack_rpc.txt @@ -81,6 +81,8 @@ There are four ways to open msgpack-rpc streams to nvim: > :echo $NVIM_LISTEN_ADDRESS < + See also |v:servername|. + 4. Through a TCP/IP socket. To make nvim listen on a TCP/IP socket, set the |$NVIM_LISTEN_ADDRESS| environment variable in a shell before starting: > diff --git a/runtime/doc/remote.txt b/runtime/doc/remote.txt index e84b7fc5e9..81ab72a100 100644 --- a/runtime/doc/remote.txt +++ b/runtime/doc/remote.txt @@ -56,11 +56,6 @@ The following command line arguments are available: *--remote-tab-wait-silent* --remote-tab-wait-silent Like --remote-wait-silent but open each file in a new tabpage. - *--servername* - --servername {name} Become the server {name}. When used together - with one of the --remote commands: connect to - server {name} instead of the default (see - below). *--remote-send* --remote-send {keys} Send {keys} to server and exit. The {keys} are not mapped. Special key names are @@ -69,9 +64,6 @@ The following command line arguments are available: *--remote-expr* --remote-expr {expr} Evaluate {expr} in server and print the result on stdout. - *--serverlist* - --serverlist Output a list of server names. - Examples ~ diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 906659a1f3..16b368c555 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -365,7 +365,7 @@ static struct vimvar { } vimvars[VV_LEN] = { /* - * The order here must match the VV_ defines in vim.h! + * The order here must match the VV_ defines in eval.h! * Initializing a union does not work, leave tv.vval empty to get zero's. */ {VV_NAME("count", VAR_NUMBER), VV_COMPAT+VV_RO}, @@ -13356,7 +13356,7 @@ static void f_serverlist(typval_T *argvars, typval_T *rettv) static void f_serverstart(typval_T *argvars, typval_T *rettv) { rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; // Will hold the address of the new server. + rettv->vval.v_string = NULL; // Address of the new server if (check_restricted() || check_secure()) { return; @@ -16294,7 +16294,7 @@ long get_vim_var_nr(int idx) /* * Get string v: variable value. Uses a static buffer, can only be used once. */ -char_u *get_vim_var_str(int idx) +char_u *get_vim_var_str(int idx) FUNC_ATTR_NONNULL_RET { return get_tv_string(&vimvars[idx].vv_tv); } diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c index 7f7291c3b9..ab1b04d73f 100644 --- a/src/nvim/msgpack_rpc/server.c +++ b/src/nvim/msgpack_rpc/server.c @@ -9,6 +9,7 @@ #include "nvim/msgpack_rpc/server.h" #include "nvim/os/os.h" #include "nvim/ascii.h" +#include "nvim/eval.h" #include "nvim/garray.h" #include "nvim/vim.h" #include "nvim/memory.h" @@ -18,7 +19,7 @@ #define MAX_CONNECTIONS 32 #define ADDRESS_MAX_SIZE 256 -#define NEOVIM_DEFAULT_TCP_PORT 7450 +#define NVIM_DEFAULT_TCP_PORT 7450 #define LISTEN_ADDRESS_ENV_VAR "NVIM_LISTEN_ADDRESS" typedef enum { @@ -27,13 +28,13 @@ typedef enum { } ServerType; typedef struct { - // The address of a pipe, or string value of a tcp address. + // Pipe/socket path, or TCP address string char addr[ADDRESS_MAX_SIZE]; // Type of the union below ServerType type; - // This is either a tcp server or unix socket(named pipe on windows) + // TCP server or unix socket (named pipe on Windows) union { struct { uv_tcp_t handle; @@ -84,23 +85,32 @@ static void server_close_cb(Server **server) uv_close(server_handle(*server), free_server); } +/// Set v:servername to the first server in the server list, or unset it if no +/// servers are known. +static void set_vservername(garray_T *srvs) +{ + char *default_server = (srvs->ga_len > 0) + ? ((Server **)srvs->ga_data)[0]->addr + : NULL; + set_vim_var_string(VV_SEND_SERVER, (char_u *)default_server, -1); +} + /// Teardown the server module void server_teardown(void) { GA_DEEP_CLEAR(&servers, Server *, server_close_cb); } -/// Starts listening on arbitrary tcp/unix addresses specified by -/// `endpoint` for API calls. The type of socket used(tcp or unix/pipe) will -/// be determined by parsing `endpoint`: If it's a valid tcp address in the -/// 'ip[:port]' format, then it will be tcp socket. The port is optional -/// and if omitted will default to NEOVIM_DEFAULT_TCP_PORT. Otherwise it will -/// be a unix socket or named pipe. +/// Starts listening for API calls on the TCP address or pipe path `endpoint`. +/// The socket type is determined by parsing `endpoint`: If it's a valid IPv4 +/// address in 'ip[:port]' format, then it will be TCP socket. The port is +/// optional and if omitted defaults to NVIM_DEFAULT_TCP_PORT. Otherwise it +/// will be a unix socket or named pipe. /// /// @param endpoint Address of the server. Either a 'ip[:port]' string or an -/// arbitrary identifier(trimmed to 256 bytes) for the unix socket or +/// arbitrary identifier (trimmed to 256 bytes) for the unix socket or /// named pipe. -/// @returns zero if successful, one on a regular error, and negative errno +/// @returns 0 on success, 1 on a regular error, and negative errno /// on failure to bind or connect. int server_start(const char *endpoint) FUNC_ATTR_NONNULL_ALL @@ -134,14 +144,14 @@ int server_start(const char *endpoint) size_t addr_len = (size_t)(ip_end - addr); if (addr_len > sizeof(ip) - 1) { - // Maximum length of an IP address buffer is 15(eg: 255.255.255.255) + // Maximum length of an IPv4 address buffer is 15 (eg: 255.255.255.255) addr_len = sizeof(ip) - 1; } // Extract the address part xstrlcpy(ip, addr, addr_len + 1); - int port = NEOVIM_DEFAULT_TCP_PORT; + int port = NVIM_DEFAULT_TCP_PORT; if (*ip_end == ':') { // Extract the port @@ -216,6 +226,11 @@ int server_start(const char *endpoint) ga_grow(&servers, 1); ((Server **)servers.ga_data)[servers.ga_len++] = server; + // Update v:servername, if not set. + if (STRLEN(get_vim_var_str(VV_SEND_SERVER)) == 0) { + set_vservername(&servers); + } + return 0; } @@ -230,7 +245,7 @@ void server_stop(char *endpoint) // Trim to `ADDRESS_MAX_SIZE` xstrlcpy(addr, endpoint, sizeof(addr)); - int i = 0; // The index of the server whose address equals addr. + int i = 0; // Index of the server whose address equals addr. for (; i < servers.ga_len; i++) { server = ((Server **)servers.ga_data)[i]; if (strcmp(addr, server->addr) == 0) { @@ -243,9 +258,9 @@ void server_stop(char *endpoint) return; } - // If we are invalidating the listen address, unset it. + // Unset $NVIM_LISTEN_ADDRESS if it is the stopped address. const char *listen_address = os_getenv(LISTEN_ADDRESS_ENV_VAR); - if (listen_address && strcmp(addr, listen_address) == 0) { + if (listen_address && STRCMP(addr, listen_address) == 0) { os_unsetenv(LISTEN_ADDRESS_ENV_VAR); } @@ -257,6 +272,11 @@ void server_stop(char *endpoint) ((Server **)servers.ga_data)[servers.ga_len - 1]; } servers.ga_len--; + + // If v:servername is the stopped address, re-initialize it. + if (STRCMP(addr, get_vim_var_str(VV_SEND_SERVER)) == 0) { + set_vservername(&servers); + } } /// Returns an allocated array of server addresses. diff --git a/test/functional/server/server_spec.lua b/test/functional/server/server_spec.lua index 5dd8197d52..1cb3c879b9 100644 --- a/test/functional/server/server_spec.lua +++ b/test/functional/server/server_spec.lua @@ -4,19 +4,56 @@ local nvim, eq, neq, ok, eval = helpers.nvim, helpers.eq, helpers.neq, helpers.ok, helpers.eval local clear = helpers.clear -describe('server*() functions', function() +describe('serverstart(), serverstop()', function() before_each(clear) - it('set $NVIM_LISTEN_ADDRESS on first serverstart()', function() - -- Ensure the listen address is unset. + it('sets $NVIM_LISTEN_ADDRESS on first invocation', function() + -- Unset $NVIM_LISTEN_ADDRESS nvim('command', 'let $NVIM_LISTEN_ADDRESS = ""') - nvim('command', 'let s = serverstart()') - eq(1, eval('$NVIM_LISTEN_ADDRESS == s')) - nvim('command', 'call serverstop(s)') - eq(0, eval('$NVIM_LISTEN_ADDRESS == s')) + + local s = eval('serverstart()') + assert(s ~= nil and s:len() > 0, "serverstart() returned empty") + eq(s, eval('$NVIM_LISTEN_ADDRESS')) + nvim('command', "call serverstop('"..s.."')") + eq('', eval('$NVIM_LISTEN_ADDRESS')) + end) + + it([[sets v:servername _only_ on nvim startup + (unless all servers are stopped)]], function() + local initial_server = eval('v:servername') + assert(initial_server ~= nil and initial_server:len() > 0, + "v:servername was not initialized") + + -- v:servername is readonly so we cannot unset it--but we can test that it + -- does not get set again thereafter. + local s = eval('serverstart()') + assert(s ~= nil and s:len() > 0, "serverstart() returned empty") + neq(initial_server, s) + + -- serverstop() does _not_ modify v:servername... + nvim('command', "call serverstop('"..s.."')") + eq(initial_server, eval('v:servername')) + + -- ...unless we stop _all_ servers. + nvim('command', "call serverstop(serverlist()[0])") + eq('', eval('v:servername')) + + -- v:servername will take the next available server. + nvim('command', "call serverstart('test_server_socket')") + eq('test_server_socket', eval('v:servername')) end) - it('let the user retrieve the list of servers', function() + it('serverstop() ignores invalid input', function() + nvim('command', "call serverstop('')") + nvim('command', "call serverstop('bogus-socket-name')") + end) + +end) + +describe('serverlist()', function() + before_each(clear) + + it('returns the list of servers', function() -- There should already be at least one server. local n = eval('len(serverlist())') |