diff options
34 files changed, 448 insertions, 255 deletions
diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim index 58d3d4550f..848fa401f1 100644 --- a/runtime/autoload/provider/clipboard.vim +++ b/runtime/autoload/provider/clipboard.vim @@ -158,7 +158,7 @@ function! provider#clipboard#Executable() abort let s:copy['*'] = s:copy['+'] let s:paste['*'] = s:paste['+'] return 'termux-clipboard' - elseif !empty($TMUX) && executable('tmux') + elseif executable('tmux') && (!empty($TMUX) || 0 == jobwait([jobstart(['tmux', 'list-buffers'])], 2000)[0]) let tmux_v = v:lua.vim.version.parse(system(['tmux', '-V'])) if !empty(tmux_v) && !v:lua.vim.version.lt(tmux_v, [3,2,0]) let s:copy['+'] = ['tmux', 'load-buffer', '-w', '-'] diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index c5dabeb551..9cb8f72348 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -1114,7 +1114,7 @@ nvim_open_term({buffer}, {opts}) *nvim_open_term()* Open a terminal instance in a buffer By default (and currently the only option) the terminal will not be - connected to an external process. Instead, input send on the channel will + connected to an external process. Instead, input sent on the channel will be echoed directly by the terminal. This is useful to display ANSI terminal sequences returned as part of a rpc message, or similar. @@ -1125,6 +1125,17 @@ nvim_open_term({buffer}, {opts}) *nvim_open_term()* |nvim_chan_send()| can be called immediately to process sequences in a virtual terminal having the intended size. + Example: this `TermHl` command can be used to display and highlight raw + ANSI termcodes, so you can use Nvim as a "scrollback pager" (for terminals + like kitty): *terminal-scrollback-pager* >lua + vim.api.nvim_create_user_command('TermHl', function() + local b = vim.api.nvim_create_buf(false, true) + local chan = vim.api.nvim_open_term(b, {}) + vim.api.nvim_chan_send(chan, table.concat(vim.api.nvim_buf_get_lines(0, 0, -1, false), '\n')) + vim.api.nvim_win_set_buf(0, b) + end, { desc = 'Highlights ANSI termcodes in curbuf' }) +< + Attributes: ~ not allowed when |textlock| is active diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 585db21a0b..304e63fd95 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -5443,7 +5443,7 @@ jobwait({jobs} [, {timeout}]) *jobwait()* • {timeout} (`integer?`) Return: ~ - (`any`) + (`integer[]`) join({list} [, {sep}]) *join()* Join the items in {list} together into one String. @@ -7986,7 +7986,7 @@ rpcnotify({channel}, {event} [, {args}...]) *rpcnotify()* Parameters: ~ • {channel} (`integer`) • {event} (`string`) - • {args} (`any?`) + • {...} (`any`) Return: ~ (`any`) @@ -8001,7 +8001,7 @@ rpcrequest({channel}, {method} [, {args}...]) *rpcrequest()* Parameters: ~ • {channel} (`integer`) • {method} (`string`) - • {args} (`any?`) + • {...} (`any`) Return: ~ (`any`) @@ -10234,6 +10234,7 @@ str2list({string} [, {utf8}]) *str2list()* and exists only for backwards-compatibility. With UTF-8 composing characters are handled properly: >vim echo str2list("á") " returns [97, 769] +< Parameters: ~ • {string} (`string`) @@ -11991,7 +11992,7 @@ winlayout([{tabnr}]) *winlayout()* • {tabnr} (`integer?`) Return: ~ - (`any`) + (`any[]`) winline() *winline()* The result is a Number, which is the screen line of the cursor diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt index c6ca5e5ce9..ab9c0b2ce8 100644 --- a/runtime/doc/deprecated.txt +++ b/runtime/doc/deprecated.txt @@ -64,6 +64,7 @@ LSP • `client.is_stopped()` Use |Client:is_stopped()| instead. • `client.supports_method()` Use |Client:supports_method()| instead. • `client.on_attach()` Use |Client:on_attach()| instead. +• `vim.lsp.start_client()` Use |vim.lsp.start()| instead. ------------------------------------------------------------------------------ DEPRECATED IN 0.10 *deprecated-0.10* diff --git a/runtime/doc/dev_arch.txt b/runtime/doc/dev_arch.txt index 1cb3b9ad67..2be6221117 100644 --- a/runtime/doc/dev_arch.txt +++ b/runtime/doc/dev_arch.txt @@ -46,11 +46,11 @@ Remember to bump NVIM_API_LEVEL if it wasn't already during this development cycle. Other references: -* |msgpack-rpc| -* |ui| -* https://github.com/neovim/neovim/pull/3246 -* https://github.com/neovim/neovim/pull/18375 -* https://github.com/neovim/neovim/pull/21605 +- |msgpack-rpc| +- |ui| +- https://github.com/neovim/neovim/pull/3246 +- https://github.com/neovim/neovim/pull/18375 +- https://github.com/neovim/neovim/pull/21605 diff --git a/runtime/doc/develop.txt b/runtime/doc/develop.txt index a61c569a67..da64475465 100644 --- a/runtime/doc/develop.txt +++ b/runtime/doc/develop.txt @@ -300,7 +300,7 @@ vim.paste in runtime/lua/vim/_editor.lua like this: > --- @returns false if client should cancel the paste. -LUA STDLIB DESIGN GUIDELINES *dev-lua* +STDLIB DESIGN GUIDELINES *dev-lua* See also |dev-naming|. @@ -337,7 +337,7 @@ preference): way. Advantage is that propagation happens for free and it's harder to accidentally swallow errors. (E.g. using `uv_handle/pipe:write()` without checking return values is common.) -4. `on_error` parameter +4. `on_error` callback - For async and "visitors" traversing a graph, where many errors may be collected while work continues. 5. `vim.notify` (sometimes with optional `opts.silent` (async, visitors ^)) @@ -434,7 +434,9 @@ Use existing common {verb} names (actions) if possible: - eval: Evaluates an expression - exec: Executes code, may return a result - fmt: Formats - - get: Gets things (often by a query) + - get: Gets things. Two variants (overloads): + 1. `get<T>(id: int): T` returns one item. + 2. `get<T>(filter: dict): T[]` returns a list. - inspect: Presents a high-level, often interactive, view - is_enabled: Checks if functionality is enabled. - open: Opens something (a buffer, window, …) diff --git a/runtime/doc/gui.txt b/runtime/doc/gui.txt index 21f1ba8241..85a0df03f9 100644 --- a/runtime/doc/gui.txt +++ b/runtime/doc/gui.txt @@ -20,8 +20,9 @@ features, whereas help tags with the "ui-" prefix refer to the |ui-protocol|. Nvim provides a default, builtin UI (the |TUI|), but there are many other (third-party) GUIs that you can use instead: -- Firenvim (Nvim in your web browser!) https://github.com/glacambre/firenvim + *vscode* - vscode-neovim (Nvim in VSCode!) https://github.com/vscode-neovim/vscode-neovim +- Firenvim (Nvim in your web browser!) https://github.com/glacambre/firenvim - Neovide https://neovide.dev/ - Goneovim https://github.com/akiyosi/goneovim - Nvy https://github.com/RMichelsen/Nvy diff --git a/runtime/doc/intro.txt b/runtime/doc/intro.txt index d099c29bdb..85169fcc17 100644 --- a/runtime/doc/intro.txt +++ b/runtime/doc/intro.txt @@ -49,17 +49,19 @@ For more information try one of these: ============================================================================== Nvim on the interwebs *internet* - *www* *distribution* *download* - - Nvim home page: https://neovim.io/ - Downloads: https://github.com/neovim/neovim/releases - Vim FAQ: https://vimhelp.org/vim_faq.txt.html - - - *bugs* *bug-report* -Report bugs and request features here: -https://github.com/neovim/neovim/issues - + *www* *distribution* +- Nvim home page: https://neovim.io/ +- Vim FAQ: https://vimhelp.org/vim_faq.txt.html + + *download* *upgrade* *ubuntu* +To install or upgrade Nvim, you can... +- Download a pre-built archive: + https://github.com/neovim/neovim/releases +- Use your system package manager: + https://github.com/neovim/neovim/blob/master/INSTALL.md#install-from-package + + *bugs* *bug-report* *feature-request* +Report bugs and request features here: https://github.com/neovim/neovim/issues Be brief, yet complete. Always give a reproducible example and try to find out which settings or other things trigger the bug. diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index e311831bd3..2654d7f14f 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -841,12 +841,11 @@ start({config}, {opts}) *vim.lsp.start()* }) < - See |vim.lsp.start_client()| for all available options. The most important + See |vim.lsp.ClientConfig| for all available options. The most important are: • `name` arbitrary name for the LSP client. Should be unique per language server. - • `cmd` command string[] or function, described at - |vim.lsp.start_client()|. + • `cmd` command string[] or function. • `root_dir` path to the project root. By default this is used to decide if an existing client should be re-used. The example above uses |vim.fs.root()| to detect the root by traversing the file system upwards @@ -868,7 +867,7 @@ start({config}, {opts}) *vim.lsp.start()* Parameters: ~ • {config} (`vim.lsp.ClientConfig`) Configuration for the server. See |vim.lsp.ClientConfig|. - • {opts} (`table?`) Optional keyword arguments + • {opts} (`table?`) Optional keyword arguments. • {reuse_client}? (`fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean`) Predicate used to decide if a client should be re-used. @@ -876,25 +875,15 @@ start({config}, {opts}) *vim.lsp.start()* re-uses a client if name and root_dir matches. • {bufnr}? (`integer`) Buffer handle to attach to if starting or re-using a client (0 for current). + • {attach}? (`boolean`) Whether to attach the client to a + buffer (default true). If set to `false`, `reuse_client` + and `bufnr` will be ignored. • {silent}? (`boolean`) Suppress error reporting if the LSP server fails to start (default false). Return: ~ (`integer?`) client_id -start_client({config}) *vim.lsp.start_client()* - Starts and initializes a client with the given configuration. - - Parameters: ~ - • {config} (`vim.lsp.ClientConfig`) Configuration for the server. See - |vim.lsp.ClientConfig|. - - Return (multiple): ~ - (`integer?`) client_id |vim.lsp.get_client_by_id()| Note: client may - not be fully initialized. Use `on_init` to do any actions once the - client has been initialized. - (`string?`) Error message, if any - status() *vim.lsp.status()* Consumes the latest progress messages from all clients and formats them as a string. Empty if there are no clients or if no new messages @@ -968,8 +957,7 @@ Lua module: vim.lsp.client *lsp-client* server. • {config} (`vim.lsp.ClientConfig`) copy of the table that was passed by the user to - |vim.lsp.start_client()|. See - |vim.lsp.ClientConfig|. + |vim.lsp.start()|. See |vim.lsp.ClientConfig|. • {server_capabilities} (`lsp.ServerCapabilities?`) Response from the server sent on `initialize` describing the server's capabilities. @@ -1093,7 +1081,7 @@ Lua module: vim.lsp.client *lsp-client* • {commands}? (`table<string,fun(command: lsp.Command, ctx: table)>`) Table that maps string of clientside commands to user-defined functions. Commands passed to - start_client take precedence over the global + `start()` take precedence over the global command registry. Each key must be a unique command name, and the value is a function which is called if any LSP action (code action, code @@ -1122,9 +1110,9 @@ Lua module: vim.lsp.client *lsp-client* Callback invoked before the LSP "initialize" phase, where `params` contains the parameters being sent to the server and `config` is the - config that was passed to - |vim.lsp.start_client()|. You can use this to - modify parameters before they are sent. + config that was passed to |vim.lsp.start()|. You + can use this to modify parameters before they + are sent. • {on_init}? (`elem_or_list<fun(client: vim.lsp.Client, initialize_result: lsp.InitializeResult)>`) Callback invoked after LSP "initialize", where `result` is a table of `capabilities` and @@ -2296,7 +2284,7 @@ connect({host_or_path}, {port}) *vim.lsp.rpc.connect()* • a host and port via TCP Return a function that can be passed to the `cmd` field for - |vim.lsp.start_client()| or |vim.lsp.start()|. + |vim.lsp.start()|. Parameters: ~ • {host_or_path} (`string`) host to connect to or path to a pipe/domain diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 047e263768..48fa595a53 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -17,9 +17,9 @@ get an idea of what lurks beneath: >vim :lua vim.print(package.loaded) Nvim includes a "standard library" |lua-stdlib| for Lua. It complements the -"editor stdlib" (|builtin-functions| and |Ex-commands|) and the |API|, all of -which can be used from Lua code (|lua-vimscript| |vim.api|). Together these -"namespaces" form the Nvim programming interface. +"editor stdlib" (|vimscript-functions| + |Ex-commands|) and the |API|, all of +which can be used from Lua code (|lua-vimscript| |vim.api|). These three +namespaces form the Nvim programming interface. Lua plugins and user config are automatically discovered and loaded, just like Vimscript. See |lua-guide| for practical guidance. diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index 11048aee30..34cf309576 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -12,7 +12,7 @@ manual. Type |gO| to see the table of contents. ============================================================================== -1. Key mapping *key-mapping* *mapping* *macro* +1. Key mapping *keybind* *key-mapping* *mapping* *macro* Key mapping is used to change the meaning of typed keys. The most common use is to define a sequence of commands for a function key. Example: > diff --git a/runtime/doc/nvim.txt b/runtime/doc/nvim.txt index 86e344c654..8593511dbc 100644 --- a/runtime/doc/nvim.txt +++ b/runtime/doc/nvim.txt @@ -6,21 +6,23 @@ Nvim *nvim* *neovim* *nvim-intro* -Nvim is based on Vim by Bram Moolenaar. +Nvim is based on Vim by Bram Moolenaar. Nvim is emphatically a fork of Vim, +not a clone: compatibility with Vim (especially editor and Vimscript features, +except |Vim9script|) is maintained where possible. See |vim-differences| for +the complete reference. -If you already use Vim see |nvim-from-vim| for a quickstart. -If you are new to Vim, try the 30-minute tutorial: >vim +If you already use Vim, see |nvim-from-vim| for a quickstart. If you just +installed Nvim and have never used it before, watch this 10-minute +video: https://youtu.be/TQn2hJeHQbM . - :Tutor<Enter> - -Nvim is emphatically a fork of Vim, not a clone: compatibility with Vim -(especially editor and Vimscript features) is maintained where possible. See -|vim-differences| for the complete reference of differences from Vim. +To learn how to use Vim in 30 minutes, try the tutorial: >vim + :Tutor<Enter> +< Type |gO| to see the table of contents. ============================================================================== -Transitioning from Vim *nvim-from-vim* +Transitioning from Vim *nvim-from-vim* 1. To start the transition, create your |init.vim| (user config) file: >vim @@ -71,4 +73,20 @@ the same Nvim configuration on all of your machines, by creating source ~/.config/nvim/init.vim ============================================================================== +What next? *nvim-quickstart* + +If you are just trying out Nvim for a few minutes, and want to see the +extremes of what it can do, try one of these popular "extension packs" or +"distributions" (Note: Nvim is not affiliated with these projects, and does +not support them): + +- *kickstart* https://github.com/nvim-lua/kickstart.nvim +- *lazyvim* https://www.lazyvim.org/ +- *nvchad* https://nvchad.com/ + +However, in general, we recommend (eventually) taking time to learn Nvim from +its stock configuration, and incrementally setting options and adding plugins +to your |config| as you find an explicit need to do so. + +============================================================================== vim:tw=78:ts=8:et:ft=help:norl: diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt index 55eba484bc..77eddfd8e1 100644 --- a/runtime/doc/ui.txt +++ b/runtime/doc/ui.txt @@ -788,7 +788,7 @@ must handle. kind Name indicating the message kind: - "" (empty) Unknown (consider a feature-request: |bugs|) + "" (empty) Unknown (consider a |feature-request|) "confirm" |confirm()| or |:confirm| dialog "confirm_sub" |:substitute| confirm dialog |:s_c| "emsg" Error (|errors|, internal error, |:throw|, …) diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index d967e7c75b..5049439337 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -534,7 +534,8 @@ gO Show a filetype-specific, navigable "outline" of the current buffer. For example, in a |help| buffer this shows the table of contents. - Currently works in |help| and |:Man| buffers. + Works in |help| and |:Man| buffers, or any buffer with + an active |LSP| client (|lsp-defaults|). [N]gs *gs* *:sl* *:sleep* :[N]sl[eep] [N][m] Do nothing for [N] seconds, or [N] milliseconds if [m] diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 6e1a9adb83..8d2d672ce1 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -186,6 +186,9 @@ nvim_terminal: - 'textwidth' set to 0 - 'nowrap' - 'nolist' + - 'nonumber' + - 'norelativenumber' + - 'signcolumn' set to "no" - 'winhighlight' uses |hl-StatusLineTerm| and |hl-StatusLineTermNC| in place of |hl-StatusLine| and |hl-StatusLineNC| @@ -686,7 +689,7 @@ Cscope: https://github.com/dhananjaylatkar/cscope_maps.nvim Eval: -- Vim9script +- *Vim9script* (the Vim 9+ flavor of Vimscript) is not supported. - *cscope_connection()* - *err_teapot()* - *js_encode()* diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua index 6583cf48b3..ef83a3ccc3 100644 --- a/runtime/lua/vim/_defaults.lua +++ b/runtime/lua/vim/_defaults.lua @@ -492,6 +492,9 @@ do vim.bo.textwidth = 0 vim.wo[0][0].wrap = false vim.wo[0][0].list = false + vim.wo[0][0].number = false + vim.wo[0][0].relativenumber = false + vim.wo[0][0].signcolumn = 'no' -- This is gross. Proper list options support when? local winhl = vim.o.winhighlight diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index acd12b353d..d74ee11b46 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -1654,7 +1654,7 @@ function vim.api.nvim_notify(msg, log_level, opts) end --- Open a terminal instance in a buffer --- --- By default (and currently the only option) the terminal will not be ---- connected to an external process. Instead, input send on the channel +--- connected to an external process. Instead, input sent on the channel --- will be echoed directly by the terminal. This is useful to display --- ANSI terminal sequences returned as part of a rpc message, or similar. --- @@ -1665,6 +1665,18 @@ function vim.api.nvim_notify(msg, log_level, opts) end --- Then `nvim_chan_send()` can be called immediately to process sequences --- in a virtual terminal having the intended size. --- +--- Example: this `TermHl` command can be used to display and highlight raw ANSI termcodes, so you +--- can use Nvim as a "scrollback pager" (for terminals like kitty): [terminal-scrollback-pager]() +--- +--- ```lua +--- vim.api.nvim_create_user_command('TermHl', function() +--- local b = vim.api.nvim_create_buf(false, true) +--- local chan = vim.api.nvim_open_term(b, {}) +--- vim.api.nvim_chan_send(chan, table.concat(vim.api.nvim_buf_get_lines(0, 0, -1, false), '\n')) +--- vim.api.nvim_win_set_buf(0, b) +--- end, { desc = 'Highlights ANSI termcodes in curbuf' }) +--- ``` +--- --- @param buffer integer the buffer to use (expected to be empty) --- @param opts vim.api.keyset.open_term Optional parameters. --- - on_input: Lua callback for input sent, i e keypresses in terminal diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index f9b5d93a4b..e207f641de 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -4928,7 +4928,7 @@ function vim.fn.jobstop(id) end --- --- @param jobs integer[] --- @param timeout? integer ---- @return any +--- @return integer[] function vim.fn.jobwait(jobs, timeout) end --- Join the items in {list} together into one String. @@ -7244,9 +7244,9 @@ function vim.fn.round(expr) end --- --- @param channel integer --- @param event string ---- @param args? any +--- @param ... any --- @return any -function vim.fn.rpcnotify(channel, event, args) end +function vim.fn.rpcnotify(channel, event, ...) end --- Sends a request to {channel} to invoke {method} via --- |RPC| and blocks until a response is received. @@ -7256,9 +7256,9 @@ function vim.fn.rpcnotify(channel, event, args) end --- --- @param channel integer --- @param method string ---- @param args? any +--- @param ... any --- @return any -function vim.fn.rpcrequest(channel, method, args) end +function vim.fn.rpcrequest(channel, method, ...) end --- @deprecated --- Deprecated. Replace >vim @@ -9328,6 +9328,7 @@ function vim.fn.str2float(string, quoted) end --- and exists only for backwards-compatibility. --- With UTF-8 composing characters are handled properly: >vim --- echo str2list("á") " returns [97, 769] +--- < --- --- @param string string --- @param utf8? boolean @@ -10870,7 +10871,7 @@ function vim.fn.winheight(nr) end --- < --- --- @param tabnr? integer ---- @return any +--- @return any[] function vim.fn.winlayout(tabnr) end --- The result is a Number, which is the screen line of the cursor diff --git a/runtime/lua/vim/_system.lua b/runtime/lua/vim/_system.lua index ce5dbffeaa..c0a0570e13 100644 --- a/runtime/lua/vim/_system.lua +++ b/runtime/lua/vim/_system.lua @@ -47,15 +47,6 @@ local function close_handle(handle) end end ----@param state vim.SystemState -local function close_handles(state) - close_handle(state.handle) - close_handle(state.stdin) - close_handle(state.stdout) - close_handle(state.stderr) - close_handle(state.timer) -end - --- @class vim.SystemObj --- @field cmd string[] --- @field pid integer @@ -132,9 +123,7 @@ function SystemObj:write(data) -- (https://github.com/neovim/neovim/pull/17620#discussion_r820775616) stdin:write('', function() stdin:shutdown(function() - if stdin then - stdin:close() - end + close_handle(stdin) end) end) end @@ -146,25 +135,52 @@ function SystemObj:is_closing() return handle == nil or handle:is_closing() or false end ----@param output fun(err:string?, data: string?)|false ----@return uv.uv_stream_t? ----@return fun(err:string?, data: string?)? Handler -local function setup_output(output) - if output == nil then - return assert(uv.new_pipe(false)), nil +--- @param output? uv.read_start.callback|false +--- @param text? boolean +--- @return uv.uv_stream_t? pipe +--- @return uv.read_start.callback? handler +--- @return string[]? data +local function setup_output(output, text) + if output == false then + return end + local bucket --- @type string[]? + local handler --- @type uv.read_start.callback + if type(output) == 'function' then - return assert(uv.new_pipe(false)), output + handler = output + else + bucket = {} + handler = function(err, data) + if err then + error(err) + end + if text and data then + bucket[#bucket + 1] = data:gsub('\r\n', '\n') + else + bucket[#bucket + 1] = data + end + end end - assert(output == false) - return nil, nil + local pipe = assert(uv.new_pipe(false)) + + --- @type uv.read_start.callback + local function handler_with_close(err, data) + handler(err, data) + if data == nil then + pipe:read_stop() + pipe:close() + end + end + + return pipe, handler_with_close, bucket end ----@param input string|string[]|true|nil ----@return uv.uv_stream_t? ----@return string|string[]? +--- @param input? string|string[]|boolean +--- @return uv.uv_stream_t? +--- @return string|string[]? local function setup_input(input) if not input then return @@ -208,28 +224,6 @@ local function setup_env(env, clear_env) return renv end ---- @param stream uv.uv_stream_t ---- @param text? boolean ---- @param bucket string[] ---- @return fun(err: string?, data: string?) -local function default_handler(stream, text, bucket) - return function(err, data) - if err then - error(err) - end - if data ~= nil then - if text then - bucket[#bucket + 1] = data:gsub('\r\n', '\n') - else - bucket[#bucket + 1] = data - end - else - stream:read_stop() - stream:close() - end - end -end - local is_win = vim.fn.has('win32') == 1 local M = {} @@ -255,9 +249,9 @@ local function spawn(cmd, opts, on_exit, on_error) return handle, pid_or_err --[[@as integer]] end ----@param timeout integer ----@param cb fun() ----@return uv.uv_timer_t +--- @param timeout integer +--- @param cb fun() +--- @return uv.uv_timer_t local function timer_oneshot(timeout, cb) local timer = assert(uv.new_timer()) timer:start(timeout, 0, function() @@ -273,7 +267,12 @@ end --- @param signal integer --- @param on_exit fun(result: vim.SystemCompleted)? local function _on_exit(state, code, signal, on_exit) - close_handles(state) + close_handle(state.handle) + close_handle(state.stdin) + close_handle(state.timer) + + -- #30846: Do not close stdout/stderr here, as they may still have data to + -- read. They will be closed in uv.read_start on EOF. local check = assert(uv.new_check()) check:start(function() @@ -311,6 +310,15 @@ local function _on_exit(state, code, signal, on_exit) end) end +--- @param state vim.SystemState +local function _on_error(state) + close_handle(state.handle) + close_handle(state.stdin) + close_handle(state.stdout) + close_handle(state.stderr) + close_handle(state.timer) +end + --- Run a system command --- --- @param cmd string[] @@ -324,8 +332,8 @@ function M.run(cmd, opts, on_exit) opts = opts or {} - local stdout, stdout_handler = setup_output(opts.stdout) - local stderr, stderr_handler = setup_output(opts.stderr) + local stdout, stdout_handler, stdout_data = setup_output(opts.stdout, opts.text) + local stderr, stderr_handler, stderr_data = setup_output(opts.stderr, opts.text) local stdin, towrite = setup_input(opts.stdin) --- @type vim.SystemState @@ -335,7 +343,9 @@ function M.run(cmd, opts, on_exit) timeout = opts.timeout, stdin = stdin, stdout = stdout, + stdout_data = stdout_data, stderr = stderr, + stderr_data = stderr_data, } --- @diagnostic disable-next-line:missing-fields @@ -350,17 +360,15 @@ function M.run(cmd, opts, on_exit) }, function(code, signal) _on_exit(state, code, signal, on_exit) end, function() - close_handles(state) + _on_error(state) end) - if stdout then - state.stdout_data = {} - stdout:read_start(stdout_handler or default_handler(stdout, opts.text, state.stdout_data)) + if stdout and stdout_handler then + stdout:read_start(stdout_handler) end - if stderr then - state.stderr_data = {} - stderr:read_start(stderr_handler or default_handler(stderr, opts.text, state.stderr_data)) + if stderr and stderr_handler then + stderr:read_start(stderr_handler) end local obj = new_systemobj(state) diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 5d771c30e9..b4c37dd160 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1873,6 +1873,8 @@ local filename = { ['.clang-tidy'] = 'yaml', ['yarn.lock'] = 'yaml', matplotlibrc = 'yaml', + ['.condarc'] = 'yaml', + condarc = 'yaml', zathurarc = 'zathurarc', ['/etc/zprofile'] = 'zsh', ['.zlogin'] = 'zsh', diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index b1a3316e3e..4717d7995a 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -211,17 +211,117 @@ local function reuse_client_default(client, config) return false end +--- Reset defaults set by `set_defaults`. +--- Must only be called if the last client attached to a buffer exits. +local function reset_defaults(bufnr) + if vim.bo[bufnr].tagfunc == 'v:lua.vim.lsp.tagfunc' then + vim.bo[bufnr].tagfunc = nil + end + if vim.bo[bufnr].omnifunc == 'v:lua.vim.lsp.omnifunc' then + vim.bo[bufnr].omnifunc = nil + end + if vim.bo[bufnr].formatexpr == 'v:lua.vim.lsp.formatexpr()' then + vim.bo[bufnr].formatexpr = nil + end + vim._with({ buf = bufnr }, function() + local keymap = vim.fn.maparg('K', 'n', false, true) + if keymap and keymap.callback == vim.lsp.buf.hover and keymap.buffer == 1 then + vim.keymap.del('n', 'K', { buffer = bufnr }) + end + end) +end + +--- @param code integer +--- @param signal integer +--- @param client_id integer +local function on_client_exit(code, signal, client_id) + local client = all_clients[client_id] + + vim.schedule(function() + for bufnr in pairs(client.attached_buffers) do + if client and client.attached_buffers[bufnr] and api.nvim_buf_is_valid(bufnr) then + api.nvim_exec_autocmds('LspDetach', { + buffer = bufnr, + modeline = false, + data = { client_id = client_id }, + }) + end + + client.attached_buffers[bufnr] = nil + + if #lsp.get_clients({ bufnr = bufnr, _uninitialized = true }) == 0 then + reset_defaults(bufnr) + end + end + + local namespace = vim.lsp.diagnostic.get_namespace(client_id) + vim.diagnostic.reset(namespace) + end) + + local name = client.name or 'unknown' + + -- Schedule the deletion of the client object so that it exists in the execution of LspDetach + -- autocommands + vim.schedule(function() + all_clients[client_id] = nil + + -- Client can be absent if executable starts, but initialize fails + -- init/attach won't have happened + if client then + changetracking.reset(client) + end + if code ~= 0 or (signal ~= 0 and signal ~= 15) then + local msg = string.format( + 'Client %s quit with exit code %s and signal %s. Check log for errors: %s', + name, + code, + signal, + lsp.get_log_path() + ) + vim.notify(msg, vim.log.levels.WARN) + end + end) +end + +--- Creates and initializes a client with the given configuration. +--- @param config vim.lsp.ClientConfig Configuration for the server. +--- @return integer? client_id |vim.lsp.get_client_by_id()| Note: client may not be +--- fully initialized. Use `on_init` to do any actions once +--- the client has been initialized. +--- @return string? # Error message, if any +local function create_and_initialize_client(config) + local ok, res = pcall(require('vim.lsp.client').create, config) + if not ok then + return nil, res --[[@as string]] + end + + local client = assert(res) + + --- @diagnostic disable-next-line: invisible + table.insert(client._on_exit_cbs, on_client_exit) + + all_clients[client.id] = client + + client:initialize() + + return client.id, nil +end + --- @class vim.lsp.start.Opts --- @inlinedoc --- --- Predicate used to decide if a client should be re-used. Used on all --- running clients. The default implementation re-uses a client if name and --- root_dir matches. ---- @field reuse_client? fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean +--- @field reuse_client? (fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean) --- --- Buffer handle to attach to if starting or re-using a client (0 for current). --- @field bufnr? integer --- +--- Whether to attach the client to a buffer (default true). +--- If set to `false`, `reuse_client` and `bufnr` will be ignored. +--- @field attach? boolean +--- --- Suppress error reporting if the LSP server fails to start (default false). --- @field silent? boolean @@ -239,10 +339,10 @@ end --- }) --- ``` --- ---- See |vim.lsp.start_client()| for all available options. The most important are: +--- See |vim.lsp.ClientConfig| for all available options. The most important are: --- --- - `name` arbitrary name for the LSP client. Should be unique per language server. ---- - `cmd` command string[] or function, described at |vim.lsp.start_client()|. +--- - `cmd` command string[] or function. --- - `root_dir` path to the project root. By default this is used to decide if an existing client --- should be re-used. The example above uses |vim.fs.root()| to detect the root by traversing --- the file system upwards starting from the current directory until either a `pyproject.toml` @@ -262,7 +362,7 @@ end --- `ftplugin/<filetype_name>.lua` (See |ftplugin-name|) --- --- @param config vim.lsp.ClientConfig Configuration for the server. ---- @param opts vim.lsp.start.Opts? Optional keyword arguments +--- @param opts vim.lsp.start.Opts? Optional keyword arguments. --- @return integer? client_id function lsp.start(config, opts) opts = opts or {} @@ -271,6 +371,10 @@ function lsp.start(config, opts) for _, client in pairs(all_clients) do if reuse_client(client, config) then + if opts.attach == false then + return client.id + end + if lsp.buf_attach_client(bufnr, client.id) then return client.id else @@ -279,7 +383,7 @@ function lsp.start(config, opts) end end - local client_id, err = lsp.start_client(config) + local client_id, err = create_and_initialize_client(config) if err then if not opts.silent then vim.notify(err, vim.log.levels.WARN) @@ -287,6 +391,10 @@ function lsp.start(config, opts) return nil end + if opts.attach == false then + return client_id + end + if client_id and lsp.buf_attach_client(bufnr, client_id) then return client_id end @@ -383,78 +491,7 @@ function lsp._set_defaults(client, bufnr) end end ---- Reset defaults set by `set_defaults`. ---- Must only be called if the last client attached to a buffer exits. -local function reset_defaults(bufnr) - if vim.bo[bufnr].tagfunc == 'v:lua.vim.lsp.tagfunc' then - vim.bo[bufnr].tagfunc = nil - end - if vim.bo[bufnr].omnifunc == 'v:lua.vim.lsp.omnifunc' then - vim.bo[bufnr].omnifunc = nil - end - if vim.bo[bufnr].formatexpr == 'v:lua.vim.lsp.formatexpr()' then - vim.bo[bufnr].formatexpr = nil - end - vim._with({ buf = bufnr }, function() - local keymap = vim.fn.maparg('K', 'n', false, true) - if keymap and keymap.callback == vim.lsp.buf.hover and keymap.buffer == 1 then - vim.keymap.del('n', 'K', { buffer = bufnr }) - end - end) -end - ---- @param code integer ---- @param signal integer ---- @param client_id integer -local function on_client_exit(code, signal, client_id) - local client = all_clients[client_id] - - vim.schedule(function() - for bufnr in pairs(client.attached_buffers) do - if client and client.attached_buffers[bufnr] and api.nvim_buf_is_valid(bufnr) then - api.nvim_exec_autocmds('LspDetach', { - buffer = bufnr, - modeline = false, - data = { client_id = client_id }, - }) - end - - client.attached_buffers[bufnr] = nil - - if #lsp.get_clients({ bufnr = bufnr, _uninitialized = true }) == 0 then - reset_defaults(bufnr) - end - end - - local namespace = vim.lsp.diagnostic.get_namespace(client_id) - vim.diagnostic.reset(namespace) - end) - - local name = client.name or 'unknown' - - -- Schedule the deletion of the client object so that it exists in the execution of LspDetach - -- autocommands - vim.schedule(function() - all_clients[client_id] = nil - - -- Client can be absent if executable starts, but initialize fails - -- init/attach won't have happened - if client then - changetracking.reset(client) - end - if code ~= 0 or (signal ~= 0 and signal ~= 15) then - local msg = string.format( - 'Client %s quit with exit code %s and signal %s. Check log for errors: %s', - name, - code, - signal, - lsp.get_log_path() - ) - vim.notify(msg, vim.log.levels.WARN) - end - end) -end - +--- @deprecated --- Starts and initializes a client with the given configuration. --- @param config vim.lsp.ClientConfig Configuration for the server. --- @return integer? client_id |vim.lsp.get_client_by_id()| Note: client may not be @@ -462,21 +499,8 @@ end --- the client has been initialized. --- @return string? # Error message, if any function lsp.start_client(config) - local ok, res = pcall(require('vim.lsp.client').create, config) - if not ok then - return nil, res --[[@as string]] - end - - local client = assert(res) - - --- @diagnostic disable-next-line: invisible - table.insert(client._on_exit_cbs, on_client_exit) - - all_clients[client.id] = client - - client:initialize() - - return client.id, nil + vim.deprecate('vim.lsp.start_client()', 'vim.lsp.start()', '0.13') + return create_and_initialize_client(config) end ---Buffer lifecycle handler for textDocument/didSave diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index a14b6ccda6..a83d75bf75 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -78,7 +78,7 @@ local validate = vim.validate --- @field settings? table --- --- Table that maps string of clientside commands to user-defined functions. ---- Commands passed to start_client take precedence over the global command registry. Each key +--- Commands passed to `start()` take precedence over the global command registry. Each key --- must be a unique command name, and the value is a function which is called if any LSP action --- (code action, code lenses, ...) triggers the command. --- @field commands? table<string,fun(command: lsp.Command, ctx: table)> @@ -104,7 +104,7 @@ local validate = vim.validate --- @field on_error? fun(code: integer, err: string) --- --- Callback invoked before the LSP "initialize" phase, where `params` contains the parameters ---- being sent to the server and `config` is the config that was passed to |vim.lsp.start_client()|. +--- being sent to the server and `config` is the config that was passed to |vim.lsp.start()|. --- You can use this to modify parameters before they are sent. --- @field before_init? fun(params: lsp.InitializeParams, config: vim.lsp.ClientConfig) --- @@ -167,7 +167,7 @@ local validate = vim.validate --- @field requests table<integer,{ type: string, bufnr: integer, method: string}> --- --- copy of the table that was passed by the user ---- to |vim.lsp.start_client()|. +--- to |vim.lsp.start()|. --- @field config vim.lsp.ClientConfig --- --- Response from the server sent on `initialize` describing the server's @@ -307,7 +307,7 @@ local function default_get_language_id(_bufnr, filetype) return filetype end ---- Validates a client configuration as given to |vim.lsp.start_client()|. +--- Validates a client configuration as given to |vim.lsp.start()|. --- @param config vim.lsp.ClientConfig local function validate_config(config) validate('config', config, 'table') diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 6c8564845f..2327a37ab1 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -617,7 +617,7 @@ end --- - a host and port via TCP --- --- Return a function that can be passed to the `cmd` field for ---- |vim.lsp.start_client()| or |vim.lsp.start()|. +--- |vim.lsp.start()|. --- ---@param host_or_path string host to connect to or path to a pipe/domain socket ---@param port integer? TCP port to connect to. If absent the first argument must be a pipe diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index ab52612f9f..1262af5e40 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1040,7 +1040,7 @@ fail: /// Open a terminal instance in a buffer /// /// By default (and currently the only option) the terminal will not be -/// connected to an external process. Instead, input send on the channel +/// connected to an external process. Instead, input sent on the channel /// will be echoed directly by the terminal. This is useful to display /// ANSI terminal sequences returned as part of a rpc message, or similar. /// @@ -1051,6 +1051,18 @@ fail: /// Then |nvim_chan_send()| can be called immediately to process sequences /// in a virtual terminal having the intended size. /// +/// Example: this `TermHl` command can be used to display and highlight raw ANSI termcodes, so you +/// can use Nvim as a "scrollback pager" (for terminals like kitty): [terminal-scrollback-pager]() +/// +/// ```lua +/// vim.api.nvim_create_user_command('TermHl', function() +/// local b = vim.api.nvim_create_buf(false, true) +/// local chan = vim.api.nvim_open_term(b, {}) +/// vim.api.nvim_chan_send(chan, table.concat(vim.api.nvim_buf_get_lines(0, 0, -1, false), '\n')) +/// vim.api.nvim_win_set_buf(0, b) +/// end, { desc = 'Highlights ANSI termcodes in curbuf' }) +/// ``` +/// /// @param buffer the buffer to use (expected to be empty) /// @param opts Optional parameters. /// - on_input: Lua callback for input sent, i e keypresses in terminal diff --git a/src/nvim/decoration_provider.c b/src/nvim/decoration_provider.c index 74f444d8e8..805e9877b6 100644 --- a/src/nvim/decoration_provider.c +++ b/src/nvim/decoration_provider.c @@ -55,14 +55,13 @@ static bool decor_provider_invoke(int provider_idx, const char *name, LuaRef ref // We get the provider here via an index in case the above call to nlua_call_ref causes // decor_providers to be reallocated. DecorProvider *provider = &kv_A(decor_providers, provider_idx); - if (!ERROR_SET(&err) && api_object_to_bool(ret, "provider %s retval", default_true, &err)) { provider->error_count = 0; return true; } - if (ERROR_SET(&err)) { + if (ERROR_SET(&err) && provider->error_count < DP_MAX_ERROR) { decor_provider_error(provider, name, err.msg); provider->error_count++; diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index e03cffd1ca..a939038603 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -1516,10 +1516,12 @@ static void win_update(win_T *wp) decor_providers_invoke_win(wp); - if (win_redraw_signcols(wp)) { - wp->w_lines_valid = 0; - wp->w_redr_type = UPD_NOT_VALID; - changed_line_abv_curs_win(wp); + FOR_ALL_WINDOWS_IN_TAB(win, curtab) { + if (win->w_buffer == wp->w_buffer && win_redraw_signcols(win)) { + win->w_lines_valid = 0; + changed_line_abv_curs_win(win); + redraw_later(win, UPD_NOT_VALID); + } } init_search_hl(wp, &screen_search_hl); diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index cd3ccf543e..08c9cd3991 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -6049,6 +6049,7 @@ M.funcs = { ]=], name = 'jobwait', params = { { 'jobs', 'integer[]' }, { 'timeout', 'integer' } }, + returns = 'integer[]', signature = 'jobwait({jobs} [, {timeout}])', }, join = { @@ -8776,7 +8777,7 @@ M.funcs = { < ]=], name = 'rpcnotify', - params = { { 'channel', 'integer' }, { 'event', 'string' }, { 'args', 'any' } }, + params = { { 'channel', 'integer' }, { 'event', 'string' }, { '...', 'any' } }, signature = 'rpcnotify({channel}, {event} [, {args}...])', }, rpcrequest = { @@ -8789,7 +8790,7 @@ M.funcs = { < ]=], name = 'rpcrequest', - params = { { 'channel', 'integer' }, { 'method', 'string' }, { 'args', 'any' } }, + params = { { 'channel', 'integer' }, { 'method', 'string' }, { '...', 'any' } }, signature = 'rpcrequest({channel}, {method} [, {args}...])', }, rpcstart = { @@ -11155,7 +11156,7 @@ M.funcs = { and exists only for backwards-compatibility. With UTF-8 composing characters are handled properly: >vim echo str2list("á") " returns [97, 769] - + < ]=], name = 'str2list', params = { { 'string', 'string' }, { 'utf8', 'boolean' } }, @@ -13091,6 +13092,7 @@ M.funcs = { ]=], name = 'winlayout', params = { { 'tabnr', 'integer' } }, + returns = 'any[]', signature = 'winlayout([{tabnr}])', }, winline = { diff --git a/test/functional/lua/system_spec.lua b/test/functional/lua/system_spec.lua index 3f847ca3be..79a70c9c72 100644 --- a/test/functional/lua/system_spec.lua +++ b/test/functional/lua/system_spec.lua @@ -18,8 +18,7 @@ local function system_sync(cmd, opts) local res = obj:wait() -- Check the process is no longer running - local proc = vim.api.nvim_get_proc(obj.pid) - assert(not proc, 'process still exists') + assert(not vim.api.nvim_get_proc(obj.pid), 'process still exists') return res end) @@ -27,23 +26,23 @@ end local function system_async(cmd, opts) return exec_lua(function() - _G.done = false + local done = false + local res --- @type vim.SystemCompleted? local obj = vim.system(cmd, opts, function(obj) - _G.done = true - _G.ret = obj + done = true + res = obj end) local ok = vim.wait(10000, function() - return _G.done + return done end) assert(ok, 'process did not exit') -- Check the process is no longer running - local proc = vim.api.nvim_get_proc(obj.pid) - assert(not proc, 'process still exists') + assert(not vim.api.nvim_get_proc(obj.pid), 'process still exists') - return _G.ret + return res end) end @@ -120,4 +119,32 @@ describe('vim.system', function() system_sync({ './test' }) end) end + + it('always captures all content of stdout/stderr #30846', function() + t.skip(n.fn.executable('git') == 0, 'missing "git" command') + eq( + 0, + exec_lua(function() + local done = 0 + local fail = 0 + for _ = 1, 200 do + vim.system( + { 'git', 'show', ':0:test/functional/plugin/lsp_spec.lua' }, + { text = true }, + function(o) + if o.code ~= 0 or #o.stdout == 0 then + fail = fail + 1 + end + done = done + 1 + end + ) + end + + local ok = vim.wait(10000, function() + return done == 200 + end, 200) + return fail + (ok and 0 or 1) + end) + ) + end) end) diff --git a/test/functional/plugin/lsp/diagnostic_spec.lua b/test/functional/plugin/lsp/diagnostic_spec.lua index ca9196562c..4ecb056d01 100644 --- a/test/functional/plugin/lsp/diagnostic_spec.lua +++ b/test/functional/plugin/lsp/diagnostic_spec.lua @@ -89,7 +89,7 @@ describe('vim.lsp.diagnostic', function() return extmarks end - client_id = assert(vim.lsp.start_client { + client_id = assert(vim.lsp.start({ cmd_env = { NVIM_LUA_NOTRACK = '1', }, @@ -101,7 +101,7 @@ describe('vim.lsp.diagnostic', function() '--headless', }, offset_encoding = 'utf-16', - }) + }, { attach = false })) end) fake_uri = 'file:///fake/uri' diff --git a/test/functional/plugin/lsp/semantic_tokens_spec.lua b/test/functional/plugin/lsp/semantic_tokens_spec.lua index 280bd27207..9912bf2063 100644 --- a/test/functional/plugin/lsp/semantic_tokens_spec.lua +++ b/test/functional/plugin/lsp/semantic_tokens_spec.lua @@ -456,7 +456,7 @@ describe('semantic token highlighting', function() vim.notify = function(...) table.insert(_G.notifications, 1, { ... }) end - return vim.lsp.start_client({ name = 'dummy', cmd = _G.server.cmd }) + return vim.lsp.start({ name = 'dummy', cmd = _G.server.cmd }, { attach = false }) end) eq(false, exec_lua('return vim.lsp.buf_is_attached(0, ...)', client_id)) diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index e30d1ba411..e735e20ff5 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -95,7 +95,7 @@ describe('LSP', function() exec_lua(function() _G.lsp = require('vim.lsp') function _G.test__start_client() - return vim.lsp.start_client { + return vim.lsp.start({ cmd_env = { NVIM_LOG_FILE = fake_lsp_logfile, NVIM_APPNAME = 'nvim_lsp_test', @@ -112,7 +112,7 @@ describe('LSP', function() name = 'test_folder', }, }, - } + }, { attach = false }) end _G.TEST_CLIENT1 = _G.test__start_client() end) diff --git a/test/functional/terminal/cursor_spec.lua b/test/functional/terminal/cursor_spec.lua index f223cdd417..4d25fe62ad 100644 --- a/test/functional/terminal/cursor_spec.lua +++ b/test/functional/terminal/cursor_spec.lua @@ -511,7 +511,7 @@ describe('buffer cursor position is correct in terminal with number column', fun before_each(function() clear() - command('set number') + command('au TermOpen * set number') end) describe('in a line with no multibyte chars or trailing spaces,', function() diff --git a/test/functional/ui/sign_spec.lua b/test/functional/ui/sign_spec.lua index 7874c04c39..b7a2429ada 100644 --- a/test/functional/ui/sign_spec.lua +++ b/test/functional/ui/sign_spec.lua @@ -607,4 +607,77 @@ describe('Signs', function() eq(6, infos[1].textoff) eq(6, infos[2].textoff) end) + + it('auto width updated in all windows after sign placed in on_win #31438', function() + exec_lua([[ + vim.cmd.call('setline(1, range(1, 500))') + vim.cmd('wincmd s | wincmd v | wincmd j | wincmd v') + + _G.log, _G.needs_clear = {}, false + local ns_id, mark_id = vim.api.nvim_create_namespace('test'), nil + + -- Add decoration which possibly clears all extmarks and adds one on line 499 + local on_win = function(_, winid, bufnr, toprow, botrow) + if _G.needs_clear then + vim.api.nvim_buf_clear_namespace(bufnr, ns_id, 0, -1) + _G.needs_clear = false + end + + if toprow < 499 and 499 <= botrow then + mark_id = vim.api.nvim_buf_set_extmark(bufnr, ns_id, 499, 0, { id = mark_id, sign_text = '!', invalidate = true }) + end + end + vim.api.nvim_set_decoration_provider(ns_id, { on_win = on_win }) + ]]) + screen:expect([[ + 1 │1 | + 2 │2 | + 3 │3 | + 4 │4 | + 5 │5 | + 6 │6 | + {2:[No Name] [+] [No Name] [+] }| + ^1 │1 | + 2 │2 | + 3 │3 | + 4 │4 | + 5 │5 | + {3:[No Name] [+] }{2:[No Name] [+] }| + | + ]]) + feed('G') + screen:expect([[ + {7: }1 │{7: }1 | + {7: }2 │{7: }2 | + {7: }3 │{7: }3 | + {7: }4 │{7: }4 | + {7: }5 │{7: }5 | + {7: }6 │{7: }6 | + {2:[No Name] [+] [No Name] [+] }| + {7: }496 │{7: }1 | + {7: }497 │{7: }2 | + {7: }498 │{7: }3 | + {7: }499 │{7: }4 | + ! ^500 │{7: }5 | + {3:[No Name] [+] }{2:[No Name] [+] }| + | + ]]) + feed(':lua log, needs_clear = {}, true<CR>') + screen:expect([[ + {7: }1 │{7: }1 | + {7: }2 │{7: }2 | + {7: }3 │{7: }3 | + {7: }4 │{7: }4 | + {7: }5 │{7: }5 | + {7: }6 │{7: }6 | + {2:[No Name] [+] [No Name] [+] }| + {7: }496 │{7: }1 | + {7: }497 │{7: }2 | + {7: }498 │{7: }3 | + {7: }499 │{7: }4 | + ! ^500 │{7: }5 | + {3:[No Name] [+] }{2:[No Name] [+] }| + :lua log, needs_clear = {}, true | + ]]) + end) end) diff --git a/test/old/testdir/test_filetype.vim b/test/old/testdir/test_filetype.vim index 06ac59de20..5a38f26902 100644 --- a/test/old/testdir/test_filetype.vim +++ b/test/old/testdir/test_filetype.vim @@ -886,7 +886,7 @@ func s:GetFilenameChecks() abort \ 'xslt': ['file.xsl', 'file.xslt'], \ 'yacc': ['file.yy', 'file.yxx', 'file.y++'], \ 'yaml': ['file.yaml', 'file.yml', 'file.eyaml', 'any/.bundle/config', '.clangd', '.clang-format', '.clang-tidy', 'file.mplstyle', 'matplotlibrc', 'yarn.lock', - \ '/home/user/.kube/config'], + \ '/home/user/.kube/config', '.condarc', 'condarc'], \ 'yang': ['file.yang'], \ 'yuck': ['file.yuck'], \ 'z8a': ['file.z8a'], |