From 4c346f677e14a996460ad47254223e846bfd479a Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 22 Nov 2024 09:20:51 +0100 Subject: [PATCH 1/4] =?UTF-8?q?feat(jobs):=20jobstart(=E2=80=A6,{term:true?= =?UTF-8?q?}),=20deprecate=20termopen()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: `termopen` has long been a superficial wrapper around `jobstart`, and has no real purpose. Also, `vim.system` and `nvim_open_term` presumably will replace all features of `jobstart` and `termopen`, so centralizing the logic will help with that. Solution: - Introduce "term" flag of `jobstart`. - Deprecate `termopen`. --- contrib/gdb/neovim_gdb.vim | 6 +- runtime/doc/api.txt | 4 +- runtime/doc/builtin.txt | 28 +- runtime/doc/channel.txt | 4 +- runtime/doc/deprecated.txt | 305 +++++++++--------- runtime/doc/terminal.txt | 2 +- runtime/lua/vim/_defaults.lua | 2 +- runtime/lua/vim/_meta/api.lua | 4 +- runtime/lua/vim/_meta/vimfn.lua | 21 +- .../dist/opt/termdebug/plugin/termdebug.vim | 8 +- src/nvim/api/buffer.c | 4 +- src/nvim/eval.c | 2 +- src/nvim/eval.lua | 21 +- src/nvim/eval/deprecated.c | 45 +++ src/nvim/eval/deprecated.h | 11 + src/nvim/eval/funcs.c | 222 +++++-------- src/nvim/ex_docmd.c | 4 +- src/nvim/generators/gen_eval.lua | 1 + src/nvim/terminal.c | 2 +- test/functional/api/buffer_updates_spec.lua | 3 +- test/functional/api/vim_spec.lua | 3 +- test/functional/core/job_spec.lua | 7 +- test/functional/core/main_spec.lua | 3 +- test/functional/core/startup_spec.lua | 29 +- test/functional/editor/mode_normal_spec.lua | 8 +- .../swapfile_preserve_recover_spec.lua | 12 +- test/functional/legacy/window_cmd_spec.lua | 2 +- test/functional/terminal/buffer_spec.lua | 10 +- test/functional/terminal/channel_spec.lua | 14 +- test/functional/terminal/clipboard_spec.lua | 2 +- test/functional/terminal/cursor_spec.lua | 2 +- test/functional/terminal/ex_terminal_spec.lua | 4 +- test/functional/terminal/highlight_spec.lua | 11 +- test/functional/terminal/scrollback_spec.lua | 6 +- test/functional/terminal/tui_spec.lua | 6 +- test/functional/testterm.lua | 2 +- test/functional/ui/hlstate_spec.lua | 2 +- test/functional/ui/messages_spec.lua | 2 +- test/functional/ui/title_spec.lua | 2 +- test/old/testdir/runnvim.vim | 3 +- 40 files changed, 408 insertions(+), 421 deletions(-) create mode 100644 src/nvim/eval/deprecated.c create mode 100644 src/nvim/eval/deprecated.h diff --git a/contrib/gdb/neovim_gdb.vim b/contrib/gdb/neovim_gdb.vim index d61e7bc0cc..e0be6cb803 100644 --- a/contrib/gdb/neovim_gdb.vim +++ b/contrib/gdb/neovim_gdb.vim @@ -167,13 +167,15 @@ function! s:Spawn(server_cmd, client_cmd, server_addr, reconnect) if type(a:server_cmd) == type('') " spawn gdbserver in a vertical split let server = s:GdbServer.new(gdb) - vsp | enew | let gdb._server_id = termopen(a:server_cmd, server) + server.term = v:true + vsp | enew | let gdb._server_id = jobstart(a:server_cmd, server) let gdb._jump_window = 2 let gdb._server_buf = bufnr('%') endif " go to the bottom window and spawn gdb client wincmd j - enew | let gdb._client_id = termopen(a:client_cmd, gdb) + gdb.term = v:true + enew | let gdb._client_id = jobstart(a:client_cmd, gdb) let gdb._client_buf = bufnr('%') tnoremap :GdbContinuei tnoremap :GdbNexti diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 70fda5ce8a..5922e6d02f 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -2170,12 +2170,12 @@ nvim_buf_call({buffer}, {fun}) *nvim_buf_call()* This temporarily switches current buffer to "buffer". If the current window already shows "buffer", the window is not switched. If a window inside the current tabpage (including a float) already shows the buffer, - then one of these windows will be set as current window temporarily. + then one of those windows will be set as current window temporarily. Otherwise a temporary scratch window (called the "autocmd window" for historical reasons) will be used. This is useful e.g. to call Vimscript functions that only work with the - current buffer/window currently, like |termopen()|. + current buffer/window currently, like `jobstart(…, {'term': v:true})`. Attributes: ~ Lua |vim.api| only diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 5f74080e0c..c836c19a80 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -5309,7 +5309,7 @@ jobresize({job}, {width}, {height}) *jobresize()* (`any`) jobstart({cmd} [, {opts}]) *jobstart()* - Note: Prefer |vim.system()| in Lua (unless using the `pty` option). + Note: Prefer |vim.system()| in Lua (unless using `rpc`, `pty`, or `term`). Spawns {cmd} as a job. If {cmd} is a List it runs directly (no 'shell'). @@ -5383,6 +5383,10 @@ jobstart({cmd} [, {opts}]) *jobstart()* stdin: (string) Either "pipe" (default) to connect the job's stdin to a channel or "null" to disconnect stdin. + term: (boolean) Spawns {cmd} in a new pseudo-terminal session + connected to the current (unmodified) buffer. Implies "pty". + Default "height" and "width" are set to the current window + dimensions. |jobstart()|. Defaults $TERM to "xterm-256color". width: (number) Width of the `pty` terminal. {opts} is passed as |self| dictionary to the callback; the @@ -11168,28 +11172,6 @@ tempname() *tempname()* Return: ~ (`string`) -termopen({cmd} [, {opts}]) *termopen()* - Spawns {cmd} in a new pseudo-terminal session connected - 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. Note that termopen() implies a "pty" arg - to jobstart(), and thus has the implications documented at - |jobstart()|. - - Returns the same values as jobstart(). - - Terminal environment is initialized as in |jobstart-env|, - except $TERM is set to "xterm-256color". Full behavior is - described in |terminal|. - - Parameters: ~ - • {cmd} (`string|string[]`) - • {opts} (`table?`) - - Return: ~ - (`integer`) - test_garbagecollect_now() *test_garbagecollect_now()* Like |garbagecollect()|, but executed right away. This must only be called directly to avoid any structure to exist diff --git a/runtime/doc/channel.txt b/runtime/doc/channel.txt index 7184151cda..dc9b0735ea 100644 --- a/runtime/doc/channel.txt +++ b/runtime/doc/channel.txt @@ -20,8 +20,8 @@ There are several ways to open a channel: 2. Through stdin, stdout and stderr of a process spawned by |jobstart()|. - 3. Through the PTY master end of a PTY opened with - `jobstart(..., {'pty': v:true})` or |termopen()|. + 3. Through the PTY master end of a PTY opened with |nvim_open_term()| or + `jobstart(…, {'pty': v:true})`. 4. By connecting to a TCP/IP socket or named pipe with |sockconnect()|. diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt index 592b6389c4..ec98cc844f 100644 --- a/runtime/doc/deprecated.txt +++ b/runtime/doc/deprecated.txt @@ -16,49 +16,34 @@ Deprecated features DEPRECATED IN 0.11 *deprecated-0.11* API -- nvim_subscribe() Plugins must maintain their own "multicast" channels list. -- nvim_unsubscribe() Plugins must maintain their own "multicast" channels list. - -LUA -- vim.region() Use |getregionpos()| instead. -- *vim.highlight* Renamed to |vim.hl|. -- vim.validate(opts: table) Use form 1. See |vim.validate()|. +• nvim_subscribe() Plugins must maintain their own "multicast" channels list. +• nvim_unsubscribe() Plugins must maintain their own "multicast" channels list. DIAGNOSTICS -- *vim.diagnostic.goto_next()* Use |vim.diagnostic.jump()| with `{count=1, float=true}` instead. -- *vim.diagnostic.goto_prev()* Use |vim.diagnostic.jump()| with `{count=-1, float=true}` instead. -- *vim.diagnostic.get_next_pos()* - Use the "lnum" and "col" fields from the return value of - |vim.diagnostic.get_next()| instead. -- *vim.diagnostic.get_prev_pos()* - Use the "lnum" and "col" fields from the return value of - |vim.diagnostic.get_prev()| instead. -- The "win_id" parameter used by various functions is deprecated in favor of +• *vim.diagnostic.goto_next()* Use |vim.diagnostic.jump()| with `{count=1, float=true}` instead. +• *vim.diagnostic.goto_prev()* Use |vim.diagnostic.jump()| with `{count=-1, float=true}` instead. +• *vim.diagnostic.get_next_pos()* Use the "lnum" and "col" fields from the + return value of |vim.diagnostic.get_next()| instead. +• *vim.diagnostic.get_prev_pos()* Use the "lnum" and "col" fields from the + return value of |vim.diagnostic.get_prev()| instead. +• The "win_id" parameter used by various functions is deprecated in favor of "winid" |winid| -- The "cursor_position" parameter of |vim.diagnostic.JumpOpts| is renamed to - "pos" +• |vim.diagnostic.JumpOpts| renamed its "cursor_position" field to "pos". HIGHLIGHTS -• *TermCursorNC* As of Nvim 0.11, unfocused |terminal| windows no - longer have any cursor. - -TREESITTER -• *TSNode:child_containing_descendant()* Use - |TSNode:child_with_descendant()| instead; it is identical except that it can - return the descendant itself. +• *TermCursorNC* Unfocused |terminal| windows do not have a cursor. LSP • *vim.lsp.util.jump_to_location* Use |vim.lsp.util.show_document()| with - `{focus=true}` instead. + `{focus=true}` instead. • *vim.lsp.buf.execute_command* Use |Client:exec_cmd()| instead. • *vim.lsp.buf.completion* Use |vim.lsp.completion.trigger()| instead. • vim.lsp.buf_request_all The `error` key has been renamed to `err` inside the result parameter of the handler. • *vim.lsp.with()* Pass configuration to equivalent functions in `vim.lsp.buf.*`. -• |vim.lsp.handlers| - No longer support client to server response handlers. Only server to - client requests/notification handlers are supported. +• |vim.lsp.handlers| Does not support client-to-server response handlers. Only + supports server-to-client requests/notification handlers. • *vim.lsp.handlers.signature_help()* Use |vim.lsp.buf.signature_help()| instead. • `client.request()` Use |Client:request()| instead. • `client.request_sync()` Use |Client:request_sync()| instead. @@ -70,6 +55,18 @@ LSP • `client.on_attach()` Use |Client:on_attach()| instead. • `vim.lsp.start_client()` Use |vim.lsp.start()| instead. +LUA +• vim.region() Use |getregionpos()| instead. +• *vim.highlight* Renamed to |vim.hl|. +• vim.validate(opts: table) Use form 1. See |vim.validate()|. + +TREESITTER +• *TSNode:child_containing_descendant()* Use |TSNode:child_with_descendant()| + instead; it is identical except that it can return the descendant itself. + +VIMSCRIPT +• *termopen()* Use |jobstart() with `{term: v:true}`. + ------------------------------------------------------------------------------ DEPRECATED IN 0.10 *deprecated-0.10* @@ -130,198 +127,198 @@ TREESITTER DEPRECATED IN 0.9 *deprecated-0.9* API -- *nvim_get_hl_by_name()* Use |nvim_get_hl()| instead. -- *nvim_get_hl_by_id()* Use |nvim_get_hl()| instead. +• *nvim_get_hl_by_name()* Use |nvim_get_hl()| instead. +• *nvim_get_hl_by_id()* Use |nvim_get_hl()| instead. TREESITTER -- *vim.treesitter.language.require_language()* Use |vim.treesitter.language.add()| instead. -- *vim.treesitter.get_node_at_pos()* Use |vim.treesitter.get_node()| instead. -- *vim.treesitter.get_node_at_cursor()* Use |vim.treesitter.get_node()| +• *vim.treesitter.language.require_language()* Use |vim.treesitter.language.add()| instead. +• *vim.treesitter.get_node_at_pos()* Use |vim.treesitter.get_node()| instead. +• *vim.treesitter.get_node_at_cursor()* Use |vim.treesitter.get_node()| and |TSNode:type()| instead. • The following top level Treesitter functions have been moved: - - *vim.treesitter.inspect_language()* -> |vim.treesitter.language.inspect()| - - *vim.treesitter.get_query_files()* -> |vim.treesitter.query.get_files()| - - *vim.treesitter.set_query()* -> |vim.treesitter.query.set()| - - *vim.treesitter.query.set_query()* -> |vim.treesitter.query.set()| - - *vim.treesitter.get_query()* -> |vim.treesitter.query.get()| - - *vim.treesitter.query.get_query()* -> |vim.treesitter.query.get()| - - *vim.treesitter.parse_query()* -> |vim.treesitter.query.parse()| - - *vim.treesitter.query.parse_query()* -> |vim.treesitter.query.parse()| - - *vim.treesitter.add_predicate()* -> |vim.treesitter.query.add_predicate()| - - *vim.treesitter.add_directive()* -> |vim.treesitter.query.add_directive()| - - *vim.treesitter.list_predicates()* -> |vim.treesitter.query.list_predicates()| - - *vim.treesitter.list_directives()* -> |vim.treesitter.query.list_directives()| - - *vim.treesitter.query.get_range()* -> |vim.treesitter.get_range()| - - *vim.treesitter.query.get_node_text()* -> |vim.treesitter.get_node_text()| + • *vim.treesitter.inspect_language()* -> |vim.treesitter.language.inspect()| + • *vim.treesitter.get_query_files()* -> |vim.treesitter.query.get_files()| + • *vim.treesitter.set_query()* -> |vim.treesitter.query.set()| + • *vim.treesitter.query.set_query()* -> |vim.treesitter.query.set()| + • *vim.treesitter.get_query()* -> |vim.treesitter.query.get()| + • *vim.treesitter.query.get_query()* -> |vim.treesitter.query.get()| + • *vim.treesitter.parse_query()* -> |vim.treesitter.query.parse()| + • *vim.treesitter.query.parse_query()* -> |vim.treesitter.query.parse()| + • *vim.treesitter.add_predicate()* -> |vim.treesitter.query.add_predicate()| + • *vim.treesitter.add_directive()* -> |vim.treesitter.query.add_directive()| + • *vim.treesitter.list_predicates()* -> |vim.treesitter.query.list_predicates()| + • *vim.treesitter.list_directives()* -> |vim.treesitter.query.list_directives()| + • *vim.treesitter.query.get_range()* -> |vim.treesitter.get_range()| + • *vim.treesitter.query.get_node_text()* -> |vim.treesitter.get_node_text()| LUA - - *nvim_exec()* Use |nvim_exec2()| instead. - - *vim.pretty_print()* Use |vim.print()| instead. + • *nvim_exec()* Use |nvim_exec2()| instead. + • *vim.pretty_print()* Use |vim.print()| instead. ------------------------------------------------------------------------------ DEPRECATED IN 0.8 OR EARLIER API -- *nvim_buf_clear_highlight()* Use |nvim_buf_clear_namespace()| instead. -- *nvim_buf_set_virtual_text()* Use |nvim_buf_set_extmark()| instead. -- *nvim_command_output()* Use |nvim_exec2()| instead. -- *nvim_execute_lua()* Use |nvim_exec_lua()| instead. -- *nvim_get_option_info()* Use |nvim_get_option_info2()| instead. +• *nvim_buf_clear_highlight()* Use |nvim_buf_clear_namespace()| instead. +• *nvim_buf_set_virtual_text()* Use |nvim_buf_set_extmark()| instead. +• *nvim_command_output()* Use |nvim_exec2()| instead. +• *nvim_execute_lua()* Use |nvim_exec_lua()| instead. +• *nvim_get_option_info()* Use |nvim_get_option_info2()| instead. COMMANDS -- *:rv* *:rviminfo* Deprecated alias to |:rshada| command. -- *:wv* *:wviminfo* Deprecated alias to |:wshada| command. +• *:rv* *:rviminfo* Deprecated alias to |:rshada| command. +• *:wv* *:wviminfo* Deprecated alias to |:wshada| command. ENVIRONMENT VARIABLES -- *$NVIM_LISTEN_ADDRESS* - - Deprecated way to: - - set the server name (use |--listen| or |serverstart()| instead) - - get the server name (use |v:servername| instead) - - detect a parent Nvim (use |$NVIM| instead) - - Ignored if --listen is given. - - Unset by |terminal| and |jobstart()| unless explicitly given by the "env" +• *$NVIM_LISTEN_ADDRESS* + • Deprecated way to: + • set the server name (use |--listen| or |serverstart()| instead) + • get the server name (use |v:servername| instead) + • detect a parent Nvim (use |$NVIM| instead) + • Ignored if --listen is given. + • Unset by |terminal| and |jobstart()| unless explicitly given by the "env" option. Example: >vim call jobstart(['foo'], { 'env': { 'NVIM_LISTEN_ADDRESS': v:servername } }) < EVENTS -- *BufCreate* Use |BufAdd| instead. -- *EncodingChanged* Never fired; 'encoding' is always "utf-8". -- *FileEncoding* Never fired; equivalent to |EncodingChanged|. -- *GUIEnter* Never fired; use |UIEnter| instead. -- *GUIFailed* Never fired. +• *BufCreate* Use |BufAdd| instead. +• *EncodingChanged* Never fired; 'encoding' is always "utf-8". +• *FileEncoding* Never fired; equivalent to |EncodingChanged|. +• *GUIEnter* Never fired; use |UIEnter| instead. +• *GUIFailed* Never fired. KEYCODES -- ** Use instead. -- ** Use instead. - -FUNCTIONS -- *buffer_exists()* Obsolete name for |bufexists()|. -- *buffer_name()* Obsolete name for |bufname()|. -- *buffer_number()* Obsolete name for |bufnr()|. -- *file_readable()* Obsolete name for |filereadable()|. -- *highlight_exists()* Obsolete name for |hlexists()|. -- *highlightID()* Obsolete name for |hlID()|. -- *inputdialog()* Use |input()| instead. -- *jobclose()* Obsolete name for |chanclose()| -- *jobsend()* Obsolete name for |chansend()| -- *last_buffer_nr()* Obsolete name for bufnr("$"). -- *rpcstart()* Use |jobstart()| with `{'rpc': v:true}` instead. -- *rpcstop()* Use |jobstop()| instead to stop any job, or - `chanclose(id, "rpc")` to close RPC communication - without stopping the job. Use chanclose(id) to close - any socket. +• ** Use instead. +• ** Use instead. HIGHLIGHTS -- *hl-VertSplit* Use |hl-WinSeparator| instead. +• *hl-VertSplit* Use |hl-WinSeparator| instead. LSP DIAGNOSTICS For each of the functions below, use the corresponding function in |vim.diagnostic| instead (unless otherwise noted). For example, use |vim.diagnostic.get()| instead of |vim.lsp.diagnostic.get()|. -- *vim.lsp.diagnostic.clear()* Use |vim.diagnostic.hide()| instead. -- *vim.lsp.diagnostic.disable()* Use |vim.diagnostic.enable()| instead. -- *vim.lsp.diagnostic.display()* Use |vim.diagnostic.show()| instead. -- *vim.lsp.diagnostic.enable()* -- *vim.lsp.diagnostic.get()* -- *vim.lsp.diagnostic.get_all()* Use |vim.diagnostic.get()| instead. -- *vim.lsp.diagnostic.get_count()* Use |vim.diagnostic.count()| instead. -- *vim.lsp.diagnostic.get_line_diagnostics()* Use |vim.diagnostic.get()| instead. -- *vim.lsp.diagnostic.get_next()* -- *vim.lsp.diagnostic.get_next_pos()* -- *vim.lsp.diagnostic.get_prev()* -- *vim.lsp.diagnostic.get_prev_pos()* -- *vim.lsp.diagnostic.get_virtual_text_chunks_for_line()* No replacement. Use +• *vim.lsp.diagnostic.clear()* Use |vim.diagnostic.hide()| instead. +• *vim.lsp.diagnostic.disable()* Use |vim.diagnostic.enable()| instead. +• *vim.lsp.diagnostic.display()* Use |vim.diagnostic.show()| instead. +• *vim.lsp.diagnostic.enable()* +• *vim.lsp.diagnostic.get()* +• *vim.lsp.diagnostic.get_all()* Use |vim.diagnostic.get()| instead. +• *vim.lsp.diagnostic.get_count()* Use |vim.diagnostic.count()| instead. +• *vim.lsp.diagnostic.get_line_diagnostics()* Use |vim.diagnostic.get()| instead. +• *vim.lsp.diagnostic.get_next()* +• *vim.lsp.diagnostic.get_next_pos()* +• *vim.lsp.diagnostic.get_prev()* +• *vim.lsp.diagnostic.get_prev_pos()* +• *vim.lsp.diagnostic.get_virtual_text_chunks_for_line()* No replacement. Use options provided by |vim.diagnostic.config()| to customize virtual text. -- *vim.lsp.diagnostic.goto_next()* -- *vim.lsp.diagnostic.goto_prev()* -- *vim.lsp.diagnostic.redraw()* Use |vim.diagnostic.show()| instead. -- *vim.lsp.diagnostic.reset()* -- *vim.lsp.diagnostic.save()* Use |vim.diagnostic.set()| instead. -- *vim.lsp.diagnostic.set_loclist()* Use |vim.diagnostic.setloclist()| instead. -- *vim.lsp.diagnostic.set_qflist()* Use |vim.diagnostic.setqflist()| instead. -- *vim.lsp.diagnostic.show_line_diagnostics()* Use |vim.diagnostic.open_float()| instead. -- *vim.lsp.diagnostic.show_position_diagnostics()* Use |vim.diagnostic.open_float()| instead. +• *vim.lsp.diagnostic.goto_next()* +• *vim.lsp.diagnostic.goto_prev()* +• *vim.lsp.diagnostic.redraw()* Use |vim.diagnostic.show()| instead. +• *vim.lsp.diagnostic.reset()* +• *vim.lsp.diagnostic.save()* Use |vim.diagnostic.set()| instead. +• *vim.lsp.diagnostic.set_loclist()* Use |vim.diagnostic.setloclist()| instead. +• *vim.lsp.diagnostic.set_qflist()* Use |vim.diagnostic.setqflist()| instead. +• *vim.lsp.diagnostic.show_line_diagnostics()* Use |vim.diagnostic.open_float()| instead. +• *vim.lsp.diagnostic.show_position_diagnostics()* Use |vim.diagnostic.open_float()| instead. The following are deprecated without replacement. These functions are moved internally and are no longer exposed as part of the API. Instead, use |vim.diagnostic.config()| and |vim.diagnostic.show()|. -- *vim.lsp.diagnostic.set_signs()* -- *vim.lsp.diagnostic.set_underline()* -- *vim.lsp.diagnostic.set_virtual_text()* +• *vim.lsp.diagnostic.set_signs()* +• *vim.lsp.diagnostic.set_underline()* +• *vim.lsp.diagnostic.set_virtual_text()* LSP FUNCTIONS -- *vim.lsp.buf.server_ready()* +• *vim.lsp.buf.server_ready()* Use |LspAttach| instead, depending on your use-case. "Server ready" is not part of the LSP spec, so the Nvim LSP client cannot meaningfully implement it. "Ready" is ambiguous because: - - Language servers may finish analyzing the workspace, but edits can always + • Language servers may finish analyzing the workspace, but edits can always re-trigger analysis/builds. - - Language servers can serve some requests even while processing changes. -- *vim.lsp.buf.range_code_action()* Use |vim.lsp.buf.code_action()| with + • Language servers can serve some requests even while processing changes. +• *vim.lsp.buf.range_code_action()* Use |vim.lsp.buf.code_action()| with the `range` parameter. -- *vim.lsp.util.diagnostics_to_items()* Use |vim.diagnostic.toqflist()| instead. -- *vim.lsp.util.set_qflist()* Use |setqflist()| instead. -- *vim.lsp.util.set_loclist()* Use |setloclist()| instead. -- *vim.lsp.buf_get_clients()* Use |vim.lsp.get_clients()| with +• *vim.lsp.util.diagnostics_to_items()* Use |vim.diagnostic.toqflist()| instead. +• *vim.lsp.util.set_qflist()* Use |setqflist()| instead. +• *vim.lsp.util.set_loclist()* Use |setloclist()| instead. +• *vim.lsp.buf_get_clients()* Use |vim.lsp.get_clients()| with {buffer=bufnr} instead. -- *vim.lsp.buf.formatting()* Use |vim.lsp.buf.format()| with +• *vim.lsp.buf.formatting()* Use |vim.lsp.buf.format()| with {async=true} instead. -- *vim.lsp.buf.formatting_sync()* Use |vim.lsp.buf.format()| with +• *vim.lsp.buf.formatting_sync()* Use |vim.lsp.buf.format()| with {async=false} instead. -- *vim.lsp.buf.range_formatting()* Use |vim.lsp.formatexpr()| +• *vim.lsp.buf.range_formatting()* Use |vim.lsp.formatexpr()| or |vim.lsp.buf.format()| instead. LUA -- vim.register_keystroke_callback() Use |vim.on_key()| instead. +• vim.register_keystroke_callback() Use |vim.on_key()| instead. NORMAL COMMANDS -- *]f* *[f* Same as "gf". +• *]f* *[f* Same as "gf". OPTIONS -- *cpo-<* *:menu-* *:menu-special* *:map-* *:map-special* +• *cpo-<* *:menu-* *:menu-special* *:map-* *:map-special* `<>` notation is always enabled. -- *'fe'* 'fenc'+'enc' before Vim 6.0; no longer used. -- *'highlight'* *'hl'* Names of builtin |highlight-groups| cannot be changed. -- *'langnoremap'* Deprecated alias to 'nolangremap'. -- 'sessionoptions' Flags "unix", "slash" are ignored and always enabled. -- *'vi'* -- 'viewoptions' Flags "unix", "slash" are ignored and always enabled. -- *'viminfo'* Deprecated alias to 'shada' option. -- *'viminfofile'* Deprecated alias to 'shadafile' option. -- *'paste'* *'nopaste'* Just Paste It.™ The 'paste' option is obsolete: +• *'fe'* 'fenc'+'enc' before Vim 6.0; no longer used. +• *'highlight'* *'hl'* Names of builtin |highlight-groups| cannot be changed. +• *'langnoremap'* Deprecated alias to 'nolangremap'. +• 'sessionoptions' Flags "unix", "slash" are ignored and always enabled. +• *'vi'* +• 'viewoptions' Flags "unix", "slash" are ignored and always enabled. +• *'viminfo'* Deprecated alias to 'shada' option. +• *'viminfofile'* Deprecated alias to 'shadafile' option. +• *'paste'* *'nopaste'* Just Paste It.™ The 'paste' option is obsolete: |paste| is handled automatically when you paste text using your terminal's or GUI's paste feature (CTRL-SHIFT-v, CMD-v (macOS), middle-click, …). Enables "paste mode": - - Disables mappings in Insert, Cmdline mode. - - Disables abbreviations. - - Resets 'autoindent' 'expandtab' 'revins' 'ruler' + • Disables mappings in Insert, Cmdline mode. + • Disables abbreviations. + • Resets 'autoindent' 'expandtab' 'revins' 'ruler' 'showmatch' 'smartindent' 'smarttab' 'softtabstop' 'textwidth' 'wrapmargin'. - - Treats 'formatoptions' as empty. - - Disables the effect of these options: - - 'cindent' - - 'indentexpr' - - 'lisp' + • Treats 'formatoptions' as empty. + • Disables the effect of these options: + • 'cindent' + • 'indentexpr' + • 'lisp' UI EXTENSIONS -- *ui-wildmenu* Use |ui-cmdline| with |ui-popupmenu| instead. Enabled +• *ui-wildmenu* Use |ui-cmdline| with |ui-popupmenu| instead. Enabled by the `ext_wildmenu` |ui-option|. Emits these events: - - `["wildmenu_show", items]` - - `["wildmenu_select", selected]` - - `["wildmenu_hide"]` -- *term_background* Unused. The terminal background color is now detected + • `["wildmenu_show", items]` + • `["wildmenu_select", selected]` + • `["wildmenu_hide"]` +• *term_background* Unused. The terminal background color is now detected by the Nvim core directly instead of the TUI. VARIABLES -- *b:terminal_job_pid* Use `jobpid(&channel)` instead. -- *b:terminal_job_id* Use `&channel` instead. To access in non-current buffer: - - Lua: `vim.bo[bufnr].channel` - - Vimscript: `getbufvar(bufnr, '&channel')` +• *b:terminal_job_pid* Use `jobpid(&channel)` instead. +• *b:terminal_job_id* Use `&channel` instead. To access in non-current buffer: + • Lua: `vim.bo[bufnr].channel` + • Vimscript: `getbufvar(bufnr, '&channel')` + +VIMSCRIPT +• *buffer_exists()* Obsolete name for |bufexists()|. +• *buffer_name()* Obsolete name for |bufname()|. +• *buffer_number()* Obsolete name for |bufnr()|. +• *file_readable()* Obsolete name for |filereadable()|. +• *highlight_exists()* Obsolete name for |hlexists()|. +• *highlightID()* Obsolete name for |hlID()|. +• *inputdialog()* Use |input()| instead. +• *jobclose()* Obsolete name for |chanclose()| +• *jobsend()* Obsolete name for |chansend()| +• *last_buffer_nr()* Obsolete name for bufnr("$"). +• *rpcstart()* Use |jobstart()| with `{'rpc': v:true}` instead. +• *rpcstop()* Use |jobstop()| instead to stop any job, or + `chanclose(id, "rpc")` to close RPC communication + without stopping the job. Use chanclose(id) to close + any socket. vim:noet:tw=78:ts=8:ft=help:norl: diff --git a/runtime/doc/terminal.txt b/runtime/doc/terminal.txt index f9536c2f0c..ff8e3a976f 100644 --- a/runtime/doc/terminal.txt +++ b/runtime/doc/terminal.txt @@ -26,7 +26,7 @@ Start *terminal-start* There are several ways to create a terminal buffer: - Run the |:terminal| command. -- Call the |nvim_open_term()| or |termopen()| function. +- Call |nvim_open_term()| or `jobstart(…, {'term': v:true})`. - Edit a "term://" buffer. Examples: >vim :edit term://bash :vsplit term://top diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua index 0b8a54e957..4216a2acb7 100644 --- a/runtime/lua/vim/_defaults.lua +++ b/runtime/lua/vim/_defaults.lua @@ -435,7 +435,7 @@ do group = nvim_terminal_augroup, desc = 'Treat term:// buffers as terminal buffers', nested = true, - command = "if !exists('b:term_title')|call termopen(matchstr(expand(\"\"), '\\c\\mterm://\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), {'cwd': expand(get(matchlist(expand(\"\"), '\\c\\mterm://\\(.\\{-}\\)//'), 1, ''))})", + command = "if !exists('b:term_title')|call jobstart(matchstr(expand(\"\"), '\\c\\mterm://\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), {'term': v:true, 'cwd': expand(get(matchlist(expand(\"\"), '\\c\\mterm://\\(.\\{-}\\)//'), 1, ''))})", }) vim.api.nvim_create_autocmd({ 'TermClose' }, { diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index 55274963ed..c912758a0b 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -272,12 +272,12 @@ function vim.api.nvim_buf_attach(buffer, send_buffer, opts) end --- This temporarily switches current buffer to "buffer". --- If the current window already shows "buffer", the window is not switched. --- If a window inside the current tabpage (including a float) already shows the ---- buffer, then one of these windows will be set as current window temporarily. +--- buffer, then one of those windows will be set as current window temporarily. --- Otherwise a temporary scratch window (called the "autocmd window" for --- historical reasons) will be used. --- --- This is useful e.g. to call Vimscript functions that only work with the ---- current buffer/window currently, like `termopen()`. +--- current buffer/window currently, like `jobstart(…, {'term': v:true})`. --- --- @param buffer integer Buffer handle, or 0 for current buffer --- @param fun function Function to call inside the buffer (currently Lua callable diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index cf1beda15f..e855b29d49 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -4805,7 +4805,7 @@ function vim.fn.jobresize(job, width, height) end --- @return any function vim.fn.jobsend(...) end ---- Note: Prefer |vim.system()| in Lua (unless using the `pty` option). +--- Note: Prefer |vim.system()| in Lua (unless using `rpc`, `pty`, or `term`). --- --- Spawns {cmd} as a job. --- If {cmd} is a List it runs directly (no 'shell'). @@ -4879,6 +4879,10 @@ function vim.fn.jobsend(...) end --- stdin: (string) Either "pipe" (default) to connect the --- job's stdin to a channel or "null" to disconnect --- stdin. +--- term: (boolean) Spawns {cmd} in a new pseudo-terminal session +--- connected to the current (unmodified) buffer. Implies "pty". +--- Default "height" and "width" are set to the current window +--- dimensions. |jobstart()|. Defaults $TERM to "xterm-256color". --- width: (number) Width of the `pty` terminal. --- --- {opts} is passed as |self| dictionary to the callback; the @@ -10168,19 +10172,8 @@ function vim.fn.tanh(expr) end --- @return string function vim.fn.tempname() end ---- Spawns {cmd} in a new pseudo-terminal session connected ---- 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. Note that termopen() implies a "pty" arg ---- to jobstart(), and thus has the implications documented at ---- |jobstart()|. ---- ---- Returns the same values as jobstart(). ---- ---- Terminal environment is initialized as in |jobstart-env|, ---- except $TERM is set to "xterm-256color". Full behavior is ---- described in |terminal|. +--- @deprecated +--- Use |jobstart()| with `{term: v:true}` instead. --- --- @param cmd string|string[] --- @param opts? table diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index fd8c4b8817..b6da91a833 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -38,7 +38,7 @@ " NEOVIM COMPATIBILITY " " The vim specific functionalities were replaced with neovim specific calls: -" - term_start -> termopen +" - term_start -> `jobstart(…, {'term': v:true})` " - term_sendkeys -> chansend " - term_getline -> getbufline " - job_info && term_getjob -> nvim_get_chan_info @@ -251,7 +251,7 @@ endfunc " Open a terminal window without a job, to run the debugged program in. func s:StartDebug_term(dict) execute s:vertical ? 'vnew' : 'new' - let s:pty_job_id = termopen('tail -f /dev/null;#gdb program') + let s:pty_job_id = jobstart('tail -f /dev/null;#gdb program', {'term': v:true}) if s:pty_job_id == 0 call s:Echoerr('Invalid argument (or job table is full) while opening terminal window') return @@ -323,7 +323,7 @@ func s:StartDebug_term(dict) execute 'new' " call ch_log($'executing "{join(gdb_cmd)}"') - let s:gdb_job_id = termopen(gdb_cmd, {'on_exit': function('s:EndTermDebug')}) + let s:gdb_job_id = jobstart(gdb_cmd, {'term': v:true, 'on_exit': function('s:EndTermDebug')}) if s:gdb_job_id == 0 call s:Echoerr('Invalid argument (or job table is full) while opening gdb terminal window') exe 'bwipe! ' . s:ptybufnr @@ -491,7 +491,7 @@ func s:StartDebug_prompt(dict) " Unix: Run the debugged program in a terminal window. Open it below the " gdb window. belowright new - let s:pty_job_id = termopen('tail -f /dev/null;#gdb program') + let s:pty_job_id = jobstart('tail -f /dev/null;#gdb program', {'term': v:true}) if s:pty_job_id == 0 call s:Echoerr('Invalid argument (or job table is full) while opening terminal window') return diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 2b6aa8b371..6c40eb9b20 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -1181,12 +1181,12 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Arena *arena, /// This temporarily switches current buffer to "buffer". /// If the current window already shows "buffer", the window is not switched. /// If a window inside the current tabpage (including a float) already shows the -/// buffer, then one of these windows will be set as current window temporarily. +/// buffer, then one of those windows will be set as current window temporarily. /// Otherwise a temporary scratch window (called the "autocmd window" for /// historical reasons) will be used. /// /// This is useful e.g. to call Vimscript functions that only work with the -/// current buffer/window currently, like |termopen()|. +/// current buffer/window currently, like `jobstart(…, {'term': v:true})`. /// /// @param buffer Buffer handle, or 0 for current buffer /// @param fun Function to call inside the buffer (currently Lua callable diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 47f0a13b29..8bdd8dad4c 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8493,7 +8493,7 @@ char *do_string_sub(char *str, size_t len, char *pat, char *sub, typval_T *expr, return ret; } -/// common code for getting job callbacks for jobstart, termopen and rpcstart +/// Common code for getting job callbacks for `jobstart`. /// /// @return true/false on success/failure. bool common_job_callbacks(dict_T *vopts, CallbackReader *on_stdout, CallbackReader *on_stderr, diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 9de189cc16..19370ab693 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -5945,7 +5945,7 @@ M.funcs = { jobstart = { args = { 1, 2 }, desc = [=[ - Note: Prefer |vim.system()| in Lua (unless using the `pty` option). + Note: Prefer |vim.system()| in Lua (unless using `rpc`, `pty`, or `term`). Spawns {cmd} as a job. If {cmd} is a List it runs directly (no 'shell'). @@ -6019,6 +6019,10 @@ M.funcs = { stdin: (string) Either "pipe" (default) to connect the job's stdin to a channel or "null" to disconnect stdin. + term: (boolean) Spawns {cmd} in a new pseudo-terminal session + connected to the current (unmodified) buffer. Implies "pty". + Default "height" and "width" are set to the current window + dimensions. |jobstart()|. Defaults $TERM to "xterm-256color". width: (number) Width of the `pty` terminal. {opts} is passed as |self| dictionary to the callback; the @@ -12271,21 +12275,10 @@ M.funcs = { signature = 'tempname()', }, termopen = { + deprecated = true, args = { 1, 2 }, desc = [=[ - Spawns {cmd} in a new pseudo-terminal session connected - 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. Note that termopen() implies a "pty" arg - to jobstart(), and thus has the implications documented at - |jobstart()|. - - Returns the same values as jobstart(). - - Terminal environment is initialized as in |jobstart-env|, - except $TERM is set to "xterm-256color". Full behavior is - described in |terminal|. + Use |jobstart()| with `{term: v:true}` instead. ]=], name = 'termopen', params = { { 'cmd', 'string|string[]' }, { 'opts', 'table' } }, diff --git a/src/nvim/eval/deprecated.c b/src/nvim/eval/deprecated.c new file mode 100644 index 0000000000..0874e4ae76 --- /dev/null +++ b/src/nvim/eval/deprecated.c @@ -0,0 +1,45 @@ +#include // for true + +#include "nvim/errors.h" +#include "nvim/eval/deprecated.h" +#include "nvim/eval/funcs.h" +#include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" +#include "nvim/ex_cmds.h" +#include "nvim/gettext_defs.h" // for _ +#include "nvim/macros_defs.h" // for S_LEN +#include "nvim/message.h" // for semsg +#include "nvim/types_defs.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/deprecated.c.generated.h" +#endif + +/// "termopen(cmd[, cwd])" function +void f_termopen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + if (check_secure()) { + return; + } + + bool must_free = false; + + if (argvars[1].v_type == VAR_UNKNOWN) { + must_free = true; + argvars[1].v_type = VAR_DICT; + argvars[1].vval.v_dict = tv_dict_alloc(); + } + + if (argvars[1].v_type != VAR_DICT) { + // Wrong argument types + semsg(_(e_invarg2), "expected dictionary"); + return; + } + + tv_dict_add_bool(argvars[1].vval.v_dict, S_LEN("term"), true); + f_jobstart(argvars, rettv, fptr); + if (must_free) { + tv_dict_free(argvars[1].vval.v_dict); + } +} + diff --git a/src/nvim/eval/deprecated.h b/src/nvim/eval/deprecated.h new file mode 100644 index 0000000000..69882695e7 --- /dev/null +++ b/src/nvim/eval/deprecated.h @@ -0,0 +1,11 @@ +#pragma once + +#include // for true + +#include "nvim/message.h" // for semsg +#include "nvim/eval/typval_defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" // IWYU pragma: keep + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/deprecated.h.generated.h" +#endif diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 768d7664f7..e3fb2e7279 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -38,6 +38,7 @@ #include "nvim/eval.h" #include "nvim/eval/buffer.h" #include "nvim/eval/decode.h" +#include "nvim/eval/deprecated.h" #include "nvim/eval/encode.h" #include "nvim/eval/executor.h" #include "nvim/eval/funcs.h" @@ -3840,7 +3841,7 @@ static const char *required_env_vars[] = { NULL }; -static dict_T *create_environment(const dictitem_T *job_env, const bool clear_env, const bool pty, +dict_T *create_environment(const dictitem_T *job_env, const bool clear_env, const bool pty, const char * const pty_term_name) { dict_T *env = tv_dict_alloc(); @@ -3933,7 +3934,7 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en } /// "jobstart()" function -static void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_NUMBER; rettv->vval.v_number = 0; @@ -3942,9 +3943,9 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } + const char *cmd; bool executable = true; - char **argv = tv_to_argv(&argvars[0], NULL, &executable); - dict_T *env = NULL; + char **argv = tv_to_argv(&argvars[0], &cmd, &executable); if (!argv) { rettv->vval.v_number = executable ? 0 : -1; return; // Did error message in tv_to_argv. @@ -3961,6 +3962,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) bool detach = false; bool rpc = false; bool pty = false; + bool term = false; bool clear_env = false; bool overlapped = false; ChannelStdinMode stdin_mode = kChannelStdinPipe; @@ -3974,7 +3976,8 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) detach = tv_dict_get_number(job_opts, "detach") != 0; rpc = tv_dict_get_number(job_opts, "rpc") != 0; - pty = tv_dict_get_number(job_opts, "pty") != 0; + term = tv_dict_get_number(job_opts, "term") != 0; + pty = term || tv_dict_get_number(job_opts, "pty") != 0; clear_env = tv_dict_get_number(job_opts, "clear_env") != 0; overlapped = tv_dict_get_number(job_opts, "overlapped") != 0; @@ -4032,23 +4035,84 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) uint16_t height = 0; char *term_name = NULL; - if (pty) { - width = (uint16_t)tv_dict_get_number(job_opts, "width"); - height = (uint16_t)tv_dict_get_number(job_opts, "height"); - // Legacy method, before env option existed, to specify $TERM. No longer - // documented, but still usable to avoid breaking scripts. - term_name = tv_dict_get_string(job_opts, "TERM", false); - if (!term_name) { - term_name = "ansi"; + if (term) { + if (text_locked()) { + text_locked_msg(); + shell_free_argv(argv); + return; } + if (curbuf->b_changed) { + emsg(_("jobstart(...,{term:true}) requires unmodified buffer")); + return; + } + assert(!rpc); + term_name = "xterm-256color"; + cwd = cwd ? cwd : "."; + overlapped = false; + detach = false; + stdin_mode = kChannelStdinPipe; + width = (uint16_t)MAX(0, curwin->w_width_inner - win_col_off(curwin)); + height = (uint16_t)curwin->w_height_inner; } - env = create_environment(job_env, clear_env, pty, term_name); + if (pty) { + width = width ? width : (uint16_t)tv_dict_get_number(job_opts, "width"); + height = height ? height : (uint16_t)tv_dict_get_number(job_opts, "height"); + // Deprecated TERM field is from before `env` option existed. + term_name = term_name ? term_name : tv_dict_get_string(job_opts, "TERM", false); + term_name = term_name ? term_name : "ansi"; + } + dict_T *env = create_environment(job_env, clear_env, pty, term_name); Channel *chan = channel_job_start(argv, NULL, on_stdout, on_stderr, on_exit, pty, rpc, overlapped, detach, stdin_mode, cwd, width, height, env, &rettv->vval.v_number); - if (chan) { + if (term) { + if (rettv->vval.v_number <= 0) { + return; + } + + int pid = chan->stream.pty.proc.pid; + + // "./…" => "/home/foo/…" + vim_FullName(cwd, NameBuff, sizeof(NameBuff), false); + // "/home/foo/…" => "~/…" + size_t len = home_replace(NULL, NameBuff, IObuff, sizeof(IObuff), true); + // Trim slash. + if (len != 1 && (IObuff[len - 1] == '\\' || IObuff[len - 1] == '/')) { + IObuff[len - 1] = NUL; + } + + if (len == 1 && IObuff[0] == '/') { + // Avoid ambiguity in the URI when CWD is root directory. + IObuff[1] = '.'; + IObuff[2] = NUL; + } + + // Terminal URI: "term://$CWD//$PID:$CMD" + snprintf(NameBuff, sizeof(NameBuff), "term://%s//%d:%s", IObuff, pid, cmd); + // Buffer has no terminal associated yet; unset 'swapfile' to ensure no swapfile is created. + curbuf->b_p_swf = false; + + apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, false, curbuf); + setfname(curbuf, NameBuff, NULL, true); + apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, curbuf); + + Error err = ERROR_INIT; + // Set (deprecated) buffer-local vars (prefer 'channel' buffer-local option). + dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_id"), + INTEGER_OBJ((Integer)chan->id), false, false, NULL, &err); + api_clear_error(&err); + dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_pid"), + INTEGER_OBJ(pid), false, false, NULL, &err); + api_clear_error(&err); + + channel_incref(chan); + channel_terminal_open(curbuf, chan); + channel_create_event(chan, NULL); + channel_decref(chan); + } + else if (chan) { channel_create_event(chan, NULL); } } @@ -8133,134 +8197,6 @@ static void f_taglist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) (char *)tag_pattern, (char *)fname); } -/// "termopen(cmd[, cwd])" function -static void f_termopen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) -{ - if (check_secure()) { - return; - } - if (text_locked()) { - text_locked_msg(); - return; - } - if (curbuf->b_changed) { - emsg(_("Can only call this function in an unmodified buffer")); - return; - } - - const char *cmd; - bool executable = true; - char **argv = tv_to_argv(&argvars[0], &cmd, &executable); - if (!argv) { - rettv->vval.v_number = executable ? 0 : -1; - return; // Did error message in tv_to_argv. - } - - if (argvars[1].v_type != VAR_DICT && argvars[1].v_type != VAR_UNKNOWN) { - // Wrong argument type - semsg(_(e_invarg2), "expected dictionary"); - shell_free_argv(argv); - return; - } - - CallbackReader on_stdout = CALLBACK_READER_INIT; - CallbackReader on_stderr = CALLBACK_READER_INIT; - Callback on_exit = CALLBACK_NONE; - dict_T *job_opts = NULL; - const char *cwd = "."; - dict_T *env = NULL; - const bool pty = true; - bool clear_env = false; - dictitem_T *job_env = NULL; - - if (argvars[1].v_type == VAR_DICT) { - job_opts = argvars[1].vval.v_dict; - - const char *const new_cwd = tv_dict_get_string(job_opts, "cwd", false); - if (new_cwd && *new_cwd != NUL) { - cwd = new_cwd; - // The new cwd must be a directory. - if (!os_isdir(cwd)) { - semsg(_(e_invarg2), "expected valid directory"); - shell_free_argv(argv); - return; - } - } - - job_env = tv_dict_find(job_opts, S_LEN("env")); - if (job_env && job_env->di_tv.v_type != VAR_DICT) { - semsg(_(e_invarg2), "env"); - shell_free_argv(argv); - return; - } - - clear_env = tv_dict_get_number(job_opts, "clear_env") != 0; - - if (!common_job_callbacks(job_opts, &on_stdout, &on_stderr, &on_exit)) { - shell_free_argv(argv); - return; - } - } - - env = create_environment(job_env, clear_env, pty, "xterm-256color"); - - const bool rpc = false; - const bool overlapped = false; - const bool detach = false; - ChannelStdinMode stdin_mode = kChannelStdinPipe; - uint16_t term_width = (uint16_t)MAX(0, curwin->w_width_inner - win_col_off(curwin)); - Channel *chan = channel_job_start(argv, NULL, on_stdout, on_stderr, on_exit, - pty, rpc, overlapped, detach, stdin_mode, - cwd, term_width, (uint16_t)curwin->w_height_inner, - env, &rettv->vval.v_number); - if (rettv->vval.v_number <= 0) { - return; - } - - int pid = chan->stream.pty.proc.pid; - - // "./…" => "/home/foo/…" - vim_FullName(cwd, NameBuff, sizeof(NameBuff), false); - // "/home/foo/…" => "~/…" - size_t len = home_replace(NULL, NameBuff, IObuff, sizeof(IObuff), true); - // Trim slash. - if (len != 1 && (IObuff[len - 1] == '\\' || IObuff[len - 1] == '/')) { - IObuff[len - 1] = NUL; - } - - if (len == 1 && IObuff[0] == '/') { - // Avoid ambiguity in the URI when CWD is root directory. - IObuff[1] = '.'; - IObuff[2] = NUL; - } - - // Terminal URI: "term://$CWD//$PID:$CMD" - snprintf(NameBuff, sizeof(NameBuff), "term://%s//%d:%s", - IObuff, pid, cmd); - // at this point the buffer has no terminal instance associated yet, so unset - // the 'swapfile' option to ensure no swap file will be created - curbuf->b_p_swf = false; - - apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, false, curbuf); - setfname(curbuf, NameBuff, NULL, true); - apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, curbuf); - - // Save the job id and pid in b:terminal_job_{id,pid} - Error err = ERROR_INIT; - // deprecated: use 'channel' buffer option - dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_id"), - INTEGER_OBJ((Integer)chan->id), false, false, NULL, &err); - api_clear_error(&err); - dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_pid"), - INTEGER_OBJ(pid), false, false, NULL, &err); - api_clear_error(&err); - - channel_incref(chan); - channel_terminal_open(curbuf, chan); - channel_create_event(chan, NULL); - channel_decref(chan); -} - /// "timer_info([timer])" function static void f_timer_info(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 052bf3b9f7..bd8623397a 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -7775,7 +7775,7 @@ static void ex_terminal(exarg_T *eap) if (*eap->arg != NUL) { // Run {cmd} in 'shell'. char *name = vim_strsave_escaped(eap->arg, "\"\\"); snprintf(ex_cmd + len, sizeof(ex_cmd) - len, - " | call termopen(\"%s\")", name); + " | call jobstart(\"%s\",{'term':v:true})", name); xfree(name); } else { // No {cmd}: run the job with tokenized 'shell'. if (*p_sh == NUL) { @@ -7798,7 +7798,7 @@ static void ex_terminal(exarg_T *eap) shell_free_argv(argv); snprintf(ex_cmd + len, sizeof(ex_cmd) - len, - " | call termopen([%s])", shell_argv + 1); + " | call jobstart([%s], {'term':v:true})", shell_argv + 1); } do_cmdline_cmd(ex_cmd); diff --git a/src/nvim/generators/gen_eval.lua b/src/nvim/generators/gen_eval.lua index 443c68e008..0b6ee6cb24 100644 --- a/src/nvim/generators/gen_eval.lua +++ b/src/nvim/generators/gen_eval.lua @@ -19,6 +19,7 @@ hashpipe:write([[ #include "nvim/digraph.h" #include "nvim/eval.h" #include "nvim/eval/buffer.h" +#include "nvim/eval/deprecated.h" #include "nvim/eval/fs.h" #include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index fa08f3d6ca..8d83b85cb2 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -357,7 +357,7 @@ static void term_output_callback(const char *s, size_t len, void *user_data) /// Initializes terminal properties, and triggers TermOpen. /// -/// The PTY process (TerminalOptions.data) was already started by termopen(), +/// The PTY process (TerminalOptions.data) was already started by jobstart(), /// via ex_terminal() or the term:// BufReadCmd. /// /// @param buf Buffer used for presentation of the terminal. diff --git a/test/functional/api/buffer_updates_spec.lua b/test/functional/api/buffer_updates_spec.lua index 527394bfd1..489f601d31 100644 --- a/test/functional/api/buffer_updates_spec.lua +++ b/test/functional/api/buffer_updates_spec.lua @@ -879,7 +879,8 @@ describe('API: buffer events:', function() it('when :terminal lines change', function() local buffer_lines = {} local expected_lines = {} - fn.termopen({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '-n', '-c', 'set shortmess+=A' }, { + fn.jobstart({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '-n', '-c', 'set shortmess+=A' }, { + term = true, env = { VIMRUNTIME = os.getenv('VIMRUNTIME') }, }) diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 130be9987f..fbc9490df6 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -2704,7 +2704,8 @@ describe('API', function() -- :terminal with args + running process. command('enew') local progpath_esc = eval('shellescape(v:progpath)') - fn.termopen(('%s -u NONE -i NONE'):format(progpath_esc), { + fn.jobstart(('%s -u NONE -i NONE'):format(progpath_esc), { + term = true, env = { VIMRUNTIME = os.getenv('VIMRUNTIME') }, }) eq(-1, eval('jobwait([&channel], 0)[0]')) -- Running? diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index b4d80d4ed4..20b7ab0f13 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -1230,7 +1230,7 @@ describe('pty process teardown', function() it('does not prevent/delay exit. #4798 #4900', function() skip(fn.executable('sleep') == 0, 'missing "sleep" command') -- Use a nested nvim (in :term) to test without --headless. - fn.termopen({ + fn.jobstart({ n.nvim_prog, '-u', 'NONE', @@ -1243,7 +1243,10 @@ describe('pty process teardown', function() '+terminal', '+!(sleep 300 &)', '+qa', - }, { env = { VIMRUNTIME = os.getenv('VIMRUNTIME') } }) + }, { + term = true, + env = { VIMRUNTIME = os.getenv('VIMRUNTIME') }, + }) -- Exiting should terminate all descendants (PTY, its children, ...). screen:expect([[ diff --git a/test/functional/core/main_spec.lua b/test/functional/core/main_spec.lua index bfa5a0eb6a..ce4ba1905f 100644 --- a/test/functional/core/main_spec.lua +++ b/test/functional/core/main_spec.lua @@ -103,7 +103,8 @@ describe('command-line option', function() -- Need to explicitly pipe to stdin so that the embedded Nvim instance doesn't try to read -- data from the terminal #18181 - fn.termopen(string.format([[echo "" | %s]], table.concat(args, ' ')), { + fn.jobstart(string.format([[echo "" | %s]], table.concat(args, ' ')), { + term = true, env = { VIMRUNTIME = os.getenv('VIMRUNTIME') }, }) screen:expect( diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index 319a037342..76b0755441 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -55,7 +55,10 @@ describe('startup', function() clear() local screen screen = Screen.new(84, 3) - fn.termopen({ nvim_prog, '-u', 'NONE', '--server', eval('v:servername'), '--remote-ui' }) + fn.jobstart( + { nvim_prog, '-u', 'NONE', '--server', eval('v:servername'), '--remote-ui' }, + { term = true } + ) screen:expect([[ ^Cannot attach UI of :terminal child to its parent. (Unset $NVIM to skip this check) | |*2 @@ -98,7 +101,7 @@ describe('startup', function() screen = Screen.new(60, 7) -- not the same colors on windows for some reason screen._default_attr_ids = nil - local id = fn.termopen({ + local id = fn.jobstart({ nvim_prog, '-u', 'NONE', @@ -108,6 +111,7 @@ describe('startup', function() 'set noruler', '-D', }, { + term = true, env = { VIMRUNTIME = os.getenv('VIMRUNTIME'), }, @@ -360,7 +364,7 @@ describe('startup', function() command([[set shellcmdflag=/s\ /c shellxquote=\"]]) end -- Running in :terminal - fn.termopen({ + fn.jobstart({ nvim_prog, '-u', 'NONE', @@ -371,6 +375,7 @@ describe('startup', function() '-c', 'echo has("ttyin") has("ttyout")', }, { + term = true, env = { VIMRUNTIME = os.getenv('VIMRUNTIME'), }, @@ -393,13 +398,14 @@ describe('startup', function() os.remove('Xtest_startup_ttyout') end) -- Running in :terminal - fn.termopen( + fn.jobstart( ( [["%s" -u NONE -i NONE --cmd "%s"]] .. [[ -c "call writefile([has('ttyin'), has('ttyout')], 'Xtest_startup_ttyout')"]] .. [[ -c q | cat -v]] ):format(nvim_prog, nvim_set), { + term = true, env = { VIMRUNTIME = os.getenv('VIMRUNTIME'), }, @@ -424,7 +430,7 @@ describe('startup', function() os.remove('Xtest_startup_ttyout') end) -- Running in :terminal - fn.termopen( + fn.jobstart( ( [[echo foo | ]] -- Input from a pipe. .. [["%s" -u NONE -i NONE --cmd "%s"]] @@ -432,6 +438,7 @@ describe('startup', function() .. [[ -c q -- -]] ):format(nvim_prog, nvim_set), { + term = true, env = { VIMRUNTIME = os.getenv('VIMRUNTIME'), }, @@ -454,13 +461,14 @@ describe('startup', function() command([[set shellcmdflag=/s\ /c shellxquote=\"]]) end -- Running in :terminal - fn.termopen( + fn.jobstart( ( [[echo foo | ]] .. [["%s" -u NONE -i NONE --cmd "%s"]] .. [[ -c "echo has('ttyin') has('ttyout')"]] ):format(nvim_prog, nvim_set), { + term = true, env = { VIMRUNTIME = os.getenv('VIMRUNTIME'), }, @@ -614,7 +622,7 @@ describe('startup', function() local screen screen = Screen.new(60, 6) screen._default_attr_ids = nil - local id = fn.termopen({ + local id = fn.jobstart({ nvim_prog, '-u', 'NONE', @@ -625,6 +633,7 @@ describe('startup', function() '--cmd', 'let g:foo = g:bar', }, { + term = true, env = { VIMRUNTIME = os.getenv('VIMRUNTIME'), }, @@ -1144,7 +1153,8 @@ describe('user config init', function() local screen = Screen.new(50, 8) screen._default_attr_ids = nil - fn.termopen({ nvim_prog }, { + fn.jobstart({ nvim_prog }, { + term = true, env = { VIMRUNTIME = os.getenv('VIMRUNTIME'), }, @@ -1418,7 +1428,7 @@ describe('inccommand on ex mode', function() clear() local screen screen = Screen.new(60, 10) - local id = fn.termopen({ + local id = fn.jobstart({ nvim_prog, '-u', 'NONE', @@ -1429,6 +1439,7 @@ describe('inccommand on ex mode', function() '-E', 'test/README.md', }, { + term = true, env = { VIMRUNTIME = os.getenv('VIMRUNTIME') }, }) fn.chansend(id, '%s/N') diff --git a/test/functional/editor/mode_normal_spec.lua b/test/functional/editor/mode_normal_spec.lua index 5237f51237..353a261edb 100644 --- a/test/functional/editor/mode_normal_spec.lua +++ b/test/functional/editor/mode_normal_spec.lua @@ -26,10 +26,10 @@ describe('Normal mode', function() it('&showcmd does not crash with :startinsert #28419', function() local screen = Screen.new(60, 17) - fn.termopen( - { n.nvim_prog, '--clean', '--cmd', 'startinsert' }, - { env = { VIMRUNTIME = os.getenv('VIMRUNTIME') } } - ) + fn.jobstart({ n.nvim_prog, '--clean', '--cmd', 'startinsert' }, { + term = true, + env = { VIMRUNTIME = os.getenv('VIMRUNTIME') }, + }) screen:expect({ grid = [[ ^ | diff --git a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua index 2820cc9663..7234985009 100644 --- a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua +++ b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua @@ -115,7 +115,8 @@ describe("preserve and (R)ecover with custom 'directory'", function() t.skip(t.is_os('win')) local screen0 = Screen.new() local child_server = new_pipename() - fn.termopen({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '--listen', child_server }, { + fn.jobstart({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '--listen', child_server }, { + term = true, env = { VIMRUNTIME = os.getenv('VIMRUNTIME') }, }) screen0:expect({ any = pesc('[No Name]') }) -- Wait for the child process to start. @@ -446,9 +447,10 @@ describe('quitting swapfile dialog on startup stops TUI properly', function() end) it('(Q)uit at first file argument', function() - local chan = fn.termopen( + local chan = fn.jobstart( { nvim_prog, '-u', 'NONE', '-i', 'NONE', '--cmd', init_dir, '--cmd', init_set, testfile }, { + term = true, env = { VIMRUNTIME = os.getenv('VIMRUNTIME') }, } ) @@ -468,7 +470,7 @@ describe('quitting swapfile dialog on startup stops TUI properly', function() end) it('(A)bort at second file argument with -p', function() - local chan = fn.termopen({ + local chan = fn.jobstart({ nvim_prog, '-u', 'NONE', @@ -482,6 +484,7 @@ describe('quitting swapfile dialog on startup stops TUI properly', function() otherfile, testfile, }, { + term = true, env = { VIMRUNTIME = os.getenv('VIMRUNTIME') }, }) retry(nil, nil, function() @@ -508,7 +511,7 @@ describe('quitting swapfile dialog on startup stops TUI properly', function() second %s /^ \zssecond$/ third %s /^ \zsthird$/]]):format(testfile, testfile, testfile) ) - local chan = fn.termopen({ + local chan = fn.jobstart({ nvim_prog, '-u', 'NONE', @@ -522,6 +525,7 @@ describe('quitting swapfile dialog on startup stops TUI properly', function() 'set tags=' .. otherfile, '-tsecond', }, { + term = true, env = { VIMRUNTIME = os.getenv('VIMRUNTIME') }, }) retry(nil, nil, function() diff --git a/test/functional/legacy/window_cmd_spec.lua b/test/functional/legacy/window_cmd_spec.lua index d68c780f49..b58bf0bf43 100644 --- a/test/functional/legacy/window_cmd_spec.lua +++ b/test/functional/legacy/window_cmd_spec.lua @@ -120,7 +120,7 @@ describe('splitkeep', function() row = 0, col = 0, })) - vim.cmd("call termopen([&sh, &shcf, 'true'], { 'on_exit': 'C2' })") + vim.cmd("call jobstart([&sh, &shcf, 'true'], { 'term': v:true, 'on_exit': 'C2' })") end })]]) feed('j') diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua index edb4c928c1..e209ed9025 100644 --- a/test/functional/terminal/buffer_spec.lua +++ b/test/functional/terminal/buffer_spec.lua @@ -378,7 +378,7 @@ describe(':terminal buffer', function() }, exec_lua('return _G.input')) end) - it('no heap-buffer-overflow when using termopen(echo) #3161', function() + it('no heap-buffer-overflow when using jobstart("echo",{term=true}) #3161', function() local testfilename = 'Xtestfile-functional-terminal-buffers_spec' write_file(testfilename, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaa') finally(function() @@ -387,8 +387,8 @@ describe(':terminal buffer', function() feed_command('edit ' .. testfilename) -- Move cursor away from the beginning of the line feed('$') - -- Let termopen() modify the buffer - feed_command('call termopen("echo")') + -- Let jobstart(…,{term=true}) modify the buffer + feed_command([[call jobstart("echo", {'term':v:true})]]) assert_alive() feed_command('bdelete!') end) @@ -411,7 +411,7 @@ describe(':terminal buffer', function() it('handles split UTF-8 sequences #16245', function() local screen = Screen.new(50, 7) - fn.termopen({ testprg('shell-test'), 'UTF-8' }) + fn.jobstart({ testprg('shell-test'), 'UTF-8' }, { term = true }) screen:expect([[ ^å | ref: å̲ | @@ -669,7 +669,7 @@ if is_os('win') then end) end -describe('termopen()', function() +describe('termopen() (deprecated alias to `jobstart(…,{term=true})`)', function() before_each(clear) it('disallowed when textlocked and in cmdwin buffer', function() diff --git a/test/functional/terminal/channel_spec.lua b/test/functional/terminal/channel_spec.lua index 9912c1ff7b..bb97411f43 100644 --- a/test/functional/terminal/channel_spec.lua +++ b/test/functional/terminal/channel_spec.lua @@ -75,8 +75,8 @@ describe('terminal channel is closed and later released if', function() eq(chans - 1, eval('len(nvim_list_chans())')) end) - it('opened by termopen(), exited, and deleted by pressing a key', function() - command([[let id = termopen('echo')]]) + it('opened by jobstart(…,{term=true}), exited, and deleted by pressing a key', function() + command([[let id = jobstart('echo',{'term':v:true})]]) local chans = eval('len(nvim_list_chans())') -- wait for process to exit screen:expect({ any = '%[Process exited 0%]' }) @@ -96,8 +96,8 @@ describe('terminal channel is closed and later released if', function() end) -- This indirectly covers #16264 - it('opened by termopen(), exited, and deleted by :bdelete', function() - command([[let id = termopen('echo')]]) + it('opened by jobstart(…,{term=true}), exited, and deleted by :bdelete', function() + command([[let id = jobstart('echo', {'term':v:true})]]) local chans = eval('len(nvim_list_chans())') -- wait for process to exit screen:expect({ any = '%[Process exited 0%]' }) @@ -124,7 +124,7 @@ it('chansend sends lines to terminal channel in proper order', function() screen._default_attr_ids = nil local shells = is_os('win') and { 'cmd.exe', 'pwsh.exe -nop', 'powershell.exe -nop' } or { 'sh' } for _, sh in ipairs(shells) do - command([[let id = termopen(']] .. sh .. [[')]]) + command([[let id = jobstart(']] .. sh .. [[', {'term':v:true})]]) command([[call chansend(id, ['echo "hello"', 'echo "world"', ''])]]) screen:expect { any = [[echo "hello".*echo "world"]], @@ -149,7 +149,7 @@ describe('no crash when TermOpen autocommand', function() }) end) - it('processes job exit event when using termopen()', function() + it('processes job exit event when using jobstart(…,{term=true})', function() command([[autocmd TermOpen * call input('')]]) async_meths.nvim_command('terminal foobar') screen:expect { @@ -179,7 +179,7 @@ describe('no crash when TermOpen autocommand', function() assert_alive() end) - it('wipes buffer and processes events when using termopen()', function() + it('wipes buffer and processes events when using jobstart(…,{term=true})', function() command([[autocmd TermOpen * bwipe! | call input('')]]) async_meths.nvim_command('terminal foobar') screen:expect { diff --git a/test/functional/terminal/clipboard_spec.lua b/test/functional/terminal/clipboard_spec.lua index 4a1a0e29fd..f0ce407eaa 100644 --- a/test/functional/terminal/clipboard_spec.lua +++ b/test/functional/terminal/clipboard_spec.lua @@ -56,7 +56,7 @@ describe(':terminal', function() return string.format('\027]52;;%s\027\\', arg) end - fn.termopen({ testprg('shell-test'), '-t', osc52(encoded) }) + fn.jobstart({ testprg('shell-test'), '-t', osc52(encoded) }, { term = true }) retry(nil, 1000, function() eq(text, exec_lua([[ return vim.g.clipboard_data ]])) diff --git a/test/functional/terminal/cursor_spec.lua b/test/functional/terminal/cursor_spec.lua index d4d08fb24c..3583fa0850 100644 --- a/test/functional/terminal/cursor_spec.lua +++ b/test/functional/terminal/cursor_spec.lua @@ -242,7 +242,7 @@ describe(':terminal cursor', function() feed([[]]) command('set statusline=~~~') command('new') - call('termopen', { testprg('tty-test') }) + call('jobstart', { testprg('tty-test') }, { term = true }) feed('i') screen:expect({ grid = [[ diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua index 5ebe7bd4fc..5224d322d3 100644 --- a/test/functional/terminal/ex_terminal_spec.lua +++ b/test/functional/terminal/ex_terminal_spec.lua @@ -175,7 +175,7 @@ local function test_terminal_with_fake_shell(backslash) api.nvim_set_option_value('shellxquote', '', {}) -- win: avoid extra quotes end) - it('with no argument, acts like termopen()', function() + it('with no argument, acts like jobstart(…,{term=true})', function() command('autocmd! nvim_terminal TermClose') feed_command('terminal') screen:expect([[ @@ -196,7 +196,7 @@ local function test_terminal_with_fake_shell(backslash) ]]) end) - it("with no argument, but 'shell' has arguments, acts like termopen()", function() + it("with no argument, but 'shell' has arguments, acts like jobstart(…,{term=true})", function() api.nvim_set_option_value('shell', shell_path .. ' INTERACT', {}) feed_command('terminal') screen:expect([[ diff --git a/test/functional/terminal/highlight_spec.lua b/test/functional/terminal/highlight_spec.lua index 5ed3c72b72..c43d139f70 100644 --- a/test/functional/terminal/highlight_spec.lua +++ b/test/functional/terminal/highlight_spec.lua @@ -33,7 +33,7 @@ describe(':terminal highlight', function() [12] = { bold = true, underdouble = true }, [13] = { italic = true, undercurl = true }, }) - command(("enew | call termopen(['%s'])"):format(testprg('tty-test'))) + command(("enew | call jobstart(['%s'], {'term':v:true})"):format(testprg('tty-test'))) feed('i') screen:expect([[ tty ready | @@ -150,7 +150,7 @@ it(':terminal highlight has lower precedence than editor #9964', function() }, }) -- Child nvim process in :terminal (with cterm colors). - fn.termopen({ + fn.jobstart({ nvim_prog_abs(), '-n', '-u', @@ -163,6 +163,7 @@ it(':terminal highlight has lower precedence than editor #9964', function() '+norm! ichild nvim', '+norm! oline 2', }, { + term = true, env = { VIMRUNTIME = os.getenv('VIMRUNTIME'), }, @@ -200,7 +201,7 @@ it('CursorLine and CursorColumn work in :terminal buffer in Normal mode', functi [4] = { background = Screen.colors.Grey90, reverse = true }, [5] = { background = Screen.colors.Red }, }) - command(("enew | call termopen(['%s'])"):format(testprg('tty-test'))) + command(("enew | call jobstart(['%s'], {'term':v:true})"):format(testprg('tty-test'))) screen:expect([[ ^tty ready | |*6 @@ -318,7 +319,7 @@ describe(':terminal highlight forwarding', function() [2] = { { fg_indexed = true, foreground = tonumber('0xe0e000') }, { foreground = 3 } }, [3] = { { foreground = tonumber('0xff8000') }, {} }, }) - command(("enew | call termopen(['%s'])"):format(testprg('tty-test'))) + command(("enew | call jobstart(['%s'], {'term':v:true})"):format(testprg('tty-test'))) feed('i') screen:expect([[ tty ready | @@ -364,7 +365,7 @@ describe(':terminal highlight with custom palette', function() [9] = { bold = true }, }) api.nvim_set_var('terminal_color_3', '#123456') - command(("enew | call termopen(['%s'])"):format(testprg('tty-test'))) + command(("enew | call jobstart(['%s'], {'term':v:true})"):format(testprg('tty-test'))) feed('i') screen:expect([[ tty ready | diff --git a/test/functional/terminal/scrollback_spec.lua b/test/functional/terminal/scrollback_spec.lua index 0de7873200..804c5367eb 100644 --- a/test/functional/terminal/scrollback_spec.lua +++ b/test/functional/terminal/scrollback_spec.lua @@ -355,7 +355,9 @@ describe(':terminal prints more lines than the screen height and exits', functio it('will push extra lines to scrollback', function() clear() local screen = Screen.new(30, 7, { rgb = false }) - command(("call termopen(['%s', '10']) | startinsert"):format(testprg('tty-test'))) + command( + ("call jobstart(['%s', '10'], {'term':v:true}) | startinsert"):format(testprg('tty-test')) + ) screen:expect([[ line6 | line7 | @@ -623,7 +625,7 @@ describe('pending scrollback line handling', function() local bufnr = vim.api.nvim_create_buf(false, true) local args = ... vim.api.nvim_buf_call(bufnr, function() - vim.fn.termopen(args) + vim.fn.jobstart(args, { term = true }) end) vim.api.nvim_win_set_buf(0, bufnr) vim.cmd('startinsert') diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 832bacb534..de92aefd5b 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -2114,7 +2114,7 @@ describe('TUI', function() [5] = { bold = true, reverse = true }, [6] = { foreground = Screen.colors.White, background = Screen.colors.DarkGreen }, }) - fn.termopen({ + fn.jobstart({ nvim_prog, '--clean', '--cmd', @@ -2124,6 +2124,7 @@ describe('TUI', function() '--cmd', 'let start = reltime() | while v:true | if reltimefloat(reltime(start)) > 2 | break | endif | endwhile', }, { + term = true, env = { VIMRUNTIME = os.getenv('VIMRUNTIME'), }, @@ -2146,7 +2147,7 @@ describe('TUI', function() for _, guicolors in ipairs({ 'notermguicolors', 'termguicolors' }) do it('has no black flicker when clearing regions during startup with ' .. guicolors, function() local screen = Screen.new(50, 10) - fn.termopen({ + fn.jobstart({ nvim_prog, '--clean', '--cmd', @@ -2154,6 +2155,7 @@ describe('TUI', function() '--cmd', 'sleep 10', }, { + term = true, env = { VIMRUNTIME = os.getenv('VIMRUNTIME'), }, diff --git a/test/functional/testterm.lua b/test/functional/testterm.lua index 7ae28dce69..17209d947e 100644 --- a/test/functional/testterm.lua +++ b/test/functional/testterm.lua @@ -141,7 +141,7 @@ function M.setup_screen(extra_rows, cmd, cols, env, screen_opts) }) api.nvim_command('enew') - api.nvim_call_function('termopen', { cmd, env and { env = env } or nil }) + api.nvim_call_function('jobstart', { cmd, { term = true, env = (env and env or nil) } }) api.nvim_input('') local vim_errmsg = api.nvim_eval('v:errmsg') if vim_errmsg and '' ~= vim_errmsg then diff --git a/test/functional/ui/hlstate_spec.lua b/test/functional/ui/hlstate_spec.lua index 745ad70efe..e10c79fa48 100644 --- a/test/functional/ui/hlstate_spec.lua +++ b/test/functional/ui/hlstate_spec.lua @@ -224,7 +224,7 @@ describe('ext_hlstate detailed highlights', function() [6] = { { foreground = tonumber('0x40ffff'), fg_indexed = true }, { 5, 1 } }, [7] = { {}, { { hi_name = 'MsgArea', ui_name = 'MsgArea', kind = 'ui' } } }, }) - command(("enew | call termopen(['%s'])"):format(testprg('tty-test'))) + command(("enew | call jobstart(['%s'],{'term':v:true})"):format(testprg('tty-test'))) screen:expect([[ ^tty ready | | diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index 287db81a12..ce30fbdc7e 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -2079,7 +2079,7 @@ describe('ui/msg_puts_printf', function() ) cmd = cmd .. '"' .. nvim_prog .. '" -u NONE -i NONE -Es -V1' - command([[call termopen(']] .. cmd .. [[')]]) + command([[call jobstart(']] .. cmd .. [[',{'term':v:true})]]) screen:expect([[ ^Exモードに入ります。ノー | マルモードに戻るには "vis| diff --git a/test/functional/ui/title_spec.lua b/test/functional/ui/title_spec.lua index aa9ac3f5b5..2de1e71457 100644 --- a/test/functional/ui/title_spec.lua +++ b/test/functional/ui/title_spec.lua @@ -80,7 +80,7 @@ describe('title', function() it('is updated in Terminal mode', function() api.nvim_set_option_value('title', true, {}) api.nvim_set_option_value('titlestring', '(%{mode(1)}) | nvim', {}) - fn.termopen({ n.testprg('shell-test'), 'INTERACT' }) + fn.jobstart({ n.testprg('shell-test'), 'INTERACT' }, { term = true }) screen:expect(function() eq('(nt) | nvim', screen.title) end) diff --git a/test/old/testdir/runnvim.vim b/test/old/testdir/runnvim.vim index 578614c8a1..f5945aeaee 100644 --- a/test/old/testdir/runnvim.vim +++ b/test/old/testdir/runnvim.vim @@ -8,6 +8,7 @@ function s:logger.on_exit(id, data, event) endfunction let s:logger.env = #{VIMRUNTIME: $VIMRUNTIME} +let s:logger.term = v:true " Replace non-printable chars by special sequence, or "<%x>". let s:escaped_char = {"\n": '\n', "\r": '\r', "\t": '\t'} @@ -25,7 +26,7 @@ function Main() set lines=25 set columns=80 enew - let job = termopen(args, s:logger) + let job = jobstart(args, s:logger) let results = jobwait([job], 5 * 60 * 1000) " TODO(ZyX-I): Get colors let screen = getline(1, '$') From f7890bb005aab81833f67b7f4e8050c40cd2dc92 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 18 Dec 2024 16:23:24 +0100 Subject: [PATCH 2/4] =?UTF-8?q?fixup!=20feat(jobs):=20jobstart(=E2=80=A6,{?= =?UTF-8?q?term:true}),=20deprecate=20termopen()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- runtime/doc/news.txt | 1 + src/nvim/eval/deprecated.c | 1 - src/nvim/eval/deprecated.h | 2 +- src/nvim/eval/funcs.c | 13 ++++++++++--- test/functional/core/job_spec.lua | 32 ++++++++++++++++++++++++------- 5 files changed, 37 insertions(+), 12 deletions(-) diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 73704a143c..a977794230 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -293,6 +293,7 @@ TERMINAL means that the |TermCursorNC| highlight group is no longer supported: an unfocused terminal window will have no cursor at all (so there is nothing to highlight). +• |jobstart()| gained the "term" flag. TREESITTER diff --git a/src/nvim/eval/deprecated.c b/src/nvim/eval/deprecated.c index 0874e4ae76..67c254dac9 100644 --- a/src/nvim/eval/deprecated.c +++ b/src/nvim/eval/deprecated.c @@ -42,4 +42,3 @@ void f_termopen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) tv_dict_free(argvars[1].vval.v_dict); } } - diff --git a/src/nvim/eval/deprecated.h b/src/nvim/eval/deprecated.h index 69882695e7..04cf60218c 100644 --- a/src/nvim/eval/deprecated.h +++ b/src/nvim/eval/deprecated.h @@ -2,8 +2,8 @@ #include // for true -#include "nvim/message.h" // for semsg #include "nvim/eval/typval_defs.h" // IWYU pragma: keep +#include "nvim/message.h" // for semsg #include "nvim/types_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index e3fb2e7279..b36fd15516 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -3842,7 +3842,7 @@ static const char *required_env_vars[] = { }; dict_T *create_environment(const dictitem_T *job_env, const bool clear_env, const bool pty, - const char * const pty_term_name) + const char * const pty_term_name) { dict_T *env = tv_dict_alloc(); @@ -3992,6 +3992,14 @@ void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } } + dictitem_T *const job_term = tv_dict_find(job_opts, S_LEN("term")); + if (job_term && VAR_BOOL != job_term->di_tv.v_type) { + // Restrict "term" field to boolean, in case we want to allow buffer numbers in the future. + semsg(_(e_invarg2), "'term' must Boolean"); + shell_free_argv(argv); + return; + } + if (pty && rpc) { semsg(_(e_invarg2), "job cannot have both 'pty' and 'rpc' options set"); shell_free_argv(argv); @@ -4111,8 +4119,7 @@ void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) channel_terminal_open(curbuf, chan); channel_create_event(chan, NULL); channel_decref(chan); - } - else if (chan) { + } else if (chan) { channel_create_event(chan, NULL); } } diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index 20b7ab0f13..7820ee7e1e 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -65,6 +65,31 @@ describe('jobs', function() ]]) end) + it('validation', function() + matches( + "E475: Invalid argument: job cannot have both 'pty' and 'rpc' options set", + pcall_err(command, "let j = jobstart(['cat', '-'], { 'pty': v:true, 'rpc': v:true })") + ) + matches( + 'E475: Invalid argument: expected valid directory', + pcall_err(command, "let j = jobstart(['cat', '-'], { 'cwd': 1 })") + ) + matches( + 'E475: Invalid argument: expected valid directory', + pcall_err(command, "let j = jobstart(['cat', '-'], { 'cwd': 'bogusssssss/bogus' })") + ) + matches( + "E475: Invalid argument: 'term' must Boolean", + pcall_err(command, "let j = jobstart(['cat', '-'], { 'term': 'bogus' })") + ) + matches( + "E475: Invalid argument: 'term' must Boolean", + pcall_err(command, "let j = jobstart(['cat', '-'], { 'term': 1 })") + ) + command("let j = jobstart(['cat', '-'], { 'term': v:true })") + command("let j = jobstart(['cat', '-'], { 'term': v:false })") + end) + it('must specify env option as a dict', function() command('let g:job_opts.env = v:true') local _, err = pcall(function() @@ -969,13 +994,6 @@ describe('jobs', function() eq({ 'notification', 'exit', { 0, 143 } }, next_msg()) end) - it('cannot have both rpc and pty options', function() - command('let g:job_opts.pty = v:true') - command('let g:job_opts.rpc = v:true') - local _, err = pcall(command, "let j = jobstart(['cat', '-'], g:job_opts)") - matches("E475: Invalid argument: job cannot have both 'pty' and 'rpc' options set", err) - end) - it('does not crash when repeatedly failing to start shell', function() source([[ set shell=nosuchshell From e340b4e113e0e3d82c228ba9f4cd4520f2c58c0d Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 18 Dec 2024 16:28:55 +0100 Subject: [PATCH 3/4] =?UTF-8?q?fixup!=20fixup!=20feat(jobs):=20jobstart(?= =?UTF-8?q?=E2=80=A6,{term:true}),=20deprecate=20termopen()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/nvim/eval/funcs.c | 2 +- test/functional/core/job_spec.lua | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index b36fd15516..bff1c11540 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -3995,7 +3995,7 @@ void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) dictitem_T *const job_term = tv_dict_find(job_opts, S_LEN("term")); if (job_term && VAR_BOOL != job_term->di_tv.v_type) { // Restrict "term" field to boolean, in case we want to allow buffer numbers in the future. - semsg(_(e_invarg2), "'term' must Boolean"); + semsg(_(e_invarg2), "'term' must be Boolean"); shell_free_argv(argv); return; } diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index 7820ee7e1e..c99b5cb15a 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -79,11 +79,11 @@ describe('jobs', function() pcall_err(command, "let j = jobstart(['cat', '-'], { 'cwd': 'bogusssssss/bogus' })") ) matches( - "E475: Invalid argument: 'term' must Boolean", + "E475: Invalid argument: 'term' must be Boolean", pcall_err(command, "let j = jobstart(['cat', '-'], { 'term': 'bogus' })") ) matches( - "E475: Invalid argument: 'term' must Boolean", + "E475: Invalid argument: 'term' must be Boolean", pcall_err(command, "let j = jobstart(['cat', '-'], { 'term': 1 })") ) command("let j = jobstart(['cat', '-'], { 'term': v:true })") From 045b1d7f90cb116b506c6b43954b6337488f9af5 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 18 Dec 2024 16:35:31 +0100 Subject: [PATCH 4/4] =?UTF-8?q?fixup!=20fixup!=20fixup!=20feat(jobs):=20jo?= =?UTF-8?q?bstart(=E2=80=A6,{term:true}),=20deprecate=20termopen()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/nvim/eval/deprecated.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/nvim/eval/deprecated.h b/src/nvim/eval/deprecated.h index 04cf60218c..b870403aa4 100644 --- a/src/nvim/eval/deprecated.h +++ b/src/nvim/eval/deprecated.h @@ -3,7 +3,6 @@ #include // for true #include "nvim/eval/typval_defs.h" // IWYU pragma: keep -#include "nvim/message.h" // for semsg #include "nvim/types_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS