From 7838c242e96c4f6c6432a997234bb2dbdbfea658 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 14 Oct 2025 19:22:03 -0400 Subject: [PATCH] docs: types, news, lua-plugin - mention "lua_ls", not "luals". https://github.com/neovim/neovim/discussions/36182 Co-authored-by: Maria Solano --- runtime/doc/dev_arch.txt | 28 +++++++++++++++++++------ runtime/doc/develop.txt | 1 + runtime/doc/editing.txt | 3 +++ runtime/doc/lsp.txt | 22 ++++++++++---------- runtime/doc/lua-plugin.txt | 35 ++++++++++++++++++++++++++++++++ runtime/doc/lua.txt | 4 ++-- runtime/doc/news.txt | 15 +++++++------- runtime/lua/vim/lsp.lua | 6 +++--- runtime/lua/vim/lsp/client.lua | 6 +++--- runtime/lua/vim/shared.lua | 4 ++-- src/nvim/msgpack_rpc/server.c | 2 +- src/nvim/path.c | 18 ++++++++-------- test/functional/api/vim_spec.lua | 14 ++++++------- 13 files changed, 106 insertions(+), 52 deletions(-) diff --git a/runtime/doc/dev_arch.txt b/runtime/doc/dev_arch.txt index 97f22b57a8..1e4aa4eb19 100644 --- a/runtime/doc/dev_arch.txt +++ b/runtime/doc/dev_arch.txt @@ -24,9 +24,29 @@ Data structures Use `kvec.h` for most lists. When you absolutely need a linked list, use `lib/queue_defs.h` which defines an "intrusive" linked list. +============================================================================== +Events + +All new events must be implemented using `aucmd_defer()` (and where possible, +old events should be migrated to this), so that they are processed in +a predictable manner, which avoids crashes and race conditions. See +`do_markset_autocmd` for an example. + ============================================================================== UI events +The long-term vision is that UI events are just another type of "editor event" +(formerly known as "autocmds"). There is no real reason that we have separate +types of user-facing or plugin-facing events. Events are events. Their +"transport" is irrelevant and any event should be possible to emit over any +transport (editor or RPC). + +Meanwhile the current situation is that UI events are a particular RPC event +packaged in a generic `redraw` notification. They also can be listened to +in-process via |vim.ui_attach()|. + +UI events are deferred to UIs, which implies a deepcopy of the UI event data. + The source files most directly involved with UI events are: 1. `src/nvim/ui.*`: calls handler functions of registered UI structs (independent from msgpack-rpc) 2. `src/nvim/api/ui.*`: forwards messages over msgpack-rpc to remote UIs. @@ -38,12 +58,8 @@ functions used by the source files above. It also generates metadata accessible as `api_info().ui_events`. See commit d3a8e9217f39c59dd7762bd22a76b8bd03ca85ff for an example of adding -a new UI event. - -UI events are deferred to UIs, which implies a deepcopy of the UI event data. - -Remember to bump NVIM_API_LEVEL if it wasn't already during this development -cycle. +a new UI event. Remember to bump NVIM_API_LEVEL if it wasn't already during +this development cycle. Other references: - |msgpack-rpc| diff --git a/runtime/doc/develop.txt b/runtime/doc/develop.txt index 883aae711e..c9ceffe089 100644 --- a/runtime/doc/develop.txt +++ b/runtime/doc/develop.txt @@ -475,6 +475,7 @@ everywhere, not "buffer" in some places and "buf" in others. - chan: |channel| - cmd: Command - cmdline: Command-line UI or input + - dir: Directory - fn: Function - hl: Highlight - pos: Position diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt index 385905d9d1..804ceca74d 100644 --- a/runtime/doc/editing.txt +++ b/runtime/doc/editing.txt @@ -87,6 +87,9 @@ g CTRL-G Prints the current position of the cursor in five If the buffer did have a name, that name becomes the |alternate-file| name. An unlisted buffer is created to hold the old name. + + See |nvim_buf_set_name()| to avoid filename escaping. + *:0file* :0f[ile][!] Remove the name of the current buffer. The optional ! avoids truncating the message, as with |:file|. diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index cac38f6450..673704a7a4 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -30,7 +30,7 @@ Follow these steps to get LSP features: 2. Define a new config |lsp-new-config| (or install https://github.com/neovim/nvim-lspconfig). Example: >lua - vim.lsp.config['luals'] = { + vim.lsp.config['lua_ls'] = { -- Command and arguments to start the server. cmd = { 'lua-language-server' }, -- Filetypes to automatically attach to. @@ -52,7 +52,7 @@ Follow these steps to get LSP features: 3. Use |vim.lsp.enable()| to enable the config. Example: >lua - vim.lsp.enable('luals') + vim.lsp.enable('lua_ls') < 4. Open a code file matching one of the `filetypes` specified in the config. Note: Depending on the LSP server, you may need to ensure your project has @@ -997,8 +997,8 @@ config({name}, {cfg}) *vim.lsp.config()* filetypes = { 'c', 'cpp' }, } < - • Get the resolved configuration for "luals": >lua - local cfg = vim.lsp.config.luals + • Get the resolved configuration for "lua_ls": >lua + local cfg = vim.lsp.config.lua_ls < Attributes: ~ @@ -1014,7 +1014,7 @@ enable({name}, {enable}) *vim.lsp.enable()* Examples: >lua vim.lsp.enable('clangd') - vim.lsp.enable({'luals', 'pyright'}) + vim.lsp.enable({'lua_ls', 'pyright'}) < Example: *lsp-restart* Passing `false` stops and detaches the client(s). @@ -1689,12 +1689,12 @@ Lua module: vim.lsp.client *lsp-client* Fields: ~ • {before_init}? (`fun(params: lsp.InitializeParams, config: vim.lsp.ClientConfig)`) - 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()|. - You can use this to modify parameters before - they are sent. + Callback which can modify parameters before + they are sent to the server. Invoked before LSP + "initialize" phase (after `cmd` is invoked), + where `params` is the parameters being sent to + the server and `config` is the config passed to + |vim.lsp.start()|. • {capabilities}? (`lsp.ClientCapabilities`) Map overriding the default capabilities defined by |vim.lsp.protocol.make_client_capabilities()|, diff --git a/runtime/doc/lua-plugin.txt b/runtime/doc/lua-plugin.txt index a978159497..5c4fc052c8 100644 --- a/runtime/doc/lua-plugin.txt +++ b/runtime/doc/lua-plugin.txt @@ -217,6 +217,11 @@ Consider making use of 'filetype' for any functionality that is specific to a filetype, by putting the initialization logic in a `ftplugin/{filetype}.lua` script. +For buffers owned by your plugin (often used to show a custom UI or view), +typically your plugin will set a custom 'filetype'. In that case, it's useful +to set the 'filetype' "as late as possible", so that users can override +buffer-local settings after your plugin has (re)initialized the buffer. + FILETYPE EXAMPLE A plugin tailored to Rust development might have initialization in @@ -240,6 +245,10 @@ A plugin tailored to Rust development might have initialization in ============================================================================== Configuration *lua-plugin-config* +To allow users to override buffer-local configuration for filetypes owned by +your plugin, publish a |FileType| event, "as late as possible". +|lua-plugin-filetype| + Once you have merged the default configuration with the user's config, you should validate configs. @@ -250,6 +259,32 @@ Validations could include: This can be tricky to implement, and may be better suited for a |health| check, to reduce overhead. +============================================================================== +UI *lua-plugin-ui* + +Some plugins have their own "UI" which they present in a buffer that the +plugin "owns". In that buffer typically you will want to provide custom +actions. + +Besides creating || mappings, you may want to consider providing actions +by defining an in-process LSP server. Offering actions as code-actions +|vim.lsp.buf.code_action()| means the user can see all available actions using +the default |gra| mapping to view the code-actions menu. They can even define +mappings to a specific action by invoking `vim.lsp.buf.code_action()` with the +`filter` + `apply` parameters: >lua + + vim.lsp.buf.code_action({ + apply = true, + filter = function(a) + return a.title == 'Do something' + end, + }) +< + +Example: See `runtime/lua/vim/pack/_lsp.lua` for how vim.pack defines an +in-process LSP server to provide interactive features in its `nvim-pack://` +buffer. + ============================================================================== Troubleshooting *lua-plugin-troubleshooting* diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index d8cf0a3f77..70c605464a 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -1710,7 +1710,7 @@ vim.is_callable({f}) *vim.is_callable()* Returns true if object `f` can be called as a function. Parameters: ~ - • {f} (`any`) Any object + • {f} (`any?`) Any object Return: ~ (`boolean`) `true` if `f` is callable, else `false` @@ -1727,7 +1727,7 @@ vim.isarray({t}) *vim.isarray()* |rpcrequest()| or |vim.fn|. Parameters: ~ - • {t} (`table?`) + • {t} (`any?`) Return: ~ (`boolean`) `true` if array-like table, else `false`. diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index e6104ffa87..975564460f 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -80,7 +80,6 @@ EVENTS message UI that mimics the legacy message grid. Benefit: reduced UI event traffic and more flexibility for UIs. The `msg_history_show` event has an additional "prev_cmd" argument. -• A new `empty` message kind is emitted for an empty (e.g. `:echo ""`) message. HIGHLIGHTS @@ -88,12 +87,11 @@ HIGHLIGHTS LSP -• `root_markers` in |vim.lsp.Config| can now be ordered by priority. +• JSON "null" values in LSP messages are represented as |vim.NIL| instead of `nil`. + Missing fields (as opposed to JSON "null") are still represented as `nil`. • The function set with |vim.lsp.log.set_format_func()| is now given all arguments corresponding to a log entry instead of the individual arguments. -• `vim.lsp.semantic_tokens.start/stop` now renamed to - `vim.lsp.semantic_tokens.enable` -• Missing fields in LSP messages are now represented using |vim.NIL| instead of nil. +• Renamed `vim.lsp.semantic_tokens` `start()/stop()` to `enable()`. • |vim.lsp.util.convert_signature_help_to_markdown_lines()| activeParameter handling updated: • Values < 0 are now treated as `nil` instead of 0. @@ -103,7 +101,6 @@ LSP LUA • Renamed `vim.diff` to `vim.text.diff`. -• |vim.net.request()| adds a minimal HTTP GET API using curl. OPTIONS @@ -120,7 +117,6 @@ TREESITTER `metadata[capture_id].offset`. The offset will be applied in |vim.treesitter.get_range()|, which should be preferred over reading metadata directly for retrieving node ranges. -• |Query:iter_captures()| supports specifying starting and ending columns. TUI @@ -203,6 +199,7 @@ EDITOR EVENTS +• A new `empty` message kind is emitted for an empty (e.g. `:echo ""`) message. • |CmdlineLeave| sets |v:char| to the character that stops the Cmdline mode. • |CmdlineLeavePre| triggered before preparing to leave the command line. • New `append` parameter for |ui-messages| `msg_show` event. @@ -220,7 +217,7 @@ HIGHLIGHTS LSP • |vim.lsp.ClientConfig| gained `workspace_required`. -• You can control priority of |vim.lsp.Config| `root_markers`. +• You can control the priority of |vim.lsp.Config| `root_markers`. • Support for `textDocument/documentColor`: |lsp-document_color| https://microsoft.github.io/language-server-protocol/specification/#textDocument_documentColor • Support for `textDocument/colorPresentation |lsp-document_color| @@ -261,6 +258,7 @@ LSP LUA +• |vim.net.request()| can fetch files via HTTP GET requests. • |vim.wait()| returns the callback results. • Lua type annotations for `vim.uv`. • |vim.hl.range()| now allows multiple timed highlights. @@ -330,6 +328,7 @@ TERMINAL TREESITTER +• |Query:iter_captures()| supports specifying starting and ending columns. • |:EditQuery| command gained tab-completion, works with injected languages. TUI diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 40178cadb9..bb1fe6fa20 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -301,9 +301,9 @@ end --- filetypes = { 'c', 'cpp' }, --- } --- ``` ---- - Get the resolved configuration for "luals": +--- - Get the resolved configuration for "lua_ls": --- ```lua ---- local cfg = vim.lsp.config.luals +--- local cfg = vim.lsp.config.lua_ls --- ``` --- ---@since 13 @@ -522,7 +522,7 @@ end --- --- ```lua --- vim.lsp.enable('clangd') ---- vim.lsp.enable({'luals', 'pyright'}) +--- vim.lsp.enable({'lua_ls', 'pyright'}) --- ``` --- --- Example: [lsp-restart]() Passing `false` stops and detaches the client(s). Thus you can diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index f6c3084ef7..abf86a843a 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -34,9 +34,9 @@ local all_clients = {} --- @class vim.lsp.ClientConfig --- ---- 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()|. ---- You can use this to modify parameters before they are sent. +--- Callback which can modify parameters before they are sent to the server. Invoked before LSP +--- "initialize" phase (after `cmd` is invoked), where `params` is the parameters being sent to the +--- server and `config` is the config passed to |vim.lsp.start()|. --- @field before_init? fun(params: lsp.InitializeParams, config: vim.lsp.ClientConfig) --- --- Map overriding the default capabilities defined by |vim.lsp.protocol.make_client_capabilities()|, diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 8c55081050..b4827d0f54 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -826,7 +826,7 @@ end --- ---@see https://github.com/openresty/luajit2#tableisarray --- ----@param t? table +---@param t? any ---@return boolean `true` if array-like table, else `false`. function vim.isarray(t) if type(t) ~= 'table' then @@ -1246,7 +1246,7 @@ end --- Returns true if object `f` can be called as a function. --- ----@param f any Any object +---@param f? any Any object ---@return boolean `true` if `f` is callable, else `false` function vim.is_callable(f) if type(f) == 'function' then diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c index 1c839ac9d2..6c205b6954 100644 --- a/src/nvim/msgpack_rpc/server.c +++ b/src/nvim/msgpack_rpc/server.c @@ -135,7 +135,7 @@ char *server_address_new(const char *name) return xstrdup(fmt); } -/// Check if this instance owns a pipe address. +/// Check if this instance owns a pipe address (loopback). bool server_owns_pipe_address(const char *address) { bool result = false; diff --git a/src/nvim/path.c b/src/nvim/path.c index d2170ecd3b..f4393f07cd 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -46,8 +46,7 @@ enum { /// Compare two file names. /// -/// @param s1 First file name. Environment variables in this name will be -/// expanded. +/// @param s1 First file name. Environment variables in this name will be expanded. /// @param s2 Second file name. /// @param checkname When both files don't exist, only compare their names. /// @param expandenv Whether to expand environment variables in file names. @@ -540,7 +539,6 @@ bool path_has_wildcard(const char *p) return false; } -// Unix style wildcard expansion code. static int pstrcmp(const void *a, const void *b) { return pathcmp(*(char **)a, *(char **)b, -1); @@ -1956,8 +1954,10 @@ bool same_directory(char *f1, char *f2) } // Compare path "p[]" to "q[]". -// If "maxlen" >= 0 compare "p[maxlen]" to "q[maxlen]" +// If `maxlen` >= 0 compare `p[maxlen]` to `q[maxlen]` // Return value like strcmp(p, q), but consider path separators. +// +// See also `path_full_compare`. int pathcmp(const char *p, const char *q, int maxlen) { int i, j; @@ -2314,12 +2314,12 @@ int append_path(char *path, const char *to_append, size_t max_len) return OK; } -/// Expand a given file to its absolute path. +/// Used by `vim_FullName` and `fix_fname` to expand a filename to its full path. /// -/// @param fname filename which should be expanded. -/// @param buf buffer to store the absolute path of "fname". -/// @param len length of "buf". -/// @param force also expand when "fname" is already absolute. +/// @param fname Filename to expand. +/// @param buf Where to store the absolute path of "fname". +/// @param len Length of `buf`. +/// @param force Also expand when `fname` is already absolute. /// /// @return FAIL for failure, OK for success. static int path_to_absolute(const char *fname, char *buf, size_t len, int force) diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 35b4c21a76..843dcfea0e 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -4616,7 +4616,7 @@ describe('API', function() }, }, api.nvim_parse_cmd('4,6MyCommand! test it', {})) end) - it('works for commands separated by bar', function() + it('sets nextcmd for bar-separated commands', function() eq({ cmd = 'argadd', args = { 'a.txt' }, @@ -4655,6 +4655,12 @@ describe('API', function() }, }, api.nvim_parse_cmd('argadd a.txt | argadd b.txt', {})) end) + it('sets nextcmd after expr-arg commands #36029', function() + local result = api.nvim_parse_cmd('exe "ls"|edit foo', {}) + eq({ '"ls"' }, result.args) + eq('execute', result.cmd) + eq('edit foo', result.nextcmd) + end) it('parses :map commands with space in RHS', function() eq({ addr = 'none', @@ -4849,12 +4855,6 @@ describe('API', function() result = api.nvim_parse_cmd('copen 5', {}) eq(5, result.count) end) - it('parses nextcmd for commands #36029', function() - local result = api.nvim_parse_cmd('exe "ls"|edit foo', {}) - eq({ '"ls"' }, result.args) - eq('execute', result.cmd) - eq('edit foo', result.nextcmd) - end) end) describe('nvim_cmd', function()