aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--runtime/autoload/provider/clipboard.vim12
-rw-r--r--runtime/doc/builtin.txt1
-rw-r--r--runtime/doc/develop.txt37
-rw-r--r--runtime/doc/health.txt6
-rw-r--r--runtime/doc/lsp.txt9
-rw-r--r--runtime/doc/lua.txt22
-rw-r--r--runtime/doc/news.txt26
-rw-r--r--runtime/doc/provider.txt6
-rw-r--r--runtime/doc/treesitter.txt31
-rw-r--r--runtime/lua/vim/_editor.lua26
-rw-r--r--runtime/lua/vim/_meta/api_keysets_extra.lua2
-rw-r--r--runtime/lua/vim/_meta/vimfn.lua1
-rw-r--r--runtime/lua/vim/health.lua4
-rw-r--r--runtime/lua/vim/iter.lua10
-rw-r--r--runtime/lua/vim/lsp/completion.lua2
-rw-r--r--runtime/lua/vim/lsp/rpc.lua4
-rw-r--r--runtime/lua/vim/treesitter/_fold.lua4
-rw-r--r--runtime/lua/vim/treesitter/_query_linter.lua2
-rw-r--r--runtime/lua/vim/treesitter/languagetree.lua8
-rw-r--r--runtime/lua/vim/treesitter/query.lua28
-rw-r--r--runtime/lua/vim/ui.lua11
-rw-r--r--runtime/syntax/sudoers.vim17
-rw-r--r--runtime/syntax/vim.vim70
-rwxr-xr-xscripts/gen_vimdoc.lua4
-rw-r--r--src/nvim/api/ui.c8
-rw-r--r--src/nvim/eval.lua1
-rw-r--r--src/nvim/main.c18
-rw-r--r--src/nvim/msgpack_rpc/server.c31
-rw-r--r--src/nvim/os/env.c30
-rw-r--r--src/nvim/os/input.c2
-rw-r--r--test/functional/core/fileio_spec.lua2
-rw-r--r--test/functional/ex_cmds/profile_spec.lua9
-rw-r--r--test/functional/lua/loader_spec.lua3
-rw-r--r--test/functional/lua/vim_spec.lua10
-rw-r--r--test/functional/lua/watch_spec.lua3
-rw-r--r--test/functional/options/defaults_spec.lua25
-rw-r--r--test/functional/plugin/lsp/completion_spec.lua2
-rw-r--r--test/functional/plugin/lsp_spec.lua52
-rw-r--r--test/functional/plugin/man_spec.lua2
-rw-r--r--test/functional/treesitter/parser_spec.lua9
-rw-r--r--test/functional/treesitter/query_spec.lua20
-rw-r--r--test/functional/vimscript/server_spec.lua66
-rw-r--r--test/testutil.lua15
44 files changed, 345 insertions, 308 deletions
diff --git a/Makefile b/Makefile
index 9317bea509..84d4859bb3 100644
--- a/Makefile
+++ b/Makefile
@@ -14,7 +14,7 @@ else
TOUCH := touch
RM := rm -rf
CMAKE := $(shell (command -v cmake3 || echo cmake))
- CMAKE_GENERATOR ?= $(shell (command -v ninja > /dev/null 2>&1 && echo "Ninja") || echo "Unix Makefiles")
+ CMAKE_GENERATOR ?= "$(shell (command -v ninja > /dev/null 2>&1 && echo "Ninja") || echo "Unix Makefiles")"
define rmdir
rm -rf $1
endef
diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim
index 82e0953196..58d3d4550f 100644
--- a/runtime/autoload/provider/clipboard.vim
+++ b/runtime/autoload/provider/clipboard.vim
@@ -140,6 +140,18 @@ function! provider#clipboard#Executable() abort
let s:copy['*'] = s:copy['+']
let s:paste['*'] = s:paste['+']
return 'win32yank'
+ elseif executable('putclip') && executable('getclip')
+ let s:copy['+'] = ['putclip']
+ let s:paste['+'] = ['getclip']
+ let s:copy['*'] = s:copy['+']
+ let s:paste['*'] = s:paste['+']
+ return 'putclip'
+ elseif executable('clip') && executable('powershell')
+ let s:copy['+'] = ['clip']
+ let s:paste['+'] = ['powershell', '-NoProfile', '-NoLogo', '-Command', 'Get-Clipboard']
+ let s:copy['*'] = s:copy['+']
+ let s:paste['*'] = s:paste['+']
+ return 'clip'
elseif executable('termux-clipboard-set')
let s:copy['+'] = ['termux-clipboard-set']
let s:paste['+'] = ['termux-clipboard-get']
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index f1d8cc8526..cd8abc2351 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -4361,6 +4361,7 @@ maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()*
"lhsrawalt" The {lhs} of the mapping as raw bytes, alternate
form, only present when it differs from "lhsraw"
"rhs" The {rhs} of the mapping as typed.
+ "callback" Lua function, if RHS was defined as such.
"silent" 1 for a |:map-silent| mapping, else 0.
"noremap" 1 if the {rhs} of the mapping is not remappable.
"script" 1 if mapping was defined with <script>.
diff --git a/runtime/doc/develop.txt b/runtime/doc/develop.txt
index d7837dc2fe..41bb90299b 100644
--- a/runtime/doc/develop.txt
+++ b/runtime/doc/develop.txt
@@ -138,15 +138,15 @@ DOCUMENTATION *dev-doc*
- Write docstrings (as opposed to inline comments) with present tense ("Gets"),
not imperative ("Get"). This tends to reduce ambiguity and improve clarity
by describing "What" instead of "How". >
- GOOD:
+ ✅ OK:
/// Gets a highlight definition.
- BAD:
+ ❌ NO:
/// Get a highlight definition.
- Avoid starting docstrings with "The" or "A" unless needed to avoid
ambiguity. This is a visual aid and reduces noise. >
- GOOD:
+ ✅ OK:
/// @param dirname Path fragment before `pend`
- BAD:
+ ❌ NO:
/// @param dirname The path fragment before `pend`
- Vim differences:
- Do not prefix help tags with "nvim-". Use |vim_diff.txt| to catalog
@@ -329,13 +329,20 @@ Where possible, these patterns apply to _both_ Lua and the API:
- When accepting a buffer id, etc., 0 means "current buffer", nil means "all
buffers". Likewise for window id, tabpage id, etc.
- Examples: |vim.lsp.codelens.clear()| |vim.diagnostic.enable()|
-- Any function signature that accepts a callback function should define the
- callback as the LAST parameter, if possible. This improves readability of
- calls by placing the less "noisy" arguments near the start. >
- GOOD:
- filter(table, opts, function() … end)
- BAD:
- filter(function() … end, table, opts)
+- Any function signature that accepts a callback (example: |table.foreach()|)
+ should place it as the LAST parameter (after opts), if possible (or ALWAYS
+ for "continuation callbacks"—functions called exactly once).
+ - Improves readability by placing the less "noisy" arguments near the start.
+ - Consistent with luv.
+ - Useful for future async lib which transforms functions of the form
+ `function(<args>, cb(<ret)>))` => `function(<args>) -> <ret>`.
+ - Example: >lua
+ -- ✅ OK:
+ filter(…, opts, function() … end)
+ -- ❌ NO:
+ filter(function() … end, …, opts)
+ -- ❌ NO:
+ filter(…, function() … end, opts)
- "Enable" ("toggle") interface and behavior:
- `enable(…, nil)` and `enable(…, {buf=nil})` are synonyms and control the
the "global" enablement of a feature.
@@ -566,10 +573,10 @@ a good name: it's idiomatic and unambiguous. If the package is named "neovim",
it confuses users, and complicates documentation and discussions.
Examples of API-client package names:
-- GOOD: nvim-racket
-- GOOD: pynvim
-- BAD: python-client
-- BAD: neovim_
+- ✅ OK: nvim-racket
+- ✅ OK: pynvim
+- ❌ NO: python-client
+- ❌ NO: neovim_
API client implementation guidelines ~
diff --git a/runtime/doc/health.txt b/runtime/doc/health.txt
index e879f11351..cb70961b55 100644
--- a/runtime/doc/health.txt
+++ b/runtime/doc/health.txt
@@ -7,10 +7,10 @@
Type |gO| to see the table of contents.
==============================================================================
-Checkhealth *health*
+Checkhealth *vim.health* *health*
-health.vim is a minimal framework to help users troubleshoot configuration and
+vim.health is a minimal framework to help users troubleshoot configuration and
any other environment conditions that a plugin might care about. Nvim ships
with healthchecks for configuration, performance, python support, ruby
support, clipboard support, and more.
@@ -49,7 +49,7 @@ Commands *health-commands*
:checkhealth vim*
<
-Create a healthcheck *health-dev* *vim.health*
+Create a healthcheck *health-dev*
Healthchecks are functions that check the user environment, configuration, or
any other prerequisites that a plugin cares about. Nvim ships with
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index 7f5ae06030..a8b5825e63 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -1639,8 +1639,7 @@ Lua module: vim.lsp.completion *lsp-completion*
Fields: ~
• {autotrigger}? (`boolean`) Whether to trigger completion
automatically. Default: false
- • {convert}? (`fun(item: lsp.CompletionItem): table`) An optional
- function used to customize the transformation of an
+ • {convert}? (`fun(item: lsp.CompletionItem): table`) Transforms an
LSP CompletionItem to |complete-items|.
@@ -2312,14 +2311,14 @@ connect({host_or_path}, {port}) *vim.lsp.rpc.connect()*
Create a LSP RPC client factory that connects to either:
• a named pipe (windows)
• a domain socket (unix)
- • a host and port via TCP
+ • a host and port via TCP (host must be IP address)
Return a function that can be passed to the `cmd` field for
|vim.lsp.start_client()| or |vim.lsp.start()|.
Parameters: ~
- • {host_or_path} (`string`) host to connect to or path to a pipe/domain
- socket
+ • {host_or_path} (`string`) host (IP address) to connect to or path to
+ a pipe/domain socket
• {port} (`integer?`) TCP port to connect to. If absent the
first argument must be a pipe
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
index f071d67030..0a7c53a482 100644
--- a/runtime/doc/lua.txt
+++ b/runtime/doc/lua.txt
@@ -352,16 +352,14 @@ Example: >vim
<
*lua-table-ambiguous*
Lua tables are used as both dictionaries and lists, so it is impossible to
-determine whether empty table is meant to be empty list or empty dictionary.
-Additionally Lua does not have integer numbers. To distinguish between these
-cases there is the following agreement:
+decide whether empty table is a list or a dict. Also Lua does not have integer
+numbers. To disambiguate these cases, we define:
*lua-list*
-0. Empty table is empty list.
-1. Table with N consecutive integer indices starting from 1 and ending with
- N is considered a list. See also |list-iterator|.
+0. Empty table is a list. Use |vim.empty_dict()| to represent empty dict.
+1. Table with N consecutive (no `nil` values, aka "holes") integer keys 1…N is
+ a list. See also |list-iterator|.
*lua-dict*
-2. Table with string keys, none of which contains NUL byte, is considered to
- be a dictionary.
+2. Table with string keys, none of which contains NUL byte, is a dict.
3. Table with string keys, at least one of which contains NUL byte, is also
considered to be a dictionary, but this time it is converted to
a |msgpack-special-map|.
@@ -3839,10 +3837,12 @@ argument into an *Iter* object with methods (such as |Iter:filter()| and
chained to create iterator "pipelines": the output of each pipeline stage is
input to the next stage. The first stage depends on the type passed to
`vim.iter()`:
-• List tables (arrays, |lua-list|) yield only the value of each element.
- • Holes (nil values) are allowed.
+• Lists or arrays (|lua-list|) yield only the value of each element.
+ • Holes (nil values) are allowed (but discarded).
+ • Use pairs() to treat array/list tables as dicts (preserve holes and
+ non-contiguous integer keys): `vim.iter(pairs(…))`.
• Use |Iter:enumerate()| to also pass the index to the next stage.
- • Or initialize with ipairs(): `vim.iter(ipairs(…))`.
+ • Or initialize with ipairs(): `vim.iter(ipairs(…))`.
• Non-list tables (|lua-dict|) yield both the key and value of each element.
• Function |iterator|s yield all values returned by the underlying function.
• Tables with a |__call()| metamethod are treated as function iterators.
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index b7e1e0c84f..dacb27e320 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -53,21 +53,14 @@ EDITOR
documented and skips help buffers if run from a non-help buffer, otherwise
it moves to another help buffer.
-VIM SCRIPT
-
-• |v:msgpack_types| has the type "binary" removed. |msgpackparse()| no longer
- treats BIN, STR and FIXSTR as separate types. Any of these is returned as a
- string if possible, or a |blob| if the value contained embedded NUL:s.
-
EVENTS
• TODO
LSP
-• Add convert field in |vim.lsp.completion.BufferOpts| of
- |vim.lsp.completion.enable()| an optional function used to customize the
- transformation of an Lsp CompletionItem to |complete-items|.
+• |vim.lsp.completion.enable()| gained the `convert` callback which enables
+ customizing the transformation of an LSP CompletionItem to |complete-items|.
• |vim.lsp.diagnostic.from()| can be used to convert a list of
|vim.Diagnostic| objects into their LSP diagnostic representation.
@@ -89,12 +82,22 @@ PLUGINS
TREESITTER
-• TODO
+• |Query:iter_matches()| correctly returns all matching nodes in a match
+ instead of only the last node. This means that the returned table maps
+ capture IDs to a list of nodes that need to be iterated over. For
+ backwards compatibility, an option `all=false` (only return the last
+ matching node) is provided that will be removed in a future release.
TUI
• TODO
+VIMSCRIPT
+
+• |v:msgpack_types| has the type "binary" removed. |msgpackparse()| no longer
+ treats BIN, STR and FIXSTR as separate types. Any of these is returned as a
+ string if possible, or a |blob| if the value contained embedded NUL:s.
+
==============================================================================
NEW FEATURES *news-features*
@@ -159,7 +162,8 @@ PLUGINS
STARTUP
-• TODO
+• Nvim will fail if the |--listen| or |$NVIM_LISTEN_ADDRESS| address is
+ invalid, instead of silently skipping an invalid address.
TERMINAL
diff --git a/runtime/doc/provider.txt b/runtime/doc/provider.txt
index a39f4bc5d7..f1b0daee76 100644
--- a/runtime/doc/provider.txt
+++ b/runtime/doc/provider.txt
@@ -194,6 +194,8 @@ registers. Nvim looks for these clipboard tools, in order of priority:
- lemonade (for SSH) https://github.com/pocke/lemonade
- doitclient (for SSH) https://www.chiark.greenend.org.uk/~sgtatham/doit/
- win32yank (Windows)
+ - putclip, getclip (Windows) https://cygwin.com/packages/summary/cygutils.html
+ - clip, powershell (Windows) https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/clip
- termux (via termux-clipboard-set, termux-clipboard-set)
- tmux (if $TMUX is set)
@@ -248,8 +250,8 @@ For Windows WSL, try this g:clipboard definition:
\ '*': 'clip.exe',
\ },
\ 'paste': {
- \ '+': 'powershell.exe -c [Console]::Out.Write($(Get-Clipboard -Raw).tostring().replace("`r", ""))',
- \ '*': 'powershell.exe -c [Console]::Out.Write($(Get-Clipboard -Raw).tostring().replace("`r", ""))',
+ \ '+': 'powershell.exe -NoLogo -NoProfile -c [Console]::Out.Write($(Get-Clipboard -Raw).tostring().replace("`r", ""))',
+ \ '*': 'powershell.exe -NoLogo -NoProfile -c [Console]::Out.Write($(Get-Clipboard -Raw).tostring().replace("`r", ""))',
\ },
\ 'cache_enabled': 0,
\ }
diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt
index d9b71c4b5b..fea469e63b 100644
--- a/runtime/doc/treesitter.txt
+++ b/runtime/doc/treesitter.txt
@@ -1036,9 +1036,8 @@ add_directive({name}, {handler}, {opts})
the same name
• {all}? (`boolean`) Use the correct implementation of the
match table where capture IDs map to a list of nodes
- instead of a single node. Defaults to false (for backward
- compatibility). This option will eventually become the
- default and removed.
+ instead of a single node. Defaults to true. This option
+ will be removed in a future release.
*vim.treesitter.query.add_predicate()*
add_predicate({name}, {handler}, {opts})
@@ -1049,14 +1048,13 @@ add_predicate({name}, {handler}, {opts})
• {handler} (`fun(match: table<integer,TSNode[]>, pattern: integer, source: integer|string, predicate: any[], metadata: vim.treesitter.query.TSMetadata)`)
• see |vim.treesitter.query.add_directive()| for argument
meanings
- • {opts} (`table`) A table with the following fields:
+ • {opts} (`table?`) A table with the following fields:
• {force}? (`boolean`) Override an existing predicate of
the same name
• {all}? (`boolean`) Use the correct implementation of the
match table where capture IDs map to a list of nodes
- instead of a single node. Defaults to false (for backward
- compatibility). This option will eventually become the
- default and removed.
+ instead of a single node. Defaults to true. This option
+ will be removed in a future release.
edit({lang}) *vim.treesitter.query.edit()*
Opens a live editor to query the buffer you started from.
@@ -1216,14 +1214,8 @@ Query:iter_matches({node}, {source}, {start}, {stop}, {opts})
indices to a list of nodes, and metadata from any directives processing
the match.
- WARNING: Set `all=true` to ensure all matching nodes in a match are
- returned, otherwise only the last node in a match is returned, breaking
- captures involving quantifiers such as `(comment)+ @comment`. The default
- option `all=false` is only provided for backward compatibility and will be
- removed after Nvim 0.10.
-
Example: >lua
- for pattern, match, metadata in cquery:iter_matches(tree:root(), bufnr, 0, -1, { all = true }) do
+ for pattern, match, metadata in cquery:iter_matches(tree:root(), bufnr, 0, -1) do
for id, nodes in pairs(match) do
local name = query.captures[id]
for _, node in ipairs(nodes) do
@@ -1248,12 +1240,11 @@ Query:iter_matches({node}, {source}, {start}, {stop}, {opts})
start depth for each match. This is used to prevent
traversing too deep into a tree.
• match_limit (integer) Set the maximum number of
- in-progress matches (Default: 256).
- • all (boolean) When set, the returned match table maps
- capture IDs to a list of nodes. Older versions of
- iter_matches incorrectly mapped capture IDs to a single
- node, which is incorrect behavior. This option will
- eventually become the default and removed.
+ in-progress matches (Default: 256). all (boolean) When
+ `false` (default `true`), the returned table maps capture
+ IDs to a single (last) node instead of the full list of
+ matching nodes. This option is only for backward
+ compatibility and will be removed in a future release.
Return: ~
(`fun(): integer, table<integer, TSNode[]>, vim.treesitter.query.TSMetadata`)
diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua
index c7c8362bfb..7b5570cc99 100644
--- a/runtime/lua/vim/_editor.lua
+++ b/runtime/lua/vim/_editor.lua
@@ -1,17 +1,19 @@
-- Nvim-Lua stdlib: the `vim` module (:help lua-stdlib)
--
--- Lua code lives in one of three places:
--- 1. runtime/lua/vim/ (the runtime): For "nice to have" features, e.g. the
--- `inspect` and `lpeg` modules.
--- 2. runtime/lua/vim/shared.lua: pure Lua functions which always
--- are available. Used in the test runner, as well as worker threads
--- and processes launched from Nvim.
--- 3. runtime/lua/vim/_editor.lua: Code which directly interacts with
--- the Nvim editor state. Only available in the main thread.
+-- Lua code lives in one of four places:
+-- 1. Plugins! Not everything needs to live on "vim.*". Plugins are the correct model for
+-- non-essential features which the user may want to disable or replace with a third-party
+-- plugin. Examples: "editorconfig", "comment".
+-- - "opt-out": runtime/plugin/*.lua
+-- - "opt-in": runtime/pack/dist/opt/
+-- 2. runtime/lua/vim/ (the runtime): Lazy-loaded modules. Examples: `inspect`, `lpeg`.
+-- 3. runtime/lua/vim/shared.lua: pure Lua functions which always are available. Used in the test
+-- runner, as well as worker threads and processes launched from Nvim.
+-- 4. runtime/lua/vim/_editor.lua: Eager-loaded code which directly interacts with the Nvim
+-- editor state. Only available in the main thread.
--
--- Guideline: "If in doubt, put it in the runtime".
---
--- Most functions should live directly in `vim.`, not in submodules.
+-- The top level "vim.*" namespace is for fundamental Lua and editor features. Use submodules for
+-- everything else (but avoid excessive "nesting"), or plugins (see above).
--
-- Compatibility with Vim's `if_lua` is explicitly a non-goal.
--
@@ -19,9 +21,7 @@
-- - https://github.com/luafun/luafun
-- - https://github.com/rxi/lume
-- - http://leafo.net/lapis/reference/utilities.html
--- - https://github.com/torch/paths
-- - https://github.com/bakpakin/Fennel (pretty print, repl)
--- - https://github.com/howl-editor/howl/tree/master/lib/howl/util
-- These are for loading runtime modules lazily since they aren't available in
-- the nvim binary as specified in executor.c
diff --git a/runtime/lua/vim/_meta/api_keysets_extra.lua b/runtime/lua/vim/_meta/api_keysets_extra.lua
index 8faf5f49aa..81bce50746 100644
--- a/runtime/lua/vim/_meta/api_keysets_extra.lua
+++ b/runtime/lua/vim/_meta/api_keysets_extra.lua
@@ -23,7 +23,7 @@ error('Cannot require a meta file')
--- @field conceal? boolean
--- @field spell? boolean
--- @field ui_watched? boolean
---- @field url? boolean
+--- @field url? string
--- @field hl_mode? string
---
--- @field virt_text? [string, string][]
diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua
index 05b9fc2bbf..10b09333a8 100644
--- a/runtime/lua/vim/_meta/vimfn.lua
+++ b/runtime/lua/vim/_meta/vimfn.lua
@@ -5262,6 +5262,7 @@ function vim.fn.map(expr1, expr2) end
--- "lhsrawalt" The {lhs} of the mapping as raw bytes, alternate
--- form, only present when it differs from "lhsraw"
--- "rhs" The {rhs} of the mapping as typed.
+--- "callback" Lua function, if RHS was defined as such.
--- "silent" 1 for a |:map-silent| mapping, else 0.
--- "noremap" 1 if the {rhs} of the mapping is not remappable.
--- "script" 1 if mapping was defined with <script>.
diff --git a/runtime/lua/vim/health.lua b/runtime/lua/vim/health.lua
index 236f9da752..d183c82516 100644
--- a/runtime/lua/vim/health.lua
+++ b/runtime/lua/vim/health.lua
@@ -1,6 +1,6 @@
--- @brief
---<pre>help
---- health.vim is a minimal framework to help users troubleshoot configuration and
+--- vim.health is a minimal framework to help users troubleshoot configuration and
--- any other environment conditions that a plugin might care about. Nvim ships
--- with healthchecks for configuration, performance, python support, ruby
--- support, clipboard support, and more.
@@ -39,7 +39,7 @@
--- :checkhealth vim*
--- <
---
---- Create a healthcheck *health-dev* *vim.health*
+--- Create a healthcheck *health-dev*
---
--- Healthchecks are functions that check the user environment, configuration, or
--- any other prerequisites that a plugin cares about. Nvim ships with
diff --git a/runtime/lua/vim/iter.lua b/runtime/lua/vim/iter.lua
index 6bddf0bc5e..4bbcaf16db 100644
--- a/runtime/lua/vim/iter.lua
+++ b/runtime/lua/vim/iter.lua
@@ -6,10 +6,12 @@
--- of each pipeline stage is input to the next stage. The first stage depends on the type passed to
--- `vim.iter()`:
---
---- - List tables (arrays, |lua-list|) yield only the value of each element.
---- - Holes (nil values) are allowed.
+--- - Lists or arrays (|lua-list|) yield only the value of each element.
+--- - Holes (nil values) are allowed (but discarded).
+--- - Use pairs() to treat array/list tables as dicts (preserve holes and non-contiguous integer
+--- keys): `vim.iter(pairs(…))`.
--- - Use |Iter:enumerate()| to also pass the index to the next stage.
---- - Or initialize with ipairs(): `vim.iter(ipairs(…))`.
+--- - Or initialize with ipairs(): `vim.iter(ipairs(…))`.
--- - Non-list tables (|lua-dict|) yield both the key and value of each element.
--- - Function |iterator|s yield all values returned by the underlying function.
--- - Tables with a |__call()| metamethod are treated as function iterators.
@@ -1034,7 +1036,7 @@ function Iter.new(src, ...)
if type(k) ~= 'number' or k <= 0 or math.floor(k) ~= k then
return Iter.new(pairs(src))
end
- t[#t + 1] = v
+ t[#t + 1] = v -- Coerce to list-like table.
end
return ArrayIter.new(t)
end
diff --git a/runtime/lua/vim/lsp/completion.lua b/runtime/lua/vim/lsp/completion.lua
index 89d6d0e8b9..71ea2df100 100644
--- a/runtime/lua/vim/lsp/completion.lua
+++ b/runtime/lua/vim/lsp/completion.lua
@@ -597,7 +597,7 @@ end
--- @class vim.lsp.completion.BufferOpts
--- @field autotrigger? boolean Whether to trigger completion automatically. Default: false
---- @field convert? fun(item: lsp.CompletionItem): table An optional function used to customize the transformation of an LSP CompletionItem to |complete-items|.
+--- @field convert? fun(item: lsp.CompletionItem): table Transforms an LSP CompletionItem to |complete-items|.
---@param client_id integer
---@param bufnr integer
diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua
index 7acb67b25e..a9c6723094 100644
--- a/runtime/lua/vim/lsp/rpc.lua
+++ b/runtime/lua/vim/lsp/rpc.lua
@@ -627,12 +627,12 @@ end
---
--- - a named pipe (windows)
--- - a domain socket (unix)
---- - a host and port via TCP
+--- - a host and port via TCP (host must be IP address)
---
--- Return a function that can be passed to the `cmd` field for
--- |vim.lsp.start_client()| or |vim.lsp.start()|.
---
----@param host_or_path string host to connect to or path to a pipe/domain socket
+---@param host_or_path string host (IP address) 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
---@return fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient
function M.connect(host_or_path, port)
diff --git a/runtime/lua/vim/treesitter/_fold.lua b/runtime/lua/vim/treesitter/_fold.lua
index 27590eff5d..7375beaf9d 100644
--- a/runtime/lua/vim/treesitter/_fold.lua
+++ b/runtime/lua/vim/treesitter/_fold.lua
@@ -131,9 +131,7 @@ local function compute_folds_levels(bufnr, info, srow, erow, parse_injections)
-- Collect folds starting from srow - 1, because we should first subtract the folds that end at
-- srow - 1 from the level of srow - 1 to get accurate level of srow.
- for _, match, metadata in
- query:iter_matches(tree:root(), bufnr, math.max(srow - 1, 0), erow, { all = true })
- do
+ for _, match, metadata in query:iter_matches(tree:root(), bufnr, math.max(srow - 1, 0), erow) do
for id, nodes in pairs(match) do
if query.captures[id] == 'fold' then
local range = ts.get_range(nodes[1], bufnr, metadata[id])
diff --git a/runtime/lua/vim/treesitter/_query_linter.lua b/runtime/lua/vim/treesitter/_query_linter.lua
index 8654b89c9b..ea1ae5416a 100644
--- a/runtime/lua/vim/treesitter/_query_linter.lua
+++ b/runtime/lua/vim/treesitter/_query_linter.lua
@@ -176,7 +176,7 @@ function M.lint(buf, opts)
parser:parse()
parser:for_each_tree(function(tree, ltree)
if ltree:lang() == 'query' then
- for _, match, _ in query:iter_matches(tree:root(), buf, 0, -1, { all = true }) do
+ for _, match, _ in query:iter_matches(tree:root(), buf, 0, -1) do
local lang_context = {
lang = lang,
parser_info = parser_info,
diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua
index d43a0a5d0b..cc9ffeaa29 100644
--- a/runtime/lua/vim/treesitter/languagetree.lua
+++ b/runtime/lua/vim/treesitter/languagetree.lua
@@ -833,13 +833,7 @@ function LanguageTree:_get_injections()
local start_line, _, end_line, _ = root_node:range()
for pattern, match, metadata in
- self._injection_query:iter_matches(
- root_node,
- self._source,
- start_line,
- end_line + 1,
- { all = true }
- )
+ self._injection_query:iter_matches(root_node, self._source, start_line, end_line + 1)
do
local lang, combined, ranges = self:_get_injection(match, metadata)
if lang then
diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua
index 8e21353154..bd0574b8b7 100644
--- a/runtime/lua/vim/treesitter/query.lua
+++ b/runtime/lua/vim/treesitter/query.lua
@@ -620,8 +620,8 @@ local directive_handlers = {
--- @field force? boolean
---
--- Use the correct implementation of the match table where capture IDs map to
---- a list of nodes instead of a single node. Defaults to false (for backward
---- compatibility). This option will eventually become the default and removed.
+--- a list of nodes instead of a single node. Defaults to true. This option will
+--- be removed in a future release.
--- @field all? boolean
--- Adds a new predicate to be used in queries
@@ -629,7 +629,7 @@ local directive_handlers = {
---@param name string Name of the predicate, without leading #
---@param handler fun(match: table<integer,TSNode[]>, pattern: integer, source: integer|string, predicate: any[], metadata: vim.treesitter.query.TSMetadata)
--- - see |vim.treesitter.query.add_directive()| for argument meanings
----@param opts vim.treesitter.query.add_predicate.Opts
+---@param opts? vim.treesitter.query.add_predicate.Opts
function M.add_predicate(name, handler, opts)
-- Backward compatibility: old signature had "force" as boolean argument
if type(opts) == 'boolean' then
@@ -642,7 +642,7 @@ function M.add_predicate(name, handler, opts)
error(string.format('Overriding existing predicate %s', name))
end
- if opts.all then
+ if opts.all ~= false then
predicate_handlers[name] = handler
else
--- @param match table<integer, TSNode[]>
@@ -894,16 +894,10 @@ end
--- index of the pattern in the query, a table mapping capture indices to a list
--- of nodes, and metadata from any directives processing the match.
---
---- WARNING: Set `all=true` to ensure all matching nodes in a match are
---- returned, otherwise only the last node in a match is returned, breaking captures
---- involving quantifiers such as `(comment)+ @comment`. The default option
---- `all=false` is only provided for backward compatibility and will be removed
---- after Nvim 0.10.
----
--- Example:
---
--- ```lua
---- for pattern, match, metadata in cquery:iter_matches(tree:root(), bufnr, 0, -1, { all = true }) do
+--- for pattern, match, metadata in cquery:iter_matches(tree:root(), bufnr, 0, -1) do
--- for id, nodes in pairs(match) do
--- local name = query.captures[id]
--- for _, node in ipairs(nodes) do
@@ -925,9 +919,9 @@ end
--- - max_start_depth (integer) if non-zero, sets the maximum start depth
--- for each match. This is used to prevent traversing too deep into a tree.
--- - match_limit (integer) Set the maximum number of in-progress matches (Default: 256).
---- - all (boolean) When set, the returned match table maps capture IDs to a list of nodes.
---- Older versions of iter_matches incorrectly mapped capture IDs to a single node, which is
---- incorrect behavior. This option will eventually become the default and removed.
+--- - all (boolean) When `false` (default `true`), the returned table maps capture IDs to a single
+--- (last) node instead of the full list of matching nodes. This option is only for backward
+--- compatibility and will be removed in a future release.
---
---@return (fun(): integer, table<integer, TSNode[]>, vim.treesitter.query.TSMetadata): pattern id, match, metadata
function Query:iter_matches(node, source, start, stop, opts)
@@ -960,10 +954,10 @@ function Query:iter_matches(node, source, start, stop, opts)
local captures = match:captures()
- if not opts.all then
+ if opts.all == false then
-- Convert the match table into the old buggy version for backward
- -- compatibility. This is slow. Plugin authors, if you're reading this, set the "all"
- -- option!
+ -- compatibility. This is slow, but we only do it when the caller explicitly opted into it by
+ -- setting `all` to `false`.
local old_match = {} ---@type table<integer, TSNode>
for k, v in pairs(captures or {}) do
old_match[k] = v[#v]
diff --git a/runtime/lua/vim/ui.lua b/runtime/lua/vim/ui.lua
index b831a4f23e..2d3b828ea1 100644
--- a/runtime/lua/vim/ui.lua
+++ b/runtime/lua/vim/ui.lua
@@ -170,7 +170,7 @@ end
--- Returns all URLs at cursor, if any.
--- @return string[]
function M._get_urls()
- local urls = {}
+ local urls = {} ---@type string[]
local bufnr = vim.api.nvim_get_current_buf()
local cursor = vim.api.nvim_win_get_cursor(0)
@@ -183,7 +183,7 @@ function M._get_urls()
})
for _, v in ipairs(extmarks) do
local details = v[4]
- if details.url then
+ if details and details.url then
urls[#urls + 1] = details.url
end
end
@@ -195,15 +195,16 @@ function M._get_urls()
local lang = ltree:lang()
local query = vim.treesitter.query.get(lang, 'highlights')
if query then
- local tree = ltree:tree_for_range(range)
- for _, match, metadata in query:iter_matches(tree:root(), bufnr, row, row + 1, { all = true }) do
+ local tree = assert(ltree:tree_for_range(range))
+ for _, match, metadata in query:iter_matches(tree:root(), bufnr, row, row + 1) do
for id, nodes in pairs(match) do
for _, node in ipairs(nodes) do
if vim.treesitter.node_contains(node, range) then
local url = metadata[id] and metadata[id].url
if url and match[url] then
for _, n in ipairs(match[url]) do
- urls[#urls + 1] = vim.treesitter.get_node_text(n, bufnr, metadata[url])
+ urls[#urls + 1] =
+ vim.treesitter.get_node_text(n, bufnr, { metadata = metadata[url] })
end
end
end
diff --git a/runtime/syntax/sudoers.vim b/runtime/syntax/sudoers.vim
index bf2d337d9e..4cdf598be0 100644
--- a/runtime/syntax/sudoers.vim
+++ b/runtime/syntax/sudoers.vim
@@ -2,9 +2,10 @@
" Language: sudoers(5) configuration files
" Maintainer: Eisuke Kawashima ( e.kawaschima+vim AT gmail.com )
" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
-" Latest Revision: 2021 Mar 15
+" Latest Revision: 2024 Sep 02
" Recent Changes: Support for #include and #includedir.
" Added many new options (Samuel D. Leslie)
+" Update allowed Tag_Spec Runas_Spec syntax items
if exists("b:current_syntax")
finish
@@ -22,7 +23,7 @@ syn match sudoersUserSpec '^' nextgroup=@sudoersUserInSpec skipwhite
syn match sudoersSpecEquals contained '=' nextgroup=@sudoersCmndSpecList skipwhite
-syn cluster sudoersCmndSpecList contains=sudoersUserRunasBegin,sudoersPASSWD,@sudoersCmndInSpec
+syn cluster sudoersCmndSpecList contains=sudoersUserRunasBegin,sudoersTagSpec,@sudoersCmndInSpec
syn keyword sudoersTodo contained TODO FIXME XXX NOTE
@@ -92,10 +93,11 @@ syn cluster sudoersUserList contains=sudoersUserListComma,sudoersUserLis
syn match sudoersUserSpecComma contained ',' nextgroup=@sudoersUserInSpec skipwhite skipnl
syn cluster sudoersUserSpec contains=sudoersUserSpecComma,@sudoersHostInSpec
-syn match sudoersUserRunasBegin contained '(' nextgroup=@sudoersUserInRunas skipwhite skipnl
+syn match sudoersUserRunasBegin contained '(' nextgroup=@sudoersUserInRunas,sudoersUserRunasColon skipwhite skipnl
syn match sudoersUserRunasComma contained ',' nextgroup=@sudoersUserInRunas skipwhite skipnl
-syn match sudoersUserRunasEnd contained ')' nextgroup=sudoersPASSWD,@sudoersCmndInSpec skipwhite skipnl
-syn cluster sudoersUserRunas contains=sudoersUserRunasComma,@sudoersUserInRunas,sudoersUserRunasEnd
+syn match sudoersUserRunasColon contained ':' nextgroup=@sudoersUserInRunas skipwhite skipnl
+syn match sudoersUserRunasEnd contained ')' nextgroup=sudoersTagSpec,@sudoersCmndInSpec skipwhite skipnl
+syn cluster sudoersUserRunas contains=sudoersUserRunasComma,sudoersUserRunasColon,@sudoersUserInRunas,sudoersUserRunasEnd
syn match sudoersHostAliasEquals contained '=' nextgroup=@sudoersHostInList skipwhite skipnl
@@ -291,7 +293,7 @@ syn region sudoersStringValue contained start=+"+ skip=+\\"+ end=+"+ nextgroup
syn match sudoersListValue contained '[^[:space:],:=\\]*\%(\\[[:space:],:=\\][^[:space:],:=\\]*\)*' nextgroup=sudoersParameterListComma skipwhite skipnl
syn region sudoersListValue contained start=+"+ skip=+\\"+ end=+"+ nextgroup=sudoersParameterListComma skipwhite skipnl
-syn match sudoersPASSWD contained '\%(NO\)\=PASSWD:' nextgroup=@sudoersCmndInSpec skipwhite
+syn match sudoersTagSpec contained '\%(NO\)\=\%(EXEC\|FOLLOW\|LOG_\%(INPUT\|OUTPUT\)\|MAIL\|INTERCEPT\|PASSWD\|SETENV\):' nextgroup=sudoersTagSpec,@sudoersCmndInSpec skipwhite
hi def link sudoersSpecEquals Operator
hi def link sudoersTodo Todo
@@ -345,6 +347,7 @@ hi def link sudoersUserListColon Delimiter
hi def link sudoersUserSpecComma Delimiter
hi def link sudoersUserRunasBegin Delimiter
hi def link sudoersUserRunasComma Delimiter
+hi def link sudoersUserRunasColon Delimiter
hi def link sudoersUserRunasEnd Delimiter
hi def link sudoersHostAliasEquals Operator
hi def link sudoersHostListComma Delimiter
@@ -381,7 +384,7 @@ hi def link sudoersListParameterEquals Operator
hi def link sudoersIntegerValue Number
hi def link sudoersStringValue String
hi def link sudoersListValue String
-hi def link sudoersPASSWD Special
+hi def link sudoersTagSpec Special
hi def link sudoersInclude Statement
let b:current_syntax = "sudoers"
diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim
index f3d9c53f0b..89577ffaae 100644
--- a/runtime/syntax/vim.vim
+++ b/runtime/syntax/vim.vim
@@ -177,10 +177,11 @@ syn match vimNumber '\%(^\|\A\)\zs#\x\{6}' skipwhite nextgroup=vimGlobal,vimSub
syn case match
" All vimCommands are contained by vimIsCommand. {{{2
-syn cluster vimCmdList contains=vimAbb,vimAddress,vimAutoCmd,vimAugroup,vimBehave,vimCall,vimCatch,vimConst,vimDef,@vimEcho,vimEnddef,vimEndfunction,vimExecute,vimIsCommand,vimExtCmd,vimFor,vimFunction,vimGlobal,vimHighlight,vimLet,vimLoadkeymap,vimMap,vimMark,vimMatch,vimNotFunc,vimNorm,vimSet,vimSleep,vimSyntax,vimThrow,vimUnlet,vimUnmap,vimUserCmd,vimMenu,vimMenutranslate,@vim9CmdList
+syn cluster vimCmdList contains=vimAbb,vimAddress,vimAutoCmd,vimAugroup,vimBehave,vimCall,vimCatch,vimConst,vimDef,vimDelcommand,@vimEcho,vimEnddef,vimEndfunction,vimExecute,vimIsCommand,vimExtCmd,vimFor,vimFunction,vimGlobal,vimHighlight,vimLet,vimLoadkeymap,vimMap,vimMark,vimMatch,vimNotFunc,vimNorm,vimSet,vimSleep,vimSyntax,vimThrow,vimUnlet,vimUnmap,vimUserCmd,vimMenu,vimMenutranslate,@vim9CmdList
syn cluster vim9CmdList contains=vim9Const,vim9Final,vim9For,vim9Var
syn match vimCmdSep "[:|]\+" skipwhite nextgroup=@vimCmdList,vimSubst1
syn match vimIsCommand "\<\%(\h\w*\|[23]mat\%[ch]\)\>" contains=vimCommand
+syn match vimBang contained "!"
syn match vimVar contained "\<\h[a-zA-Z0-9#_]*\>"
syn match vimVar "\<[bwglstav]:\h[a-zA-Z0-9#_]*\>"
syn match vimVar "\s\zs&\%([lg]:\)\=\a\+\>"
@@ -353,33 +354,46 @@ syn match vimSpecFileMod "\(:[phtre]\)\+" contained
" User-Specified Commands: {{{2
" =======================
syn cluster vimUserCmdList contains=@vimCmdList,vimCmplxRepeat,@vimComment,vimCtrlChar,vimEscapeBrace,vimFunc,vimNotation,vimNumber,vimOper,vimRegister,vimSpecFile,vimString,vimSubst,vimSubstRep,vimSubstRange
-syn keyword vimUserCommand contained com[mand]
-syn match vimUserCmdName contained "\<\u\w*\>" nextgroup=vimUserCmdBlock skipwhite
-syn match vimUserCmd "\<com\%[mand]!\=\>.*$" contains=vimUserAttrb,vimUserAttrbError,vimUserCommand,@vimUserCmdList,vimComFilter,vimCmdBlock,vimUserCmdName
-syn match vimUserAttrbError contained "-\a\+\ze\s"
-syn match vimUserAttrb contained "-nargs=[01*?+]" contains=vimUserAttrbKey,vimOper
-syn match vimUserAttrb contained "-complete=" contains=vimUserAttrbKey,vimOper nextgroup=vimUserAttrbCmplt,vimUserCmdError
-syn match vimUserAttrb contained "-range\(=%\|=\d\+\)\=" contains=vimNumber,vimOper,vimUserAttrbKey
-syn match vimUserAttrb contained "-count\(=\d\+\)\=" contains=vimNumber,vimOper,vimUserAttrbKey
-syn match vimUserAttrb contained "-bang\>" contains=vimOper,vimUserAttrbKey
-syn match vimUserAttrb contained "-bar\>" contains=vimOper,vimUserAttrbKey
-syn match vimUserAttrb contained "-buffer\>" contains=vimOper,vimUserAttrbKey
-syn match vimUserAttrb contained "-register\>" contains=vimOper,vimUserAttrbKey
+syn keyword vimUserCmdKey contained com[mand]
+syn match vimUserCmdName contained "\<\u[[:alnum:]]*\>" skipwhite nextgroup=vimUserCmdBlock
+syn match vimUserCmd "\<com\%[mand]\>!\=.*$" contains=vimUserCmdKey,vimBang,vimUserCmdAttr,vimUserCmdAttrError,vimUserCmdName,@vimUserCmdList,vimComFilter
+syn match vimUserCmdAttrError contained "-\a\+\ze\%(\s\|=\)"
+syn match vimUserCmdAttr contained "-addr=" contains=vimUserCmdAttrKey nextgroup=vimUserCmdAttrAddr
+syn match vimUserCmdAttr contained "-bang\>" contains=vimUserCmdAttrKey
+syn match vimUserCmdAttr contained "-bar\>" contains=vimUserCmdAttrKey
+syn match vimUserCmdAttr contained "-buffer\>" contains=vimUserCmdAttrKey
+syn match vimUserCmdAttr contained "-complete=" contains=vimUserCmdAttrKey nextgroup=vimUserCmdAttrCmplt,vimUserCmdError
+syn match vimUserCmdAttr contained "-count\>" contains=vimUserCmdAttrKey
+syn match vimUserCmdAttr contained "-count=" contains=vimUserCmdAttrKey nextgroup=vimNumber
+syn match vimUserCmdAttr contained "-keepscript\>" contains=vimUserCmdAttrKey
+syn match vimUserCmdAttr contained "-nargs=" contains=vimUserCmdAttrKey nextgroup=vimUserCmdAttrNargs
+syn match vimUserCmdAttr contained "-range\>" contains=vimUserCmdAttrKey
+syn match vimUserCmdAttr contained "-range=" contains=vimUserCmdAttrKey nextgroup=vimNumber,vimUserCmdAttrRange
+syn match vimUserCmdAttr contained "-register\>" contains=vimUserCmdAttrKey
+
+syn match vimUserCmdAttrNargs contained "[01*?+]"
+syn match vimUserCmdAttrRange contained "%"
+
if !exists("g:vimsyn_noerror") && !exists("g:vimsyn_nousercmderror")
syn match vimUserCmdError contained "\S\+\>"
endif
-syn case ignore
-syn keyword vimUserAttrbKey contained bar ban[g] cou[nt] ra[nge] com[plete] n[args] re[gister]
-" GEN_SYN_VIM: vimUserAttrbCmplt, START_STR='syn keyword vimUserAttrbCmplt contained', END_STR=''
-syn keyword vimUserAttrbCmplt contained arglist augroup behave breakpoint buffer color command compiler cscope diff_buffer dir dir_in_path environment event expression file file_in_path filetype function help highlight history keymap locale mapclear mapping menu messages option packadd runtime scriptnames shellcmd sign syntax syntime tag tag_listfiles user var
-syn keyword vimUserAttrbCmplt contained custom customlist nextgroup=vimUserAttrbCmpltFunc,vimUserCmdError
-syn match vimUserAttrbCmpltFunc contained ",\%([sS]:\|<[sS][iI][dD]>\)\=\%(\h\w*\%([.#]\h\w*\)\+\|\h\w*\)"hs=s+1 nextgroup=vimUserCmdError
+syn case ignore
+syn keyword vimUserCmdAttrKey contained a[ddr] ban[g] bar bu[ffer] com[plete] cou[nt] k[eepscript] n[args] ra[nge] re[gister]
+" GEN_SYN_VIM: vimUserCmdAttrCmplt, START_STR='syn keyword vimUserCmdAttrCmplt contained', END_STR=''
+syn keyword vimUserCmdAttrCmplt contained arglist augroup behave breakpoint buffer color command compiler cscope diff_buffer dir dir_in_path environment event expression file file_in_path filetype function help highlight history keymap locale mapclear mapping menu messages option packadd runtime scriptnames shellcmd sign syntax syntime tag tag_listfiles user var
+syn keyword vimUserCmdAttrCmplt contained custom customlist nextgroup=vimUserCmdAttrCmpltFunc,vimUserCmdError
+syn match vimUserCmdAttrCmpltFunc contained ",\%([sS]:\|<[sS][iI][dD]>\)\=\%(\h\w*\%([.#]\h\w*\)\+\|\h\w*\)"hs=s+1 nextgroup=vimUserCmdError
+" GEN_SYN_VIM: vimUserCmdAttrAddr, START_STR='syn keyword vimUserCmdAttrAddr contained', END_STR=''
+syn keyword vimUserCmdAttrAddr contained arguments arg buffers buf lines line loaded_buffers load other quickfix qf tabs tab windows win
+syn match vimUserCmdAttrAddr contained "?"
syn case match
-syn match vimUserAttrbCmplt contained "custom,\u\w*"
syn region vimUserCmdBlock contained matchgroup=vimSep start="{" end="}" contains=@vimDefBodyList
+syn match vimDelcommand "\<delc\%[ommand]\>" skipwhite nextgroup=vimDelcommandAttr
+syn match vimDelcommandAttr contained "-buffer\>"
+
" Lower Priority Comments: after some vim commands... {{{2
" =======================
if get(g:, "vimsyn_comment_strings", 1)
@@ -1162,6 +1176,8 @@ if !exists("skip_vim_syntax_inits")
hi def link vimDefComment vim9Comment
hi def link vimDefKey vimCommand
hi def link vimDefParam vimVar
+ hi def link vimDelcommand vimCommand
+ hi def link vimDelcommandAttr vimUserCmdAttr
hi def link vimEcho vimCommand
hi def link vimEchohlNone vimGroup
hi def link vimEchohl vimCommand
@@ -1319,13 +1335,15 @@ if !exists("skip_vim_syntax_inits")
hi def link vimUnlet vimCommand
hi def link vimUnletBang vimBang
hi def link vimUnmap vimMap
- hi def link vimUserAttrbCmpltFunc Special
- hi def link vimUserAttrbCmplt vimSpecial
- hi def link vimUserAttrbKey vimOption
- hi def link vimUserAttrb vimSpecial
- hi def link vimUserAttrbError Error
+ hi def link vimUserCmdAttrAddr vimSpecial
+ hi def link vimUserCmdAttrCmplt vimSpecial
+ hi def link vimUserCmdAttrNargs vimSpecial
+ hi def link vimUserCmdAttrRange vimSpecial
+ hi def link vimUserCmdAttrKey vimUserCmdAttr
+ hi def link vimUserCmdAttr Special
+ hi def link vimUserCmdAttrError Error
hi def link vimUserCmdError Error
- hi def link vimUserCommand vimCommand
+ hi def link vimUserCmdKey vimCommand
hi def link vimUserFunc Normal
hi def link vimVar Identifier
hi def link vimWarn WarningMsg
diff --git a/scripts/gen_vimdoc.lua b/scripts/gen_vimdoc.lua
index dc384c12f5..aa09bc7dc7 100755
--- a/scripts/gen_vimdoc.lua
+++ b/scripts/gen_vimdoc.lua
@@ -373,8 +373,8 @@ local config = {
section_fmt = function(_name)
return 'Checkhealth'
end,
- helptag_fmt = function(name)
- return name:lower()
+ helptag_fmt = function()
+ return 'vim.health* *health' -- HACK
end,
},
}
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index a99d97acb8..5dc373acdc 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -93,15 +93,15 @@ void remote_ui_free_all_mem(void)
}
#endif
-/// Wait until ui has connected on stdio channel if only_stdio
-/// is true, otherwise any channel.
+/// Wait until UI has connected.
+///
+/// @param only_stdio UI is expected to connect on stdio.
void remote_ui_wait_for_attach(bool only_stdio)
{
if (only_stdio) {
Channel *channel = find_channel(CHAN_STDIO);
if (!channel) {
- // this function should only be called in --embed mode, stdio channel
- // can be assumed.
+ // `only_stdio` implies --embed mode, thus stdio channel can be assumed.
abort();
}
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 5087c2d36d..cbab735489 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -6442,6 +6442,7 @@ M.funcs = {
"lhsrawalt" The {lhs} of the mapping as raw bytes, alternate
form, only present when it differs from "lhsraw"
"rhs" The {rhs} of the mapping as typed.
+ "callback" Lua function, if RHS was defined as such.
"silent" 1 for a |:map-silent| mapping, else 0.
"noremap" 1 if the {rhs} of the mapping is not remappable.
"script" 1 if mapping was defined with <script>.
diff --git a/src/nvim/main.c b/src/nvim/main.c
index c507a201b0..a45ee81c81 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -332,12 +332,6 @@ int main(int argc, char **argv)
#endif
bool use_builtin_ui = (has_term && !headless_mode && !embedded_mode && !silent_mode);
- // don't bind the server yet, if we are using builtin ui.
- // This will be done when nvim server has been forked from the ui process
- if (!use_builtin_ui) {
- server_init(params.listen_addr);
- }
-
if (params.remote) {
remote_request(&params, params.remote, params.server_addr, argc, argv,
use_builtin_ui);
@@ -355,11 +349,19 @@ int main(int argc, char **argv)
ui_client_channel_id = rv;
}
+ // NORETURN: Start builtin UI client.
if (ui_client_channel_id) {
time_finish();
ui_client_run(remote_ui); // NORETURN
}
assert(!ui_client_channel_id && !use_builtin_ui);
+ // Nvim server...
+
+ int listen_rv = server_init(params.listen_addr);
+ if (listen_rv != 0) {
+ mainerr("Failed to --listen", listen_rv < 0
+ ? os_strerror(listen_rv) : (listen_rv == 1 ? "empty address" : NULL));
+ }
TIME_MSG("expanding arguments");
@@ -1434,9 +1436,9 @@ scripterror:
// On Windows expand "~\" or "~/" prefix in file names to profile directory.
#ifdef MSWIN
if (*p == '~' && (p[1] == '\\' || p[1] == '/')) {
- size_t size = strlen(os_get_homedir()) + strlen(p);
+ size_t size = strlen(os_homedir()) + strlen(p);
char *tilde_expanded = xmalloc(size);
- snprintf(tilde_expanded, size, "%s%s", os_get_homedir(), p + 1);
+ snprintf(tilde_expanded, size, "%s%s", os_homedir(), p + 1);
xfree(p);
p = tilde_expanded;
}
diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c
index 24bd343a34..ae34829181 100644
--- a/src/nvim/msgpack_rpc/server.c
+++ b/src/nvim/msgpack_rpc/server.c
@@ -28,27 +28,32 @@ static garray_T watchers = GA_EMPTY_INIT_VALUE;
#endif
/// Initializes the module
-bool server_init(const char *listen_addr)
+///
+/// @returns 0: success, 1: validation error, 2: already listening, -errno: failed to bind/listen.
+int server_init(const char *listen_addr)
{
+ bool must_free = false;
ga_init(&watchers, sizeof(SocketWatcher *), 1);
// $NVIM_LISTEN_ADDRESS (deprecated)
- if (!listen_addr && os_env_exists(ENV_LISTEN)) {
+ if ((!listen_addr || listen_addr[0] == '\0') && os_env_exists(ENV_LISTEN)) {
listen_addr = os_getenv(ENV_LISTEN);
}
- int rv = listen_addr ? server_start(listen_addr) : 1;
- if (0 != rv) {
+ if (!listen_addr || listen_addr[0] == '\0') {
listen_addr = server_address_new(NULL);
- if (!listen_addr) {
- return false;
- }
- rv = server_start(listen_addr);
- xfree((char *)listen_addr);
+ must_free = true;
+ }
+
+ if (!listen_addr) {
+ abort(); // Cannot happen.
}
+ int rv = server_start(listen_addr);
+
if (os_env_exists(ENV_LISTEN)) {
- // Unset $NVIM_LISTEN_ADDRESS, it's a liability hereafter.
+ // Unset $NVIM_LISTEN_ADDRESS, it's a liability hereafter. It is "input only", it must not be
+ // leaked to child jobs or :terminal.
os_unsetenv(ENV_LISTEN);
}
@@ -57,7 +62,11 @@ bool server_init(const char *listen_addr)
ELOG("test log message");
}
- return rv == 0;
+ if (must_free) {
+ xfree((char *)listen_addr);
+ }
+
+ return rv;
}
/// Teardown a single server
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index a4d5c02b5b..8dfedd073e 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -395,7 +395,21 @@ void os_get_hostname(char *hostname, size_t size)
#endif
}
-/// To get the "real" home directory:
+/// The "real" home directory as determined by `init_homedir`.
+static char *homedir = NULL;
+static char *os_uv_homedir(void);
+
+/// Gets the "real", resolved user home directory as determined by `init_homedir`.
+const char *os_homedir(void)
+{
+ if (!homedir) {
+ emsg("os_homedir failed: homedir not initialized");
+ return NULL;
+ }
+ return homedir;
+}
+
+/// Sets `homedir` to the "real", resolved user home directory, as follows:
/// 1. get value of $HOME
/// 2. if $HOME is not set, try the following
/// For Windows:
@@ -409,20 +423,6 @@ void os_get_hostname(char *hostname, size_t size)
/// This also works with mounts and links.
/// Don't do this for Windows, it will change the "current dir" for a drive.
/// 3. fall back to current working directory as a last resort
-static char *homedir = NULL;
-static char *os_uv_homedir(void);
-
-/// Public accessor for the cached "real", resolved user home directory. See
-/// comment on `homedir`.
-const char *os_get_homedir(void)
-{
- if (!homedir) {
- emsg("os_get_homedir failed: homedir not initialized");
- return NULL;
- }
- return homedir;
-}
-
void init_homedir(void)
{
// In case we are called a second time.
diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c
index c4eb2803f6..8affc58591 100644
--- a/src/nvim/os/input.c
+++ b/src/nvim/os/input.c
@@ -514,7 +514,7 @@ static void process_ctrl_c(void)
size_t available = input_available();
ssize_t i;
- for (i = (ssize_t)available - 1; i >= 0; i--) {
+ for (i = (ssize_t)available - 1; i >= 0; i--) { // Reverse-search input for Ctrl_C.
uint8_t c = (uint8_t)input_read_pos[i];
if (c == Ctrl_C
|| (c == 'C' && i >= 3
diff --git a/test/functional/core/fileio_spec.lua b/test/functional/core/fileio_spec.lua
index 1c4f42eb43..073041fced 100644
--- a/test/functional/core/fileio_spec.lua
+++ b/test/functional/core/fileio_spec.lua
@@ -325,7 +325,7 @@ describe('tmpdir', function()
before_each(function()
-- Fake /tmp dir so that we can mess it up.
- os_tmpdir = vim.uv.fs_mkdtemp(vim.fs.dirname(t.tmpname()) .. '/nvim_XXXXXXXXXX')
+ os_tmpdir = vim.uv.fs_mkdtemp(vim.fs.dirname(t.tmpname(false)) .. '/nvim_XXXXXXXXXX')
end)
after_each(function()
diff --git a/test/functional/ex_cmds/profile_spec.lua b/test/functional/ex_cmds/profile_spec.lua
index 57e5c6b2dc..365583948b 100644
--- a/test/functional/ex_cmds/profile_spec.lua
+++ b/test/functional/ex_cmds/profile_spec.lua
@@ -6,17 +6,11 @@ require('os')
local eval = n.eval
local command = n.command
local eq, neq = t.eq, t.neq
-local tempfile = t.tmpname()
+local tempfile = t.tmpname(false)
local source = n.source
local matches = t.matches
local read_file = t.read_file
--- tmpname() also creates the file on POSIX systems. Remove it again.
--- We just need the name, ignoring any race conditions.
-if uv.fs_stat(tempfile).uid then
- os.remove(tempfile)
-end
-
local function assert_file_exists(filepath)
neq(nil, uv.fs_stat(filepath).uid)
end
@@ -31,6 +25,7 @@ describe(':profile', function()
after_each(function()
n.expect_exit(command, 'qall!')
if uv.fs_stat(tempfile).uid ~= nil then
+ -- Delete the tempfile. We just need the name, ignoring any race conditions.
os.remove(tempfile)
end
end)
diff --git a/test/functional/lua/loader_spec.lua b/test/functional/lua/loader_spec.lua
index 7d71e33ced..06403e856c 100644
--- a/test/functional/lua/loader_spec.lua
+++ b/test/functional/lua/loader_spec.lua
@@ -74,8 +74,7 @@ describe('vim.loader', function()
vim.loader.enable()
]]
- local tmp = t.tmpname()
- assert(os.remove(tmp))
+ local tmp = t.tmpname(false)
assert(t.mkdir(tmp))
assert(t.mkdir(tmp .. '/%'))
local tmp1 = tmp .. '/%/x'
diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua
index a70f35e8e3..df68020d8e 100644
--- a/test/functional/lua/vim_spec.lua
+++ b/test/functional/lua/vim_spec.lua
@@ -145,10 +145,9 @@ describe('lua stdlib', function()
-- "0.10" or "0.10-dev+xxx"
local curstr = ('%s.%s%s'):format(curver.major, curver.minor, prerel or '')
eq(
- dedent(
- [[
- foo.bar() is deprecated. Run ":checkhealth vim.deprecated" for more information]]
- ):format(curstr),
+ ([[foo.bar() is deprecated. Run ":checkhealth vim.deprecated" for more information]]):format(
+ curstr
+ ),
exec_lua('return vim.deprecate(...)', 'foo.bar()', 'zub.wooo{ok=yay}', curstr)
)
-- Same message as above; skipped this time.
@@ -178,8 +177,7 @@ describe('lua stdlib', function()
it('plugin=nil, to be deleted in the next major version (1.0)', function()
eq(
- dedent [[
- foo.baz() is deprecated. Run ":checkhealth vim.deprecated" for more information]],
+ [[foo.baz() is deprecated. Run ":checkhealth vim.deprecated" for more information]],
exec_lua [[ return vim.deprecate('foo.baz()', nil, '1.0') ]]
)
end)
diff --git a/test/functional/lua/watch_spec.lua b/test/functional/lua/watch_spec.lua
index 3d2dda716e..ab6b1416aa 100644
--- a/test/functional/lua/watch_spec.lua
+++ b/test/functional/lua/watch_spec.lua
@@ -12,7 +12,6 @@ local skip = t.skip
-- events which can happen with some backends on some platforms
local function touch(path)
local tmp = t.tmpname()
- io.open(tmp, 'w'):close()
assert(vim.uv.fs_rename(tmp, path))
end
@@ -42,7 +41,7 @@ describe('vim._watch', function()
)
end
- local root_dir = vim.uv.fs_mkdtemp(vim.fs.dirname(t.tmpname()) .. '/nvim_XXXXXXXXXX')
+ local root_dir = vim.uv.fs_mkdtemp(vim.fs.dirname(t.tmpname(false)) .. '/nvim_XXXXXXXXXX')
local expected_events = 0
diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua
index f61139d92d..0faced5149 100644
--- a/test/functional/options/defaults_spec.lua
+++ b/test/functional/options/defaults_spec.lua
@@ -247,6 +247,7 @@ describe('startup defaults', function()
} })
eq('Xtest-logpath', eval('$NVIM_LOG_FILE'))
end)
+
it('defaults to stdpath("log")/log if empty', function()
eq(true, mkdir(xdgdir) and mkdir(xdgstatedir))
clear({
@@ -257,6 +258,7 @@ describe('startup defaults', function()
})
eq(xdgstatedir .. '/log', string.gsub(eval('$NVIM_LOG_FILE'), '\\', '/'))
end)
+
it('defaults to stdpath("log")/log if invalid', function()
eq(true, mkdir(xdgdir) and mkdir(xdgstatedir))
clear({
@@ -266,6 +268,8 @@ describe('startup defaults', function()
},
})
eq(xdgstatedir .. '/log', string.gsub(eval('$NVIM_LOG_FILE'), '\\', '/'))
+ -- Avoid "failed to open $NVIM_LOG_FILE" noise in test output.
+ expect_exit(command, 'qall!')
end)
end)
end)
@@ -339,9 +343,11 @@ describe('XDG defaults', function()
local state_dir = is_os('win') and 'nvim-data' or 'nvim'
local root_path = is_os('win') and 'C:' or ''
- describe('with too long XDG variables', function()
+ describe('with too long XDG vars', function()
before_each(function()
clear({
+ -- Ensure valid --listen address despite broken XDG vars (else Nvim won't start).
+ args = { '--listen', is_os('win') and '' or t.tmpname(false) },
args_rm = { 'runtimepath' },
env = {
NVIM_LOG_FILE = testlog,
@@ -361,6 +367,9 @@ describe('XDG defaults', function()
it('are correctly set', function()
if not is_os('win') then
+ -- Broken XDG vars cause serverstart() to fail (except on Windows, where servernames are not
+ -- informed by $XDG_STATE_HOME).
+ t.matches('Failed to start server: no such file or directory', t.pcall_err(fn.serverstart))
assert_log('Failed to start server: no such file or directory: /X/X/X', testlog, 10)
end
@@ -522,9 +531,11 @@ describe('XDG defaults', function()
end)
end)
- describe('with XDG variables that can be expanded', function()
+ describe('with expandable XDG vars', function()
before_each(function()
clear({
+ -- Ensure valid --listen address despite broken XDG vars (else Nvim won't start).
+ args = { '--listen', is_os('win') and '' or t.tmpname(false) },
args_rm = { 'runtimepath' },
env = {
NVIM_LOG_FILE = testlog,
@@ -544,6 +555,9 @@ describe('XDG defaults', function()
it('are not expanded', function()
if not is_os('win') then
+ -- Broken XDG vars cause serverstart() to fail (except on Windows, where servernames are not
+ -- informed by $XDG_STATE_HOME).
+ t.matches('Failed to start server: no such file or directory', t.pcall_err(fn.serverstart))
assert_log(
'Failed to start server: no such file or directory: %$XDG_RUNTIME_DIR%/',
testlog,
@@ -895,7 +909,7 @@ describe('stdpath()', function()
assert_alive() -- Check for crash. #8393
end)
- it('reacts to $NVIM_APPNAME', function()
+ it('supports $NVIM_APPNAME', function()
local appname = 'NVIM_APPNAME_TEST' .. ('_'):rep(106)
clear({ env = { NVIM_APPNAME = appname, NVIM_LOG_FILE = testlog } })
eq(appname, fn.fnamemodify(fn.stdpath('config'), ':t'))
@@ -916,7 +930,7 @@ describe('stdpath()', function()
local function test_appname(testAppname, expected_exitcode)
local lua_code = string.format(
[[
- local child = vim.fn.jobstart({ vim.v.progpath, '--clean', '--headless', '+qall!' }, { env = { NVIM_APPNAME = %q } })
+ local child = vim.fn.jobstart({ vim.v.progpath, '--clean', '--headless', '--listen', 'x', '+qall!' }, { env = { NVIM_APPNAME = %q } })
return vim.fn.jobwait({ child }, %d)[1]
]],
alter_slashes(testAppname),
@@ -935,9 +949,6 @@ describe('stdpath()', function()
-- Valid appnames:
test_appname('a/b', 0)
test_appname('a/b\\c', 0)
- if not is_os('win') then
- assert_log('Failed to start server: no such file or directory:', testlog)
- end
end)
describe('returns a String', function()
diff --git a/test/functional/plugin/lsp/completion_spec.lua b/test/functional/plugin/lsp/completion_spec.lua
index 16d64fc95d..d3796082fb 100644
--- a/test/functional/plugin/lsp/completion_spec.lua
+++ b/test/functional/plugin/lsp/completion_spec.lua
@@ -724,7 +724,7 @@ describe('vim.lsp.completion: protocol', function()
end)
end)
- it('custom word/abbar format', function()
+ it('enable(…,{convert=fn}) custom word/abbr format', function()
create_server({
isIncomplete = false,
items = {
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index 06e286f872..ff042424f9 100644
--- a/test/functional/plugin/lsp_spec.lua
+++ b/test/functional/plugin/lsp_spec.lua
@@ -800,8 +800,7 @@ describe('LSP', function()
eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler')
if ctx.method == 'start' then
local tmpfile_old = tmpname()
- local tmpfile_new = tmpname()
- os.remove(tmpfile_new)
+ local tmpfile_new = tmpname(false)
exec_lua(function(oldname, newname)
_G.BUFFER = vim.api.nvim_get_current_buf()
vim.api.nvim_buf_set_name(_G.BUFFER, oldname)
@@ -2370,8 +2369,7 @@ describe('LSP', function()
end)
it('Supports file creation with CreateFile payload', function()
- local tmpfile = tmpname()
- os.remove(tmpfile) -- Should not exist, only interested in a tmpname
+ local tmpfile = tmpname(false)
local uri = exec_lua('return vim.uri_from_fname(...)', tmpfile)
local edit = {
documentChanges = {
@@ -2388,9 +2386,7 @@ describe('LSP', function()
it(
'Supports file creation in folder that needs to be created with CreateFile payload',
function()
- local tmpfile = tmpname()
- os.remove(tmpfile) -- Should not exist, only interested in a tmpname
- tmpfile = tmpfile .. '/dummy/x/'
+ local tmpfile = tmpname(false) .. '/dummy/x/'
local uri = exec_lua('return vim.uri_from_fname(...)', tmpfile)
local edit = {
documentChanges = {
@@ -2468,8 +2464,7 @@ describe('LSP', function()
end)
it('DeleteFile fails if file does not exist and ignoreIfNotExists is false', function()
- local tmpfile = tmpname()
- os.remove(tmpfile)
+ local tmpfile = tmpname(false)
local uri = exec_lua('return vim.uri_from_fname(...)', tmpfile)
local edit = {
documentChanges = {
@@ -2493,8 +2488,7 @@ describe('LSP', function()
it('Can rename an existing file', function()
local old = tmpname()
write_file(old, 'Test content')
- local new = tmpname()
- os.remove(new) -- only reserve the name, file must not exist for the test scenario
+ local new = tmpname(false)
local lines = exec_lua(function(old0, new0)
local old_bufnr = vim.fn.bufadd(old0)
vim.fn.bufload(old_bufnr)
@@ -2514,10 +2508,8 @@ describe('LSP', function()
it('Can rename a directory', function()
-- only reserve the name, file must not exist for the test scenario
- local old_dir = tmpname()
- local new_dir = tmpname()
- os.remove(old_dir)
- os.remove(new_dir)
+ local old_dir = tmpname(false)
+ local new_dir = tmpname(false)
n.mkdir_p(old_dir)
@@ -2542,10 +2534,8 @@ describe('LSP', function()
end)
it('Does not touch buffers that do not match path prefix', function()
- local old = tmpname()
- local new = tmpname()
- os.remove(old)
- os.remove(new)
+ local old = tmpname(false)
+ local new = tmpname(false)
n.mkdir_p(old)
eq(
@@ -2604,8 +2594,7 @@ describe('LSP', function()
it('Maintains undo information for loaded buffer', function()
local old = tmpname()
write_file(old, 'line')
- local new = tmpname()
- os.remove(new)
+ local new = tmpname(false)
local undo_kept = exec_lua(function(old0, new0)
vim.opt.undofile = true
@@ -2629,8 +2618,7 @@ describe('LSP', function()
it('Maintains undo information for unloaded buffer', function()
local old = tmpname()
write_file(old, 'line')
- local new = tmpname()
- os.remove(new)
+ local new = tmpname(false)
local undo_kept = exec_lua(function(old0, new0)
vim.opt.undofile = true
@@ -2651,8 +2639,7 @@ describe('LSP', function()
it('Does not rename file when it conflicts with a buffer without file', function()
local old = tmpname()
write_file(old, 'Old File')
- local new = tmpname()
- os.remove(new)
+ local new = tmpname(false)
local lines = exec_lua(function(old0, new0)
local old_buf = vim.fn.bufadd(old0)
@@ -5023,13 +5010,7 @@ describe('LSP', function()
end)
it('can connect to lsp server via pipe or domain_socket', function()
- local tmpfile --- @type string
- if is_os('win') then
- tmpfile = '\\\\.\\\\pipe\\pipe.test'
- else
- tmpfile = tmpname()
- os.remove(tmpfile)
- end
+ local tmpfile = is_os('win') and '\\\\.\\\\pipe\\pipe.test' or tmpname(false)
local result = exec_lua(function(SOCK)
local uv = vim.uv
local server = assert(uv.new_pipe(false))
@@ -5135,9 +5116,7 @@ describe('LSP', function()
describe('#dynamic vim.lsp._dynamic', function()
it('supports dynamic registration', function()
- ---@type string
- local root_dir = tmpname()
- os.remove(root_dir)
+ local root_dir = tmpname(false)
mkdir(root_dir)
local tmpfile = root_dir .. '/dynamic.foo'
local file = io.open(tmpfile, 'w')
@@ -5264,8 +5243,7 @@ describe('LSP', function()
)
end
- local root_dir = tmpname()
- os.remove(root_dir)
+ local root_dir = tmpname(false)
mkdir(root_dir)
exec_lua(create_server_definition)
diff --git a/test/functional/plugin/man_spec.lua b/test/functional/plugin/man_spec.lua
index c0256d1c63..057748f51d 100644
--- a/test/functional/plugin/man_spec.lua
+++ b/test/functional/plugin/man_spec.lua
@@ -8,7 +8,6 @@ local exec_lua = n.exec_lua
local fn = n.fn
local nvim_prog = n.nvim_prog
local matches = t.matches
-local write_file = t.write_file
local tmpname = t.tmpname
local eq = t.eq
local pesc = vim.pesc
@@ -226,7 +225,6 @@ describe(':Man', function()
local actual_file = tmpname()
-- actual_file must be an absolute path to an existent file for us to test against it
matches('^/.+', actual_file)
- write_file(actual_file, '')
local args = { nvim_prog, '--headless', '+:Man ' .. actual_file, '+q' }
matches(
('Error detected while processing command line:\r\n' .. 'man.lua: "no manual entry for %s"'):format(
diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua
index 46e6a6002a..4aa8beebae 100644
--- a/test/functional/treesitter/parser_spec.lua
+++ b/test/functional/treesitter/parser_spec.lua
@@ -591,8 +591,7 @@ print()
vim.treesitter.query.parse('c', '((number_literal) @number (#set! "key" "value"))')
local parser = vim.treesitter.get_parser(0, 'c')
- local _, _, metadata =
- query:iter_matches(parser:parse()[1]:root(), 0, 0, -1, { all = true })()
+ local _, _, metadata = query:iter_matches(parser:parse()[1]:root(), 0, 0, -1)()
return metadata.key
end)
@@ -612,8 +611,7 @@ print()
)
local parser = vim.treesitter.get_parser(0, 'c')
- local _, _, metadata =
- query:iter_matches(parser:parse()[1]:root(), 0, 0, -1, { all = true })()
+ local _, _, metadata = query:iter_matches(parser:parse()[1]:root(), 0, 0, -1)()
local _, nested_tbl = next(metadata)
return nested_tbl.key
end)
@@ -633,8 +631,7 @@ print()
)
local parser = vim.treesitter.get_parser(0, 'c')
- local _, _, metadata =
- query:iter_matches(parser:parse()[1]:root(), 0, 0, -1, { all = true })()
+ local _, _, metadata = query:iter_matches(parser:parse()[1]:root(), 0, 0, -1)()
local _, nested_tbl = next(metadata)
return nested_tbl
end)
diff --git a/test/functional/treesitter/query_spec.lua b/test/functional/treesitter/query_spec.lua
index 00e8071738..d8338c1335 100644
--- a/test/functional/treesitter/query_spec.lua
+++ b/test/functional/treesitter/query_spec.lua
@@ -138,7 +138,7 @@ void ui_refresh(void)
local parser = vim.treesitter.get_parser(0, 'c')
local tree = parser:parse()[1]
local res = {}
- for pattern, match in cquery:iter_matches(tree:root(), 0, 7, 14, { all = true }) do
+ for pattern, match in cquery:iter_matches(tree:root(), 0, 7, 14) do
-- can't transmit node over RPC. just check the name and range
local mrepr = {}
for cid, nodes in pairs(match) do
@@ -211,7 +211,7 @@ void ui_refresh(void)
local parser = vim.treesitter.get_parser(0, 'c')
local tree = parser:parse()[1]
local res = {}
- for pattern, match in cquery:iter_matches(tree:root(), 0, 7, 14, { all = true }) do
+ for pattern, match in cquery:iter_matches(tree:root(), 0, 7, 14) do
-- can't transmit node over RPC. just check the name and range
local mrepr = {}
for cid, nodes in pairs(match) do
@@ -260,7 +260,7 @@ void ui_refresh(void)
local parser = vim.treesitter.get_parser(0, 'c')
local tree = parser:parse()[1]
local res = {}
- for pattern, match in cquery:iter_matches(tree:root(), 0, 7, 14, { all = true }) do
+ for pattern, match in cquery:iter_matches(tree:root(), 0, 7, 14) do
-- can't transmit node over RPC. just check the name and range
local mrepr = {}
for cid, nodes in pairs(match) do
@@ -307,7 +307,7 @@ void ui_refresh(void)
local parser = vim.treesitter.get_parser(0, 'c')
local tree = parser:parse()[1]
local res = {}
- for pattern, match in cquery:iter_matches(tree:root(), 0, 0, -1, { all = true }) do
+ for pattern, match in cquery:iter_matches(tree:root(), 0, 0, -1) do
-- can't transmit node over RPC. just check the name and range
local mrepr = {}
for cid, nodes in pairs(match) do
@@ -418,7 +418,7 @@ void ui_refresh(void)
local parser = vim.treesitter.get_parser(0, 'c')
local tree = parser:parse()[1]
local res = {}
- for pattern, match in cquery:iter_matches(tree:root(), 0, 0, -1, { all = true }) do
+ for pattern, match in cquery:iter_matches(tree:root(), 0, 0, -1) do
-- can't transmit node over RPC. just check the name and range
local mrepr = {}
for cid, nodes in pairs(match) do
@@ -464,11 +464,7 @@ void ui_refresh(void)
local parser = vim.treesitter.get_parser(0, 'c')
- -- Time bomb: update this in 0.12
- if vim.fn.has('nvim-0.12') == 1 then
- return 'Update this test to remove this message and { all = true } from add_predicate'
- end
- query.add_predicate('is-main?', is_main, { all = true })
+ query.add_predicate('is-main?', is_main)
local query0 = query.parse('c', custom_query0)
@@ -496,7 +492,7 @@ void ui_refresh(void)
local parser = vim.treesitter.get_parser(0, 'c')
- query.add_predicate('is-main?', is_main, true)
+ query.add_predicate('is-main?', is_main, { all = false, force = true })
local query0 = query.parse('c', custom_query0)
@@ -650,7 +646,7 @@ void ui_refresh(void)
local parser = vim.treesitter.get_parser(0, 'c')
local tree = parser:parse()[1]
local res = {}
- for pattern, match in cquery:iter_matches(tree:root(), 0, 7, 14) do
+ for pattern, match in cquery:iter_matches(tree:root(), 0, 7, 14, { all = false }) do
local mrepr = {}
for cid, node in pairs(match) do
table.insert(mrepr, { '@' .. cquery.captures[cid], node:type(), node:range() })
diff --git a/test/functional/vimscript/server_spec.lua b/test/functional/vimscript/server_spec.lua
index 4b0dc087f6..2dabe1afc1 100644
--- a/test/functional/vimscript/server_spec.lua
+++ b/test/functional/vimscript/server_spec.lua
@@ -4,7 +4,6 @@ local n = require('test.functional.testnvim')()
local assert_log = t.assert_log
local eq, neq, eval = t.eq, t.neq, n.eval
local clear, fn, api = n.clear, n.fn, n.api
-local ok = t.ok
local matches = t.matches
local pcall_err = t.pcall_err
local check_close = n.check_close
@@ -49,15 +48,6 @@ describe('server', function()
eq('', eval('$NVIM_LISTEN_ADDRESS'))
end)
- it('sets new v:servername if $NVIM_LISTEN_ADDRESS is invalid', function()
- clear({ env = { NVIM_LISTEN_ADDRESS = '.' } })
- -- Cleared on startup.
- eq('', eval('$NVIM_LISTEN_ADDRESS'))
- local servers = fn.serverlist()
- eq(1, #servers)
- ok(string.len(servers[1]) > 4) -- "~/.local/state/nvim…/…" or "\\.\pipe\…"
- end)
-
it('sets v:servername at startup or if all servers were stopped', function()
clear()
local initial_server = api.nvim_get_vvar('servername')
@@ -89,20 +79,26 @@ describe('server', function()
end)
it('serverstop() returns false for invalid input', function()
- clear { env = {
- NVIM_LOG_FILE = testlog,
- NVIM_LISTEN_ADDRESS = '.',
- } }
+ clear {
+ args_rm = { '--listen' },
+ env = {
+ NVIM_LOG_FILE = testlog,
+ NVIM_LISTEN_ADDRESS = '',
+ },
+ }
eq(0, eval("serverstop('')"))
eq(0, eval("serverstop('bogus-socket-name')"))
assert_log('Not listening on bogus%-socket%-name', testlog, 10)
end)
it('parses endpoints', function()
- clear { env = {
- NVIM_LOG_FILE = testlog,
- NVIM_LISTEN_ADDRESS = '.',
- } }
+ clear {
+ args_rm = { '--listen' },
+ env = {
+ NVIM_LOG_FILE = testlog,
+ NVIM_LISTEN_ADDRESS = '',
+ },
+ }
clear_serverlist()
eq({}, fn.serverlist())
@@ -178,18 +174,40 @@ end)
describe('startup --listen', function()
it('validates', function()
clear()
- local cmd = { unpack(n.nvim_argv) }
- table.insert(cmd, '--listen')
- matches('nvim.*: Argument missing after: "%-%-listen"', fn.system(cmd))
- cmd = { unpack(n.nvim_argv) }
- table.insert(cmd, '--listen2')
- matches('nvim.*: Garbage after option argument: "%-%-listen2"', fn.system(cmd))
+ -- Tests args with and without "--headless".
+ local function _test(args, expected)
+ -- XXX: clear v:shell_error, sigh...
+ fn.system({ n.nvim_prog, '-es', '+qall!' })
+ assert(0 == eval('v:shell_error'))
+ local cmd = vim.list_extend({ unpack(n.nvim_argv) }, vim.list_extend({ '--headless' }, args))
+ local output = fn.system(cmd)
+ assert(0 ~= eval('v:shell_error'))
+ -- TODO(justinmk): output not properly captured on Windows?
+ if is_os('win') then
+ return
+ end
+ matches(expected, output)
+ matches(expected, fn.system(vim.list_extend({ unpack(n.nvim_argv) }, args)))
+ end
+
+ _test({ '--listen' }, 'nvim.*: Argument missing after: "%-%-listen"')
+ _test({ '--listen2' }, 'nvim.*: Garbage after option argument: "%-%-listen2"')
+ _test({ '--listen', n.eval('v:servername') }, 'nvim.*: Failed to %-%-listen: ".* already .*"')
+ _test({ '--listen', '/' }, 'nvim.*: Failed to %-%-listen: ".*"')
+ _test(
+ { '--listen', 'https://example.com' },
+ ('nvim.*: Failed to %%-%%-listen: "%s"'):format(
+ (is_os('mac') or is_os('win')) and 'unknown node or service'
+ or 'service not available for socket type'
+ )
+ )
end)
it('sets v:servername, overrides $NVIM_LISTEN_ADDRESS', function()
local addr = (is_os('win') and [[\\.\pipe\Xtest-listen-pipe]] or './Xtest-listen-pipe')
clear({ env = { NVIM_LISTEN_ADDRESS = './Xtest-env-pipe' }, args = { '--listen', addr } })
+ eq('', eval('$NVIM_LISTEN_ADDRESS')) -- Cleared on startup.
eq(addr, api.nvim_get_vvar('servername'))
-- Address without slashes is a "name" which is appended to a generated path. #8519
diff --git a/test/testutil.lua b/test/testutil.lua
index 439f13cf49..01eaf25406 100644
--- a/test/testutil.lua
+++ b/test/testutil.lua
@@ -402,18 +402,27 @@ end
local tmpname_id = 0
local tmpdir = tmpdir_get()
---- Creates a new temporary file for use by tests.
-function M.tmpname()
+--- Generates a unique filepath for use by tests, in a test-specific "…/Xtest_tmpdir/T42.7"
+--- directory (which is cleaned up by the test runner), and writes the file unless `create=false`.
+---
+---@param create? boolean (default true) Write the file.
+function M.tmpname(create)
if tmpdir_is_local(tmpdir) then
-- Cannot control os.tmpname() dir, so hack our own tmpname() impl.
tmpname_id = tmpname_id + 1
-- "…/Xtest_tmpdir/T42.7"
local fname = ('%s/%s.%d'):format(tmpdir, (_G._nvim_test_id or 'nvim-test'), tmpname_id)
- io.open(fname, 'w'):close()
+ if create ~= false then
+ io.open(fname, 'w'):close()
+ end
return fname
end
local fname = os.tmpname()
+ if create == false then
+ os.remove(fname)
+ end
+
if M.is_os('win') and fname:sub(1, 2) == '\\s' then
-- In Windows tmpname() returns a filename starting with
-- special sequence \s, prepend $TEMP path