diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2022-05-03 15:08:35 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-05-03 06:08:35 -0700 |
commit | 4fb48c5654d9ffbffbdcdd80d0498b1ea3c8e68b (patch) | |
tree | 8a1222f6ed9a170ca2d151afcd911dfd01affdaf | |
parent | b5c15ba7e5266bdd742a835d82525d4625980d4d (diff) | |
download | rneovim-4fb48c5654d9ffbffbdcdd80d0498b1ea3c8e68b.tar.gz rneovim-4fb48c5654d9ffbffbdcdd80d0498b1ea3c8e68b.tar.bz2 rneovim-4fb48c5654d9ffbffbdcdd80d0498b1ea3c8e68b.zip |
feat(server): set $NVIM, unset $NVIM_LISTEN_ADDRESS #11009
PROBLEM
------------------------------------------------------------------------
$NVIM_LISTEN_ADDRESS has conflicting purposes as both a parameter ("the
current process should listen on this address") and a descriptor ("the
current process is a child of this address").
This contradiction means the presence of NVIM_LISTEN_ADDRESS is
ambiguous, so child Nvim always tries to listen on its _parent's_
socket. This is the cause of lots of "Failed to start server" spam in
our test/CI logs:
WARN 2022-04-30… server_start:154: Failed to start server: address already in use: \\.\pipe\nvim-4480-0
WARN 2022-04-30… server_start:154: Failed to start server: address already in use: \\.\pipe\nvim-2168-0
SOLUTION
------------------------------------------------------------------------
1. Set $NVIM to the parent v:servername, *only* in child processes.
- Now the correct way to detect a "parent" Nvim is to check for $NVIM.
2. Do NOT set $NVIM_LISTEN_ADDRESS in child processes.
3. On startup if $NVIM_LISTEN_ADDRESS exists, unset it immediately after
server init.
4. Open a channel to parent automatically, expose it as v:parent.
Fixes #3118
Fixes #6764
Fixes #9336
Ref https://github.com/neovim/neovim/pull/8247#issuecomment-380275696
Ref #8696
-rw-r--r-- | runtime/doc/api.txt | 2 | ||||
-rw-r--r-- | runtime/doc/builtin.txt | 188 | ||||
-rw-r--r-- | runtime/doc/deprecated.txt | 13 | ||||
-rw-r--r-- | runtime/doc/eval.txt | 12 | ||||
-rw-r--r-- | runtime/doc/nvim_terminal_emulator.txt | 18 | ||||
-rw-r--r-- | src/nvim/eval.h | 2 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 10 | ||||
-rw-r--r-- | src/nvim/eval/typval.c | 2 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/server.c | 49 | ||||
-rw-r--r-- | src/nvim/terminal.c | 12 | ||||
-rw-r--r-- | src/nvim/terminal.h | 2 | ||||
-rw-r--r-- | test/functional/core/job_spec.lua | 46 | ||||
-rw-r--r-- | test/functional/helpers.lua | 2 | ||||
-rw-r--r-- | test/functional/provider/nodejs_spec.lua | 4 | ||||
-rw-r--r-- | test/functional/provider/perl_spec.lua | 4 | ||||
-rw-r--r-- | test/functional/ui/screen.lua | 16 | ||||
-rw-r--r-- | test/functional/vimscript/server_spec.lua | 20 |
17 files changed, 157 insertions, 245 deletions
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index ed3a838b6d..ed13d7f8c2 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -59,7 +59,7 @@ Nvim instance: # trailing '&' which is required since Nvim won't process events while # running a blocking command): # - # :!./hello.rb & + # :!./hello.rb & # # Or from another shell by setting NVIM_LISTEN_ADDRESS: # $ NVIM_LISTEN_ADDRESS=[address] ./hello.rb diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 3e75914743..a909fd0d6b 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -158,7 +158,6 @@ foldclosedend({lnum}) Number last line of fold at {lnum} if closed foldlevel({lnum}) Number fold level at {lnum} foldtext() String line displayed for closed fold foldtextresult({lnum}) String text for closed fold at {lnum} -foreground() Number bring the Vim window to the foreground fullcommand({name}) String get full command from {name} funcref({name} [, {arglist}] [, {dict}]) Funcref reference to function {name} @@ -352,16 +351,6 @@ reg_recording() String get the recording register name reltime([{start} [, {end}]]) List get time value reltimefloat({time}) Float turn the time value into a Float reltimestr({time}) String turn time value into a String -remote_expr({server}, {string} [, {idvar} [, {timeout}]]) - String send expression -remote_foreground({server}) Number bring Vim server to the foreground -remote_peek({serverid} [, {retvar}]) - Number check for reply string -remote_read({serverid} [, {timeout}]) - String read reply string -remote_send({server}, {string} [, {idvar}]) - String send key sequence -remote_startserver({name}) none become server {name} remove({list}, {idx} [, {end}]) any/List remove items {idx}-{end} from {list} remove({blob}, {idx} [, {end}]) Number/Blob @@ -395,8 +384,6 @@ searchpairpos({start}, {middle}, {end} [, {flags} [, {skip} [...]]]) List search for other end of start/end pair searchpos({pattern} [, {flags} [, {stopline} [, {timeout} [, {skip}]]]]) List search for {pattern} -server2client({clientid}, {string}) - Number send reply string serverlist() String get a list of available servers setbufline({expr}, {lnum}, {text}) Number set line {lnum} to {text} in buffer @@ -1974,7 +1961,7 @@ expand({string} [, {nosuf} [, {list}]]) *expand()* <cword> word under the cursor <cWORD> WORD under the cursor <client> the {clientid} of the last received - message |server2client()| + message Modifiers: :p expand to full path :h head (last path component removed) @@ -2409,14 +2396,6 @@ foldtextresult({lnum}) *foldtextresult()* Can also be used as a |method|: > GetLnum()->foldtextresult() < - *foreground()* -foreground() Move the Vim window to the foreground. Useful when sent from - a client to a Vim server. |remote_send()| - On Win32 systems this might not work, the OS does not always - allow a window to bring itself to the foreground. Use - |remote_foreground()| instead. - {only in the Win32 GUI and console version} - fullcommand({name}) *fullcommand()* Get the full command name from a short abbreviated command name; see |20.2| for details on command abbreviations. @@ -4280,6 +4259,15 @@ jobstart({cmd} [, {opts}]) *jobstart()* by CommandLineToArgvW https://msdn.microsoft.com/bb776391 unless cmd[0] is some form of "cmd.exe". + *jobstart-env* + The job environment is initialized as follows: + $NVIM is set to |v:servername| of the parent Nvim + $NVIM_LISTEN_ADDRESS is unset + $NVIM_LOG_FILE is unset + $VIM is unset + $VIMRUNTIME is unset + You can set these with the `env` option. + *jobstart-options* {opts} is a dictionary with these keys: clear_env: (boolean) `env` defines the job environment @@ -4290,8 +4278,8 @@ jobstart({cmd} [, {opts}]) *jobstart()* killed when Nvim exits. If the process exits before Nvim, `on_exit` will be invoked. env: (dict) Map of environment variable name:value - pairs extending (or replacing if |clear_env|) - the current environment. + pairs extending (or replacing with |clear_env|) + the current environment. |jobstart-env| height: (number) Height of the `pty` terminal. |on_exit|: (function) Callback invoked when the job exits. |on_stdout|: (function) Callback invoked when the job emits @@ -4305,8 +4293,8 @@ jobstart({cmd} [, {opts}]) *jobstart()* platforms, this option is silently ignored.) pty: (boolean) Connect the job to a new pseudo terminal, and its streams to the master file - descriptor. Then `on_stderr` is ignored, - `on_stdout` receives all output. + descriptor. `on_stdout` receives all output, + `on_stderr` is ignored. |terminal-start| rpc: (boolean) Use |msgpack-rpc| to communicate with the job over stdio. Then `on_stdout` is ignored, but `on_stderr` can still be used. @@ -5242,9 +5230,8 @@ mode([expr]) Return a string that indicates the current mode. ! Shell or external command is executing t Terminal mode: keys go to the job - This is useful in the 'statusline' option or when used - with |remote_expr()| In most other places it always returns - "c" or "n". + This is useful in the 'statusline' option or RPC calls. In + most other places it always returns "c" or "n". Note that in the future more modes and more specific modes may be added. It's better not to compare the whole string but only the leading character(s). @@ -5964,107 +5951,6 @@ reltimestr({time}) *reltimestr()* Can also be used as a |method|: > reltime(start)->reltimestr() < - *remote_expr()* *E449* -remote_expr({server}, {string} [, {idvar} [, {timeout}]]) - Send the {string} to {server}. The {server} argument is a - string, also see |{server}|. - - The string is sent as an expression and the result is returned - after evaluation. The result must be a String or a |List|. A - |List| is turned into a String by joining the items with a - line break in between (not at the end), like with join(expr, - "\n"). - - If {idvar} is present and not empty, it is taken as the name - of a variable and a {serverid} for later use with - |remote_read()| is stored there. - - If {timeout} is given the read times out after this many - seconds. Otherwise a timeout of 600 seconds is used. - - See also |clientserver| |RemoteReply|. - This function is not available in the |sandbox|. - Note: Any errors will cause a local error message to be issued - and the result will be the empty string. - - Variables will be evaluated in the global namespace, - independent of a function currently being active. Except - when in debug mode, then local function variables and - arguments can be evaluated. - - Examples: > - :echo remote_expr("gvim", "2+2") - :echo remote_expr("gvim1", "b:current_syntax") -< - -remote_foreground({server}) *remote_foreground()* - Move the Vim server with the name {server} to the foreground. - The {server} argument is a string, also see |{server}|. - This works like: > - remote_expr({server}, "foreground()") -< Except that on Win32 systems the client does the work, to work - around the problem that the OS doesn't always allow the server - to bring itself to the foreground. - Note: This does not restore the window if it was minimized, - like foreground() does. - This function is not available in the |sandbox|. - {only in the Win32 GUI and the Win32 console version} - - -remote_peek({serverid} [, {retvar}]) *remote_peek()* - Returns a positive number if there are available strings - from {serverid}. Copies any reply string into the variable - {retvar} if specified. {retvar} must be a string with the - name of a variable. - Returns zero if none are available. - Returns -1 if something is wrong. - See also |clientserver|. - This function is not available in the |sandbox|. - Examples: > - :let repl = "" - :echo "PEEK: " .. remote_peek(id, "repl") .. ": " .. repl - -remote_read({serverid}, [{timeout}]) *remote_read()* - Return the oldest available reply from {serverid} and consume - it. Unless a {timeout} in seconds is given, it blocks until a - reply is available. - See also |clientserver|. - This function is not available in the |sandbox|. - Example: > - :echo remote_read(id) -< - *remote_send()* *E241* -remote_send({server}, {string} [, {idvar}]) - Send the {string} to {server}. The {server} argument is a - string, also see |{server}|. - - The string is sent as input keys and the function returns - immediately. At the Vim server the keys are not mapped - |:map|. - - If {idvar} is present, it is taken as the name of a variable - and a {serverid} for later use with remote_read() is stored - there. - - See also |clientserver| |RemoteReply|. - This function is not available in the |sandbox|. - - Note: Any errors will be reported in the server and may mess - up the display. - Examples: > - :echo remote_send("gvim", ":DropAndReply " .. file, "serverid") .. - \ remote_read(serverid) - - :autocmd NONE RemoteReply * - \ echo remote_read(expand("<amatch>")) - :echo remote_send("gvim", ":sleep 10 | echo " .. - \ 'server2client(expand("<client>"), "HELLO")<CR>') -< - *remote_startserver()* *E941* *E942* -remote_startserver({name}) - Become the server {name}. This fails if already running as a - server, when |v:servername| is not empty. - remove({list}, {idx} [, {end}]) *remove()* Without {end}: Remove the item at {idx} from |List| {list} and return the item. @@ -6648,21 +6534,6 @@ searchpos({pattern} [, {flags} [, {stopline} [, {timeout} [, {skip}]]]]) Can also be used as a |method|: > GetPattern()->searchpos() -server2client({clientid}, {string}) *server2client()* - Send a reply string to {clientid}. The most recent {clientid} - that sent a string can be retrieved with expand("<client>"). - Note: - Returns zero for success, -1 for failure. - This id has to be stored before the next command can be - received. I.e. before returning from the received command and - before calling any commands that waits for input. - See also |clientserver|. - Example: > - :echo server2client(expand("<client>"), "HELLO") - -< Can also be used as a |method|: > - GetClientId()->server2client(string) -< serverlist() *serverlist()* Returns a list of server addresses, or empty if all servers were stopped. |serverstart()| |serverstop()| @@ -6672,7 +6543,9 @@ serverlist() *serverlist()* serverstart([{address}]) *serverstart()* Opens a socket or named pipe at {address} and listens for |RPC| messages. Clients can send |API| commands to the address - to control Nvim. Returns the address string. + to control Nvim. + + Returns the address string. If {address} does not contain a colon ":" it is interpreted as a named pipe or Unix domain socket path. @@ -6694,14 +6567,11 @@ serverstart([{address}]) *serverstart()* If no address is given, it is equivalent to: > :call serverstart(tempname()) -< |$NVIM_LISTEN_ADDRESS| is set to {address} if not already set. - serverstop({address}) *serverstop()* Closes the pipe or socket at {address}. Returns TRUE if {address} is valid, else FALSE. - 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()|. + address in |serverlist()|. setbufline({buf}, {lnum}, {text}) *setbufline()* Set line {lnum} to {text} in buffer {buf}. This works like @@ -8281,19 +8151,15 @@ tempname() *tempname()* *temp-file-name* termopen({cmd} [, {opts}]) *termopen()* Spawns {cmd} in a new pseudo-terminal session connected - to the current buffer. {cmd} is the same as the one passed to - |jobstart()|. This function fails if the current buffer is - modified (all buffer contents are destroyed). - - The {opts} dict is similar to the one passed to |jobstart()|, - but the `pty`, `width`, `height`, and `TERM` fields are - ignored: `height`/`width` are taken from the current window - and `$TERM` is set to "xterm-256color". + to the current (unmodified) buffer. Parameters and behavior + are the same as |jobstart()| except "pty", "width", "height", + and "TERM" are ignored: "height" and "width" are taken from + the current window. Returns the same values as |jobstart()|. - See |terminal| for more information. - -test_ functions are documented here: |test-functions-details| + Terminal environment is initialized as in ||jobstart-env|, + except $TERM is set to "xterm-256color". Full behavior is + described in |terminal|. tan({expr}) *tan()* Return the tangent of {expr}, measured in radians, as a |Float| diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt index f24bf06feb..13644cf208 100644 --- a/runtime/doc/deprecated.txt +++ b/runtime/doc/deprecated.txt @@ -6,9 +6,8 @@ Nvim *deprecated* -The items listed below are "deprecated". This means they will be removed in -the future. They should not be used in new scripts, and old scripts should be -updated. +The items listed below are deprecated: they will be removed in the future. +They should not be used in new scripts, and old scripts should be updated. ============================================================================== @@ -25,8 +24,12 @@ Commands ~ *: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. +*$NVIM_LISTEN_ADDRESS* $NVIM_LISTEN_ADDRESS is a deprecated way to set the + |--listen| address of Nvim, and also had a conflicting + purpose as a way to detect a parent Nvim (use |$NVIM| + for that). It is unset by |terminal| and |jobstart()| + (unless explicitly given by the "env" option). + Ignored if --listen is given. Events ~ *BufCreate* Use |BufAdd| instead. diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index c6319c717a..3fd0d96f21 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2138,9 +2138,19 @@ v:scrollstart String describing the script or function that caused the *v:servername* *servername-variable* v:servername Primary listen-address of the current Nvim instance, the first item returned by |serverlist()|. Can be set by |--listen| or - |$NVIM_LISTEN_ADDRESS| at startup. |serverstart()| |serverstop()| + |$NVIM_LISTEN_ADDRESS| (deprecated) at startup. + See also |serverstart()| |serverstop()|. Read-only. + *$NVIM* + $NVIM is set by |terminal| and |jobstart()|, and is thus + a hint that the current environment is a subprocess of Nvim. + Example: > + if $NVIM + echo nvim_get_chan_info(v:parent) + endif + +< Note the contents of $NVIM may change in the future. v:searchforward *v:searchforward* *searchforward-variable* Search direction: 1 after a forward search, 0 after a diff --git a/runtime/doc/nvim_terminal_emulator.txt b/runtime/doc/nvim_terminal_emulator.txt index 0e2b048541..49e29111c6 100644 --- a/runtime/doc/nvim_terminal_emulator.txt +++ b/runtime/doc/nvim_terminal_emulator.txt @@ -25,23 +25,23 @@ Start *terminal-start* There are several ways to create a terminal buffer: -- Invoke the |:terminal| command. -- Call the |termopen()| function. -- Edit a file with a name matching `term://(.{-}//(\d+:)?)?\zs.*`. - For example: -> +- Run the |:terminal| command. +- Call the |nvim_open_term()| or |termopen()| function. +- Edit a "term://" buffer. Examples: > :edit term://bash :vsplit term://top -< - Note: The "term://" pattern is handled by a BufReadCmd handler, so the - |autocmd-nested| modifier is required to use it in an autocmd. > + +< Note: To open a "term://" buffer from an autocmd, the |autocmd-nested| + modifier is required. > autocmd VimEnter * ++nested split term://sh -< This is only mentioned for reference; use |:terminal| instead. +< (This is only mentioned for reference; use |:terminal| instead.) When the terminal starts, the buffer contents are updated and the buffer is named in the form of `term://{cwd}//{pid}:{cmd}`. This naming scheme is used by |:mksession| to restore a terminal buffer (by restarting the {cmd}). +The terminal environment is initialized as in |jobstart-env|. + ============================================================================== Input *terminal-input* diff --git a/src/nvim/eval.h b/src/nvim/eval.h index 53f19b8736..c81b31aba9 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -164,7 +164,7 @@ typedef enum { VV_ARGV, VV_COLLATE, VV_EXITING, - // Neovim + // Nvim VV_STDERR, VV_MSGPACK_TYPES, VV__NULL_STRING, // String with NULL value. For test purposes only. diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 88059ff21e..0a71526246 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -5088,6 +5088,16 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en tv_dict_add_str(env, S_LEN("TERM"), pty_term_name); } + // Set $NVIM (in the child process) to v:servername. #3118 + char *nvim_addr = (char *)get_vim_var_str(VV_SEND_SERVER); + if (nvim_addr[0] != '\0') { + dictitem_T *dv = tv_dict_find(env, S_LEN("NVIM")); + if (dv) { + tv_dict_item_remove(env, dv); + } + tv_dict_add_str(env, S_LEN("NVIM"), nvim_addr); + } + if (job_env) { #ifdef WIN32 TV_DICT_ITER(job_env->di_tv.vval.v_dict, var, { diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 2a432ecb47..ff52962913 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1588,8 +1588,6 @@ varnumber_T tv_dict_get_number(const dict_T *const d, const char *const key) } /// Converts a dict to an environment -/// -/// char **tv_dict_to_env(dict_T *denv) { size_t env_size = (size_t)tv_dict_len(denv); diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c index e954e4b3a3..f15ce82917 100644 --- a/src/nvim/msgpack_rpc/server.c +++ b/src/nvim/msgpack_rpc/server.c @@ -22,7 +22,8 @@ #include "nvim/vim.h" #define MAX_CONNECTIONS 32 -#define LISTEN_ADDRESS_ENV_VAR "NVIM_LISTEN_ADDRESS" +#define ENV_LISTEN "NVIM_LISTEN_ADDRESS" // deprecated +#define ENV_NVIM "NVIM" static garray_T watchers = GA_EMPTY_INIT_VALUE; @@ -35,20 +36,24 @@ bool server_init(const char *listen_addr) { ga_init(&watchers, sizeof(SocketWatcher *), 1); - // $NVIM_LISTEN_ADDRESS - const char *env_addr = os_getenv(LISTEN_ADDRESS_ENV_VAR); - int rv = listen_addr == NULL ? 1 : server_start(listen_addr); + // $NVIM_LISTEN_ADDRESS (deprecated) + if (!listen_addr && os_env_exists(ENV_LISTEN)) { + listen_addr = os_getenv(ENV_LISTEN); + } + int rv = listen_addr ? server_start(listen_addr) : 1; 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); + listen_addr = server_address_new(); + if (!listen_addr) { + return false; } + rv = server_start(listen_addr); + xfree((char *)listen_addr); + } + + if (os_env_exists(ENV_LISTEN)) { + // Unset $NVIM_LISTEN_ADDRESS, it's a liability hereafter. + os_unsetenv(ENV_LISTEN); } return rv == 0; @@ -60,8 +65,8 @@ static void close_socket_watcher(SocketWatcher **watcher) socket_watcher_close(*watcher, free_server); } -/// Set v:servername to the first server in the server list, or unset it if no -/// servers are known. +/// Sets the "primary address" (v:servername and $NVIM) to the first server in +/// the server list, or unsets if no servers are known. static void set_vservername(garray_T *srvs) { char *default_server = (srvs->ga_len > 0) @@ -156,12 +161,6 @@ int server_start(const char *endpoint) return result; } - // Update $NVIM_LISTEN_ADDRESS, if not set. - const char *listen_address = os_getenv(LISTEN_ADDRESS_ENV_VAR); - if (listen_address == NULL) { - os_setenv(LISTEN_ADDRESS_ENV_VAR, watcher->addr, 1); - } - // Add the watcher to the list. ga_grow(&watchers, 1); ((SocketWatcher **)watchers.ga_data)[watchers.ga_len++] = watcher; @@ -200,12 +199,6 @@ bool server_stop(char *endpoint) return false; } - // 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) { - os_unsetenv(LISTEN_ADDRESS_ENV_VAR); - } - socket_watcher_close(watcher, free_server); // Remove this server from the list by swapping it with the last item. @@ -215,8 +208,8 @@ bool server_stop(char *endpoint) } watchers.ga_len--; - // If v:servername is the stopped address, re-initialize it. - if (STRCMP(addr, get_vim_var_str(VV_SEND_SERVER)) == 0) { + // Bump v:servername to the next available server, if any. + if (strequal(addr, (char *)get_vim_var_str(VV_SEND_SERVER))) { set_vservername(&watchers); } diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index b07c3786c3..225aa0100e 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -179,6 +179,13 @@ static void term_output_callback(const char *s, size_t len, void *user_data) // public API {{{ +/// Initializes terminal properties, and triggers TermOpen. +/// +/// The PTY process (TerminalOptions.data) was already started by termopen(), +/// via ex_terminal() or the term:// BufReadCmd. +/// +/// @param buf Buffer used for presentation of the terminal. +/// @param opts PTY process channel, various terminal properties and callbacks. Terminal *terminal_open(buf_T *buf, TerminalOptions opts) { // Create a new terminal instance and configure it @@ -374,6 +381,7 @@ void terminal_check_size(Terminal *term) invalidate_terminal(term, -1, -1); } +/// Implements TERM_FOCUS mode. :help Terminal-mode void terminal_enter(void) { buf_T *buf = curbuf; @@ -502,6 +510,7 @@ static int terminal_check(VimState *state) return 1; } +/// Processes one char of terminal-mode input. static int terminal_execute(VimState *state, int key) { TerminalState *s = (TerminalState *)state; @@ -1448,7 +1457,8 @@ static void refresh_terminal(Terminal *term) long ml_added = buf->b_ml.ml_line_count - ml_before; adjust_topline(term, buf, ml_added); } -// Calls refresh_terminal() on all invalidated_terminals. + +/// Calls refresh_terminal() on all invalidated_terminals. static void refresh_timer_cb(TimeWatcher *watcher, void *data) { refresh_pending = false; diff --git a/src/nvim/terminal.h b/src/nvim/terminal.h index 001adbadc3..a83929e224 100644 --- a/src/nvim/terminal.h +++ b/src/nvim/terminal.h @@ -13,7 +13,7 @@ typedef void (*terminal_close_cb)(void *data); #include "nvim/buffer_defs.h" typedef struct { - void *data; + void *data; // PTY process channel uint16_t width, height; terminal_write_cb write_cb; terminal_resize_cb resize_cb; diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index 94a50b9a41..cf24e570cb 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -16,6 +16,7 @@ local poke_eventloop = helpers.poke_eventloop local iswin = helpers.iswin local get_pathsep = helpers.get_pathsep local pathroot = helpers.pathroot +local exec_lua = helpers.exec_lua local nvim_set = helpers.nvim_set local expect_twostreams = helpers.expect_twostreams local expect_msg_seq = helpers.expect_msg_seq @@ -208,7 +209,7 @@ describe('jobs', function() ok(string.find(err, "E475: Invalid argument: expected valid directory$") ~= nil) end) - it('produces error when using non-executable `cwd`', function() + it('error on non-executable `cwd`', function() if iswin() then return end -- N/A for Windows local dir = 'Xtest_not_executable_dir' @@ -249,7 +250,7 @@ describe('jobs', function() eq({'notification', 'exit', {0, 0}}, next_msg()) end) - it('allows interactive commands', function() + it('interactive commands', function() nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)") neq(0, eval('j')) nvim('command', 'call jobsend(j, "abc\\n")') @@ -295,7 +296,7 @@ describe('jobs', function() nvim('command', "call jobstop(j)") end) - it("will not buffer data if it doesn't end in newlines", function() + it("emits partial lines (does NOT buffer data lacking newlines)", function() nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)") nvim('command', 'call jobsend(j, "abc\\nxyz")') eq({'notification', 'stdout', {0, {'abc', 'xyz'}}}, next_msg()) @@ -378,7 +379,7 @@ describe('jobs', function() eq(NIL, meths.get_proc(pid)) end) - it("do not survive the exit of nvim", function() + it("disposed on Nvim exit", function() -- use sleep, which doesn't die on stdin close nvim('command', "let g:j = jobstart(has('win32') ? ['ping', '-n', '1001', '127.0.0.1'] : ['sleep', '1000'], g:job_opts)") local pid = eval('jobpid(g:j)') @@ -646,6 +647,43 @@ describe('jobs', function() ) end) + it('jobstart() environment: $NVIM, $NVIM_LISTEN_ADDRESS #11009', function() + local function get_env_in_child_job(envname, env) + return exec_lua([[ + local envname, env = ... + local join = function(s) return vim.fn.join(s, '') end + local stdout = {} + local stderr = {} + local opt = { + env = env, + stdout_buffered = true, + stderr_buffered = true, + on_stderr = function(chan, data, name) stderr = data end, + on_stdout = function(chan, data, name) stdout = data end, + } + local j1 = vim.fn.jobstart({ vim.v.progpath, '-es', '-V1',( '+echo "%s="..getenv("%s")'):format(envname, envname), '+qa!' }, opt) + vim.fn.jobwait({ j1 }, 10000) + return join({ join(stdout), join(stderr) }) + ]], + envname, + env) + end + + local addr = eval('v:servername') + ok((addr):len() > 0) + -- $NVIM is _not_ defined in the top-level Nvim process. + eq('', eval('$NVIM')) + -- jobstart() shares its v:servername with the child via $NVIM. + eq('NVIM='..addr, get_env_in_child_job('NVIM')) + -- $NVIM_LISTEN_ADDRESS is unset by server_init in the child. + eq('NVIM_LISTEN_ADDRESS=null', get_env_in_child_job('NVIM_LISTEN_ADDRESS')) + eq('NVIM_LISTEN_ADDRESS=null', get_env_in_child_job('NVIM_LISTEN_ADDRESS', + { NVIM_LISTEN_ADDRESS='Xtest_jobstart_env' })) + -- User can explicitly set $NVIM_LOG_FILE, $VIM, $VIMRUNTIME. + eq('NVIM_LOG_FILE=Xtest_jobstart_env', + get_env_in_child_job('NVIM_LOG_FILE', { NVIM_LOG_FILE='Xtest_jobstart_env' })) + end) + describe('jobwait', function() before_each(function() if iswin() then diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index b0b2dac9fd..e9c3d4bd92 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -782,7 +782,7 @@ function module.pathroot() return iswin() and (module.nvim_dir:sub(1,2)..pathsep) or '/' end --- Returns a valid, platform-independent $NVIM_LISTEN_ADDRESS. +-- Returns a valid, platform-independent Nvim listen address. -- Useful for communicating with child instances. function module.new_pipename() -- HACK: Start a server temporarily, get the name, then stop it. diff --git a/test/functional/provider/nodejs_spec.lua b/test/functional/provider/nodejs_spec.lua index 661a6f4f94..187f1c0412 100644 --- a/test/functional/provider/nodejs_spec.lua +++ b/test/functional/provider/nodejs_spec.lua @@ -29,7 +29,7 @@ describe('nodejs host', function() local fname = 'Xtest-nodejs-hello.js' write_file(fname, [[ const neovim = require('neovim'); - const nvim = neovim.attach({socket: process.env.NVIM_LISTEN_ADDRESS}); + const nvim = neovim.attach({socket: process.env.NVIM}); nvim.command('let g:job_out = "hello"'); ]]) command('let g:job_id = jobstart(["node", "'..fname..'"])') @@ -39,7 +39,7 @@ describe('nodejs host', function() local fname = 'Xtest-nodejs-hello-plugin.js' write_file(fname, [[ const neovim = require('neovim'); - const nvim = neovim.attach({socket: process.env.NVIM_LISTEN_ADDRESS}); + const nvim = neovim.attach({socket: process.env.NVIM}); class TestPlugin { hello() { diff --git a/test/functional/provider/perl_spec.lua b/test/functional/provider/perl_spec.lua index 125674660b..aff5e36e24 100644 --- a/test/functional/provider/perl_spec.lua +++ b/test/functional/provider/perl_spec.lua @@ -83,7 +83,7 @@ describe('perl provider', function() use Neovim::Ext; use Neovim::Ext::MsgPack::RPC; - my $session = Neovim::Ext::MsgPack::RPC::socket_session($ENV{NVIM_LISTEN_ADDRESS}); + my $session = Neovim::Ext::MsgPack::RPC::socket_session($ENV{NVIM}); my $nvim = Neovim::Ext::from_session($session); $nvim->command('let g:job_out = "hello"'); 1; @@ -116,7 +116,7 @@ describe('perl provider', function() use Neovim::Ext; use Neovim::Ext::MsgPack::RPC; - my $session = Neovim::Ext::MsgPack::RPC::socket_session($ENV{NVIM_LISTEN_ADDRESS}); + my $session = Neovim::Ext::MsgPack::RPC::socket_session($ENV{NVIM}); my $nvim = Neovim::Ext::from_session($session); my $plugin = TestPlugin->new($nvim); $plugin->test_command(); diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index e8a39ab6f8..80dba70b33 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -75,7 +75,7 @@ local busted = require('busted') local deepcopy = helpers.deepcopy local shallowcopy = helpers.shallowcopy local concat_tables = helpers.concat_tables -local request, run_session = helpers.request, helpers.run_session +local run_session = helpers.run_session local eq = helpers.eq local dedent = helpers.dedent local get_session = helpers.get_session @@ -90,8 +90,6 @@ end local Screen = {} Screen.__index = Screen -local debug_screen - local default_timeout_factor = 1 if os.getenv('VALGRIND') then default_timeout_factor = default_timeout_factor * 3 @@ -123,18 +121,6 @@ do Screen.colornames = colornames end -function Screen.debug(command) - if not command then - command = 'pynvim -n -c ' - end - command = command .. request('vim_eval', '$NVIM_LISTEN_ADDRESS') - if debug_screen then - debug_screen:close() - end - debug_screen = io.popen(command, 'r') - debug_screen:read() -end - function Screen.new(width, height) if not width then width = 53 diff --git a/test/functional/vimscript/server_spec.lua b/test/functional/vimscript/server_spec.lua index 238d1aeb0f..de64a77b4d 100644 --- a/test/functional/vimscript/server_spec.lua +++ b/test/functional/vimscript/server_spec.lua @@ -1,6 +1,5 @@ local helpers = require('test.functional.helpers')(after_each) local eq, neq, eval = helpers.eq, helpers.neq, helpers.eval -local command = helpers.command local clear, funcs, meths = helpers.clear, helpers.funcs, helpers.meths local iswin = helpers.iswin local ok = helpers.ok @@ -16,27 +15,25 @@ end describe('server', function() before_each(clear) - it('serverstart() sets $NVIM_LISTEN_ADDRESS on first invocation', function() - -- Unset $NVIM_LISTEN_ADDRESS - command('let $NVIM_LISTEN_ADDRESS = ""') - + it('serverstart(), serverstop() does not set $NVIM', function() local s = eval('serverstart()') assert(s ~= nil and s:len() > 0, "serverstart() returned empty") - eq(s, eval('$NVIM_LISTEN_ADDRESS')) + eq('', eval('$NVIM')) + eq('', eval('$NVIM_LISTEN_ADDRESS')) eq(1, eval("serverstop('"..s.."')")) eq('', eval('$NVIM_LISTEN_ADDRESS')) end) it('sets new v:servername if $NVIM_LISTEN_ADDRESS is invalid', function() clear({env={NVIM_LISTEN_ADDRESS='.'}}) - eq('.', eval('$NVIM_LISTEN_ADDRESS')) + -- Cleared on startup. + 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() + it('sets v:servername at startup or if all servers were stopped', function() local initial_server = meths.get_vvar('servername') assert(initial_server ~= nil and initial_server:len() > 0, 'v:servername was not initialized') @@ -55,11 +52,13 @@ describe('server', function() eq(1, funcs.serverstop(funcs.serverlist()[1])) eq('', meths.get_vvar('servername')) - -- v:servername will take the next available server. + -- v:servername and $NVIM take the next available server. local servername = (iswin() and [[\\.\pipe\Xtest-functional-server-pipe]] or 'Xtest-functional-server-socket') funcs.serverstart(servername) eq(servername, meths.get_vvar('servername')) + -- Not set in the current process, only in children. + eq('', eval('$NVIM')) end) it('serverstop() returns false for invalid input', function() @@ -136,7 +135,6 @@ 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)) |