aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/lua/vim')
-rw-r--r--runtime/lua/vim/_comment.lua15
-rw-r--r--runtime/lua/vim/_defaults.lua264
-rw-r--r--runtime/lua/vim/_editor.lua106
-rw-r--r--runtime/lua/vim/_inspector.lua4
-rw-r--r--runtime/lua/vim/_meta.lua2
-rw-r--r--runtime/lua/vim/_meta/api.lua2290
-rw-r--r--runtime/lua/vim/_meta/api_keysets.lua3
-rw-r--r--runtime/lua/vim/_meta/api_keysets_extra.lua29
-rw-r--r--runtime/lua/vim/_meta/builtin.lua9
-rw-r--r--runtime/lua/vim/_meta/builtin_types.lua73
-rw-r--r--runtime/lua/vim/_meta/diff.lua26
-rw-r--r--runtime/lua/vim/_meta/lpeg.lua41
-rw-r--r--runtime/lua/vim/_meta/options.lua195
-rw-r--r--runtime/lua/vim/_meta/regex.lua26
-rw-r--r--runtime/lua/vim/_meta/spell.lua2
-rw-r--r--runtime/lua/vim/_meta/vimfn.lua1075
-rw-r--r--runtime/lua/vim/_options.lua12
-rw-r--r--runtime/lua/vim/_watch.lua88
-rw-r--r--runtime/lua/vim/deprecated/health.lua2
-rw-r--r--runtime/lua/vim/diagnostic.lua266
-rw-r--r--runtime/lua/vim/filetype.lua1405
-rw-r--r--runtime/lua/vim/filetype/detect.lua127
-rw-r--r--runtime/lua/vim/fs.lua84
-rw-r--r--runtime/lua/vim/glob.lua30
-rw-r--r--runtime/lua/vim/health.lua137
-rw-r--r--runtime/lua/vim/health/health.lua17
-rw-r--r--runtime/lua/vim/highlight.lua86
-rw-r--r--runtime/lua/vim/iter.lua12
-rw-r--r--runtime/lua/vim/keymap.lua22
-rw-r--r--runtime/lua/vim/loader.lua20
-rw-r--r--runtime/lua/vim/lsp.lua113
-rw-r--r--runtime/lua/vim/lsp/_completion.lua276
-rw-r--r--runtime/lua/vim/lsp/_dynamic.lua4
-rw-r--r--runtime/lua/vim/lsp/_meta/protocol.lua1556
-rw-r--r--runtime/lua/vim/lsp/_watchfiles.lua4
-rw-r--r--runtime/lua/vim/lsp/buf.lua71
-rw-r--r--runtime/lua/vim/lsp/client.lua53
-rw-r--r--runtime/lua/vim/lsp/codelens.lua8
-rw-r--r--runtime/lua/vim/lsp/completion.lua754
-rw-r--r--runtime/lua/vim/lsp/diagnostic.lua33
-rw-r--r--runtime/lua/vim/lsp/handlers.lua3
-rw-r--r--runtime/lua/vim/lsp/health.lua92
-rw-r--r--runtime/lua/vim/lsp/inlay_hint.lua41
-rw-r--r--runtime/lua/vim/lsp/log.lua4
-rw-r--r--runtime/lua/vim/lsp/protocol.lua382
-rw-r--r--runtime/lua/vim/lsp/rpc.lua16
-rw-r--r--runtime/lua/vim/lsp/semantic_tokens.lua16
-rw-r--r--runtime/lua/vim/lsp/sync.lua13
-rw-r--r--runtime/lua/vim/lsp/util.lua218
-rw-r--r--runtime/lua/vim/provider/health.lua259
-rw-r--r--runtime/lua/vim/secure.lua2
-rw-r--r--runtime/lua/vim/shared.lua297
-rw-r--r--runtime/lua/vim/snippet.lua80
-rw-r--r--runtime/lua/vim/text.lua15
-rw-r--r--runtime/lua/vim/treesitter.lua64
-rw-r--r--runtime/lua/vim/treesitter/_fold.lua36
-rw-r--r--runtime/lua/vim/treesitter/_meta.lua113
-rw-r--r--runtime/lua/vim/treesitter/_meta/misc.lua78
-rw-r--r--runtime/lua/vim/treesitter/_meta/tsnode.lua185
-rw-r--r--runtime/lua/vim/treesitter/_meta/tstree.lua44
-rw-r--r--runtime/lua/vim/treesitter/_query_linter.lua21
-rw-r--r--runtime/lua/vim/treesitter/_range.lua4
-rw-r--r--runtime/lua/vim/treesitter/dev.lua72
-rw-r--r--runtime/lua/vim/treesitter/health.lua3
-rw-r--r--runtime/lua/vim/treesitter/highlighter.lua15
-rw-r--r--runtime/lua/vim/treesitter/language.lua64
-rw-r--r--runtime/lua/vim/treesitter/languagetree.lua44
-rw-r--r--runtime/lua/vim/treesitter/query.lua43
-rw-r--r--runtime/lua/vim/ui.lua94
-rw-r--r--runtime/lua/vim/version.lua14
-rw-r--r--runtime/lua/vim/vimhelp.lua38
71 files changed, 7198 insertions, 4512 deletions
diff --git a/runtime/lua/vim/_comment.lua b/runtime/lua/vim/_comment.lua
index 044cd69716..de7f62632c 100644
--- a/runtime/lua/vim/_comment.lua
+++ b/runtime/lua/vim/_comment.lua
@@ -9,8 +9,8 @@
local function get_commentstring(ref_position)
local buf_cs = vim.bo.commentstring
- local has_ts_parser, ts_parser = pcall(vim.treesitter.get_parser)
- if not has_ts_parser then
+ local ts_parser = vim.treesitter.get_parser(0, '', { error = false })
+ if not ts_parser then
return buf_cs
end
@@ -194,14 +194,9 @@ local function toggle_lines(line_start, line_end, ref_position)
-- - Debatable for highlighting in text area (like LSP semantic tokens).
-- Mostly because it causes flicker as highlighting is preserved during
-- comment toggling.
- package.loaded['vim._comment']._lines = vim.tbl_map(f, lines)
- local lua_cmd = string.format(
- 'vim.api.nvim_buf_set_lines(0, %d, %d, false, package.loaded["vim._comment"]._lines)',
- line_start - 1,
- line_end
- )
- vim.cmd.lua({ lua_cmd, mods = { lockmarks = true } })
- package.loaded['vim._comment']._lines = nil
+ vim._with({ lockmarks = true }, function()
+ vim.api.nvim_buf_set_lines(0, line_start - 1, line_end, false, vim.tbl_map(f, lines))
+ end)
end
--- Operator which toggles user-supplied range of lines
diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua
index 5b964b84a0..6cad1dbca9 100644
--- a/runtime/lua/vim/_defaults.lua
+++ b/runtime/lua/vim/_defaults.lua
@@ -85,13 +85,13 @@ do
vim.keymap.set(
'x',
'Q',
- "mode() == 'V' ? ':normal! @<C-R>=reg_recorded()<CR><CR>' : 'Q'",
+ "mode() ==# 'V' ? ':normal! @<C-R>=reg_recorded()<CR><CR>' : 'Q'",
{ silent = true, expr = true, desc = ':help v_Q-default' }
)
vim.keymap.set(
'x',
'@',
- "mode() == 'V' ? ':normal! @'.getcharstr().'<CR>' : '@'",
+ "mode() ==# 'V' ? ':normal! @'.getcharstr().'<CR>' : '@'",
{ silent = true, expr = true, desc = ':help v_@-default' }
)
@@ -113,9 +113,11 @@ do
local gx_desc =
'Opens filepath or URI under cursor with the system handler (file explorer, web browser, …)'
vim.keymap.set({ 'n' }, 'gx', function()
- local err = do_open(require('vim.ui')._get_url())
- if err then
- vim.notify(err, vim.log.levels.ERROR)
+ for _, url in ipairs(require('vim.ui')._get_urls()) do
+ local err = do_open(url)
+ if err then
+ vim.notify(err, vim.log.levels.ERROR)
+ end
end
end, { desc = gx_desc })
vim.keymap.set({ 'x' }, 'gx', function()
@@ -180,12 +182,20 @@ do
--- See |[d-default|, |]d-default|, and |CTRL-W_d-default|.
do
vim.keymap.set('n', ']d', function()
- vim.diagnostic.goto_next({ float = false })
- end, { desc = 'Jump to the next diagnostic' })
+ vim.diagnostic.jump({ count = vim.v.count1 })
+ end, { desc = 'Jump to the next diagnostic in the current buffer' })
vim.keymap.set('n', '[d', function()
- vim.diagnostic.goto_prev({ float = false })
- end, { desc = 'Jump to the previous diagnostic' })
+ vim.diagnostic.jump({ count = -vim.v.count1 })
+ end, { desc = 'Jump to the previous diagnostic in the current buffer' })
+
+ vim.keymap.set('n', ']D', function()
+ vim.diagnostic.jump({ count = math.huge, wrap = false })
+ end, { desc = 'Jump to the last diagnostic in the current buffer' })
+
+ vim.keymap.set('n', '[D', function()
+ vim.diagnostic.jump({ count = -math.huge, wrap = false })
+ end, { desc = 'Jump to the first diagnostic in the current buffer' })
vim.keymap.set('n', '<C-W>d', function()
vim.diagnostic.open_float()
@@ -198,13 +208,187 @@ do
{ remap = true, desc = 'Show diagnostics under the cursor' }
)
end
+
+ --- vim-unimpaired style mappings. See: https://github.com/tpope/vim-unimpaired
+ do
+ -- Quickfix mappings
+ vim.keymap.set('n', '[q', function()
+ vim.cmd.cprevious({ count = vim.v.count1 })
+ end, {
+ desc = ':cprevious',
+ })
+
+ vim.keymap.set('n', ']q', function()
+ vim.cmd.cnext({ count = vim.v.count1 })
+ end, {
+ desc = ':cnext',
+ })
+
+ vim.keymap.set('n', '[Q', function()
+ vim.cmd.crewind({ count = vim.v.count ~= 0 and vim.v.count or nil })
+ end, {
+ desc = ':crewind',
+ })
+
+ vim.keymap.set('n', ']Q', function()
+ vim.cmd.clast({ count = vim.v.count ~= 0 and vim.v.count or nil })
+ end, {
+ desc = ':clast',
+ })
+
+ vim.keymap.set('n', '[<C-Q>', function()
+ vim.cmd.cpfile({ count = vim.v.count1 })
+ end, {
+ desc = ':cpfile',
+ })
+
+ vim.keymap.set('n', ']<C-Q>', function()
+ vim.cmd.cnfile({ count = vim.v.count1 })
+ end, {
+ desc = ':cnfile',
+ })
+
+ -- Location list mappings
+ vim.keymap.set('n', '[l', function()
+ vim.cmd.lprevious({ count = vim.v.count1 })
+ end, {
+ desc = ':lprevious',
+ })
+
+ vim.keymap.set('n', ']l', function()
+ vim.cmd.lnext({ count = vim.v.count1 })
+ end, {
+ desc = ':lnext',
+ })
+
+ vim.keymap.set('n', '[L', function()
+ vim.cmd.lrewind({ count = vim.v.count ~= 0 and vim.v.count or nil })
+ end, {
+ desc = ':lrewind',
+ })
+
+ vim.keymap.set('n', ']L', function()
+ vim.cmd.llast({ count = vim.v.count ~= 0 and vim.v.count or nil })
+ end, {
+ desc = ':llast',
+ })
+
+ vim.keymap.set('n', '[<C-L>', function()
+ vim.cmd.lpfile({ count = vim.v.count1 })
+ end, {
+ desc = ':lpfile',
+ })
+
+ vim.keymap.set('n', ']<C-L>', function()
+ vim.cmd.lnfile({ count = vim.v.count1 })
+ end, {
+ desc = ':lnfile',
+ })
+
+ -- Argument list
+ vim.keymap.set('n', '[a', function()
+ vim.cmd.previous({ count = vim.v.count1 })
+ end, {
+ desc = ':previous',
+ })
+
+ vim.keymap.set('n', ']a', function()
+ -- count doesn't work with :next, must use range. See #30641.
+ vim.cmd.next({ range = { vim.v.count1 } })
+ end, {
+ desc = ':next',
+ })
+
+ vim.keymap.set('n', '[A', function()
+ if vim.v.count ~= 0 then
+ vim.cmd.argument({ count = vim.v.count })
+ else
+ vim.cmd.rewind()
+ end
+ end, {
+ desc = ':rewind',
+ })
+
+ vim.keymap.set('n', ']A', function()
+ if vim.v.count ~= 0 then
+ vim.cmd.argument({ count = vim.v.count })
+ else
+ vim.cmd.last()
+ end
+ end, {
+ desc = ':last',
+ })
+
+ -- Tags
+ vim.keymap.set('n', '[t', function()
+ -- count doesn't work with :tprevious, must use range. See #30641.
+ vim.cmd.tprevious({ range = { vim.v.count1 } })
+ end, { desc = ':tprevious' })
+
+ vim.keymap.set('n', ']t', function()
+ -- count doesn't work with :tnext, must use range. See #30641.
+ vim.cmd.tnext({ range = { vim.v.count1 } })
+ end, { desc = ':tnext' })
+
+ vim.keymap.set('n', '[T', function()
+ -- count doesn't work with :trewind, must use range. See #30641.
+ vim.cmd.trewind({ range = vim.v.count ~= 0 and { vim.v.count } or nil })
+ end, { desc = ':trewind' })
+
+ vim.keymap.set('n', ']T', function()
+ -- :tlast does not accept a count, so use :trewind if count given
+ if vim.v.count ~= 0 then
+ vim.cmd.trewind({ range = { vim.v.count } })
+ else
+ vim.cmd.tlast()
+ end
+ end, { desc = ':tlast' })
+
+ vim.keymap.set('n', '[<C-T>', function()
+ -- count doesn't work with :ptprevious, must use range. See #30641.
+ vim.cmd.ptprevious({ range = { vim.v.count1 } })
+ end, { desc = ' :ptprevious' })
+
+ vim.keymap.set('n', ']<C-T>', function()
+ -- count doesn't work with :ptnext, must use range. See #30641.
+ vim.cmd.ptnext({ range = { vim.v.count1 } })
+ end, { desc = ':ptnext' })
+
+ -- Buffers
+ vim.keymap.set('n', '[b', function()
+ vim.cmd.bprevious({ count = vim.v.count1 })
+ end, { desc = ':bprevious' })
+
+ vim.keymap.set('n', ']b', function()
+ vim.cmd.bnext({ count = vim.v.count1 })
+ end, { desc = ':bnext' })
+
+ vim.keymap.set('n', '[B', function()
+ if vim.v.count ~= 0 then
+ vim.cmd.buffer({ count = vim.v.count })
+ else
+ vim.cmd.brewind()
+ end
+ end, { desc = ':brewind' })
+
+ vim.keymap.set('n', ']B', function()
+ if vim.v.count ~= 0 then
+ vim.cmd.buffer({ count = vim.v.count })
+ else
+ vim.cmd.blast()
+ end
+ end, { desc = ':blast' })
+ end
end
--- Default menus
do
--- Right click popup menu
- -- TODO VimScript, no l10n
vim.cmd([[
+ anoremenu PopUp.Go\ to\ definition <Cmd>lua vim.lsp.buf.definition()<CR>
+ amenu PopUp.Open\ in\ web\ browser gx
+ anoremenu PopUp.Inspect <Cmd>Inspect<CR>
+ anoremenu PopUp.-1- <Nop>
vnoremenu PopUp.Cut "+x
vnoremenu PopUp.Copy "+y
anoremenu PopUp.Paste "+gP
@@ -213,10 +397,36 @@ do
nnoremenu PopUp.Select\ All ggVG
vnoremenu PopUp.Select\ All gg0oG$
inoremenu PopUp.Select\ All <C-Home><C-O>VG
- anoremenu PopUp.Inspect <Cmd>Inspect<CR>
- anoremenu PopUp.-1- <Nop>
+ anoremenu PopUp.-2- <Nop>
anoremenu PopUp.How-to\ disable\ mouse <Cmd>help disable-mouse<CR>
]])
+
+ local function enable_ctx_menu(ctx)
+ vim.cmd([[
+ amenu disable PopUp.Go\ to\ definition
+ amenu disable PopUp.Open\ in\ web\ browser
+ ]])
+
+ if ctx == 'url' then
+ vim.cmd([[amenu enable PopUp.Open\ in\ web\ browser]])
+ elseif ctx == 'lsp' then
+ vim.cmd([[anoremenu enable PopUp.Go\ to\ definition]])
+ end
+ end
+
+ local nvim_popupmenu_augroup = vim.api.nvim_create_augroup('nvim_popupmenu', {})
+ vim.api.nvim_create_autocmd('MenuPopup', {
+ pattern = '*',
+ group = nvim_popupmenu_augroup,
+ desc = 'Mouse popup menu',
+ -- nested = true,
+ callback = function()
+ local urls = require('vim.ui')._get_urls()
+ local url = vim.startswith(urls[1], 'http')
+ local ctx = url and 'url' or (vim.lsp.get_clients({ bufnr = 0 })[1] and 'lsp' or nil)
+ enable_ctx_menu(ctx)
+ end,
+ })
end
--- Default autocommands. See |default-autocmds|
@@ -274,6 +484,26 @@ do
end,
})
+ vim.api.nvim_create_autocmd('TermOpen', {
+ group = nvim_terminal_augroup,
+ desc = 'Default settings for :terminal buffers',
+ callback = function()
+ vim.bo.modifiable = false
+ vim.bo.undolevels = -1
+ vim.bo.scrollback = vim.o.scrollback < 0 and 10000 or math.max(1, vim.o.scrollback)
+ vim.bo.textwidth = 0
+ vim.wo[0][0].wrap = false
+ vim.wo[0][0].list = false
+
+ -- This is gross. Proper list options support when?
+ local winhl = vim.o.winhighlight
+ if winhl ~= '' then
+ winhl = winhl .. ','
+ end
+ vim.wo[0][0].winhighlight = winhl .. 'StatusLine:StatusLineTerm,StatusLineNC:StatusLineTermNC'
+ end,
+ })
+
vim.api.nvim_create_autocmd('CmdwinEnter', {
pattern = '[:>]',
desc = 'Limit syntax sync to maxlines=1 in the command window',
@@ -462,10 +692,14 @@ do
--- response indicates that it does support truecolor enable 'termguicolors',
--- but only if the user has not already disabled it.
do
- if tty.rgb then
- -- The TUI was able to determine truecolor support
+ local colorterm = os.getenv('COLORTERM')
+ if tty.rgb or colorterm == 'truecolor' or colorterm == '24bit' then
+ -- The TUI was able to determine truecolor support or $COLORTERM explicitly indicates
+ -- truecolor support
setoption('termguicolors', true)
- else
+ elseif colorterm == nil or colorterm == '' then
+ -- Neither the TUI nor $COLORTERM indicate that truecolor is supported, so query the
+ -- terminal
local caps = {} ---@type table<string, boolean>
require('vim.termcap').query({ 'Tc', 'RGB', 'setrgbf', 'setrgbb' }, function(cap, found)
if not found then
diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua
index 5e9be509c8..2e829578a7 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
@@ -208,8 +208,10 @@ vim.inspect = vim.inspect
do
local tdots, tick, got_line1, undo_started, trailing_nl = 0, 0, false, false, false
- --- Paste handler, invoked by |nvim_paste()| when a conforming UI
- --- (such as the |TUI|) pastes text into the editor.
+ --- Paste handler, invoked by |nvim_paste()|.
+ ---
+ --- Note: This is provided only as a "hook", don't call it directly; call |nvim_paste()| instead,
+ --- which arranges redo (dot-repeat) and invokes `vim.paste`.
---
--- Example: To remove ANSI color codes when pasting:
---
@@ -220,7 +222,7 @@ do
--- -- Scrub ANSI color codes from paste input.
--- lines[i] = line:gsub('\27%[[0-9;mK]+', '')
--- end
- --- overridden(lines, phase)
+ --- return overridden(lines, phase)
--- end
--- end)(vim.paste)
--- ```
@@ -494,6 +496,7 @@ do
vim.t = make_dict_accessor('t')
end
+--- @deprecated
--- Gets a dict of line segment ("chunk") positions for the region from `pos1` to `pos2`.
---
--- Input and output positions are byte positions, (0,0)-indexed. "End of line" column
@@ -507,6 +510,8 @@ end
---@return table region Dict of the form `{linenr = {startcol,endcol}}`. `endcol` is exclusive, and
---whole lines are returned as `{startcol,endcol} = {0,-1}`.
function vim.region(bufnr, pos1, pos2, regtype, inclusive)
+ vim.deprecate('vim.region', 'vim.fn.getregionpos()', '0.13')
+
if not vim.api.nvim_buf_is_loaded(bufnr) then
vim.fn.bufload(bufnr)
end
@@ -605,10 +610,9 @@ end
--- Displays a notification to the user.
---
---- This function can be overridden by plugins to display notifications using a
---- custom provider (such as the system notification provider). By default,
+--- This function can be overridden by plugins to display notifications using
+--- a custom provider (such as the system notification provider). By default,
--- writes to |:messages|.
----
---@param msg string Content of the notification to show to the user.
---@param level integer|nil One of the values from |vim.log.levels|.
---@param opts table|nil Optional parameters. Unused by default.
@@ -783,7 +787,7 @@ function vim._expand_pat(pat, env)
if mt and type(mt.__index) == 'table' then
field = rawget(mt.__index, key)
elseif final_env == vim and (vim._submodules[key] or vim._extra[key]) then
- field = vim[key]
+ field = vim[key] --- @type any
end
end
final_env = field
@@ -794,14 +798,24 @@ function vim._expand_pat(pat, env)
end
local keys = {} --- @type table<string,true>
+
--- @param obj table<any,any>
local function insert_keys(obj)
for k, _ in pairs(obj) do
- if type(k) == 'string' and string.sub(k, 1, string.len(match_part)) == match_part then
+ if
+ type(k) == 'string'
+ and string.sub(k, 1, string.len(match_part)) == match_part
+ and k:match('^[_%w]+$') ~= nil -- filter out invalid identifiers for field, e.g. 'foo#bar'
+ then
keys[k] = true
end
end
end
+ ---@param acc table<string,any>
+ local function _fold_to_map(acc, k, v)
+ acc[k] = (v or true)
+ return acc
+ end
if type(final_env) == 'table' then
insert_keys(final_env)
@@ -810,11 +824,61 @@ function vim._expand_pat(pat, env)
if mt and type(mt.__index) == 'table' then
insert_keys(mt.__index)
end
+
if final_env == vim then
insert_keys(vim._submodules)
insert_keys(vim._extra)
end
+ -- Completion for dict accessors (special vim variables and vim.fn)
+ if mt and vim.tbl_contains({ vim.g, vim.t, vim.w, vim.b, vim.v, vim.fn }, final_env) then
+ local prefix, type = unpack(
+ vim.fn == final_env and { '', 'function' }
+ or vim.g == final_env and { 'g:', 'var' }
+ or vim.t == final_env and { 't:', 'var' }
+ or vim.w == final_env and { 'w:', 'var' }
+ or vim.b == final_env and { 'b:', 'var' }
+ or vim.v == final_env and { 'v:', 'var' }
+ or { nil, nil }
+ )
+ assert(prefix, "Can't resolve final_env")
+ local vars = vim.fn.getcompletion(prefix .. match_part, type) --- @type string[]
+ insert_keys(vim
+ .iter(vars)
+ :map(function(s) ---@param s string
+ s = s:gsub('[()]+$', '') -- strip '(' and ')' for function completions
+ return s:sub(#prefix + 1) -- strip the prefix, e.g., 'g:foo' => 'foo'
+ end)
+ :fold({}, _fold_to_map))
+ end
+
+ -- Completion for option accessors (full names only)
+ if
+ mt
+ and vim.tbl_contains(
+ { vim.o, vim.go, vim.bo, vim.wo, vim.opt, vim.opt_local, vim.opt_global },
+ final_env
+ )
+ then
+ --- @type fun(option_name: string, option: vim.api.keyset.get_option_info): boolean
+ local filter = function(_, _)
+ return true
+ end
+ if vim.bo == final_env then
+ filter = function(_, option)
+ return option.scope == 'buf'
+ end
+ elseif vim.wo == final_env then
+ filter = function(_, option)
+ return option.scope == 'win'
+ end
+ end
+
+ --- @type table<string, vim.api.keyset.get_option_info>
+ local options = vim.api.nvim_get_all_options_info()
+ insert_keys(vim.iter(options):filter(filter):fold({}, _fold_to_map))
+ end
+
keys = vim.tbl_keys(keys)
table.sort(keys)
diff --git a/runtime/lua/vim/_inspector.lua b/runtime/lua/vim/_inspector.lua
index f5d1640c82..fccf4b8dbe 100644
--- a/runtime/lua/vim/_inspector.lua
+++ b/runtime/lua/vim/_inspector.lua
@@ -27,6 +27,7 @@ local defaults = {
---
---Can also be pretty-printed with `:Inspect!`. [:Inspect!]()
---
+---@since 11
---@param bufnr? integer defaults to the current buffer
---@param row? integer row to inspect, 0-based. Defaults to the row of the current cursor
---@param col? integer col to inspect, 0-based. Defaults to the col of the current cursor
@@ -84,7 +85,7 @@ function vim.inspect_pos(bufnr, row, col, filter)
-- syntax
if filter.syntax and vim.api.nvim_buf_is_valid(bufnr) then
- vim.api.nvim_buf_call(bufnr, function()
+ vim._with({ buf = bufnr }, function()
for _, i1 in ipairs(vim.fn.synstack(row + 1, col + 1)) do
results.syntax[#results.syntax + 1] =
resolve_hl({ hl_group = vim.fn.synIDattr(i1, 'name') })
@@ -145,6 +146,7 @@ end
---
---Can also be shown with `:Inspect`. [:Inspect]()
---
+---@since 11
---@param bufnr? integer defaults to the current buffer
---@param row? integer row to inspect, 0-based. Defaults to the row of the current cursor
---@param col? integer col to inspect, 0-based. Defaults to the col of the current cursor
diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua
index 731dd5b923..c9f207cb20 100644
--- a/runtime/lua/vim/_meta.lua
+++ b/runtime/lua/vim/_meta.lua
@@ -34,3 +34,5 @@ vim.uri_from_fname = uri.uri_from_fname
vim.uri_from_bufnr = uri.uri_from_bufnr
vim.uri_to_fname = uri.uri_to_fname
vim.uri_to_bufnr = uri.uri_to_bufnr
+
+vim.provider = require('vim.provider')
diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua
index 6edf2a5a96..c66b295d3a 100644
--- a/runtime/lua/vim/_meta/api.lua
+++ b/runtime/lua/vim/_meta/api.lua
@@ -20,14 +20,15 @@ function vim.api.nvim__buf_stats(buffer) end
--- @private
--- EXPERIMENTAL: this API may change in the future.
---
---- Sets info for the completion item at the given index. If the info text was
---- shown in a window, returns the window and buffer ids, or empty dict if not
---- shown.
+--- Sets info for the completion item at the given index. If the info text was shown in a window,
+--- returns the window and buffer ids, or empty dict if not shown.
---
--- @param index integer Completion candidate index
--- @param opts vim.api.keyset.complete_set Optional parameters.
---- • info: (string) info text.
---- @return table<string,any>
+--- - info: (string) info text.
+--- @return table<string,any> # Dict containing these keys:
+--- - winid: (number) floating window id
+--- - bufnr: (number) buffer id in floating window
function vim.api.nvim__complete_set(index, opts) end
--- @private
@@ -40,7 +41,7 @@ function vim.api.nvim__get_lib_dir() end
--- @param pat any[] pattern of files to search for
--- @param all boolean whether to return all matches or only the first
--- @param opts vim.api.keyset.runtime is_lua: only search Lua subdirs
---- @return string[]
+--- @return string[] # list of absolute paths to the found files
function vim.api.nvim__get_runtime(pat, all, opts) end
--- @private
@@ -50,7 +51,7 @@ function vim.api.nvim__get_runtime(pat, all, opts) end
--- in plugins.
---
--- @param obj any Object to return.
---- @return any
+--- @return any # its argument.
function vim.api.nvim__id(obj) end
--- @private
@@ -60,18 +61,18 @@ function vim.api.nvim__id(obj) end
--- in plugins.
---
--- @param arr any[] Array to return.
---- @return any[]
+--- @return any[] # its argument.
function vim.api.nvim__id_array(arr) end
--- @private
---- Returns dictionary given as argument.
+--- Returns dict given as argument.
---
--- This API function is used for testing. One should not rely on its presence
--- in plugins.
---
---- @param dct table<string,any> Dictionary to return.
---- @return table<string,any>
-function vim.api.nvim__id_dictionary(dct) end
+--- @param dct table<string,any> Dict to return.
+--- @return table<string,any> # its argument.
+function vim.api.nvim__id_dict(dct) end
--- @private
--- Returns floating-point value given as argument.
@@ -80,13 +81,11 @@ function vim.api.nvim__id_dictionary(dct) end
--- in plugins.
---
--- @param flt number Value to return.
---- @return number
+--- @return number # its argument.
function vim.api.nvim__id_float(flt) end
--- @private
---- NB: if your UI doesn't use hlstate, this will not return hlstate first
---- time.
----
+--- NB: if your UI doesn't use hlstate, this will not return hlstate first time.
--- @param grid integer
--- @param row integer
--- @param col integer
@@ -94,35 +93,55 @@ function vim.api.nvim__id_float(flt) end
function vim.api.nvim__inspect_cell(grid, row, col) end
--- @private
---- For testing. The condition in schar_cache_clear_if_full is hard to reach,
---- so this function can be used to force a cache clear in a test.
----
+--- For testing. The condition in schar_cache_clear_if_full is hard to
+--- reach, so this function can be used to force a cache clear in a test.
function vim.api.nvim__invalidate_glyph_cache() end
--- @private
+--- EXPERIMENTAL: this API will change in the future.
+---
+--- Get the properties for namespace
+---
+--- @param ns_id integer Namespace
+--- @return vim.api.keyset.ns_opts # Map defining the namespace properties, see |nvim__ns_set()|
+function vim.api.nvim__ns_get(ns_id) end
+
+--- @private
+--- EXPERIMENTAL: this API will change in the future.
+---
+--- Set some properties for namespace
+---
+--- @param ns_id integer Namespace
+--- @param opts vim.api.keyset.ns_opts Optional parameters to set:
+--- - wins: a list of windows to be scoped in
+function vim.api.nvim__ns_set(ns_id, opts) end
+
+--- @private
--- EXPERIMENTAL: this API may change in the future.
---
--- Instruct Nvim to redraw various components.
---
+---
+--- @see `:help :redraw`
--- @param opts vim.api.keyset.redraw Optional parameters.
---- • win: Target a specific `window-ID` as described below.
---- • buf: Target a specific buffer number as described below.
---- • flush: Update the screen with pending updates.
---- • valid: When present mark `win`, `buf`, or all windows for
---- redraw. When `true`, only redraw changed lines (useful for
---- decoration providers). When `false`, forcefully redraw.
---- • range: Redraw a range in `buf`, the buffer in `win` or the
---- current buffer (useful for decoration providers). Expects a
---- tuple `[first, last]` with the first and last line number of
---- the range, 0-based end-exclusive `api-indexing`.
---- • cursor: Immediately update cursor position on the screen in
---- `win` or the current window.
---- • statuscolumn: Redraw the 'statuscolumn' in `buf`, `win` or
---- all windows.
---- • statusline: Redraw the 'statusline' in `buf`, `win` or all
---- windows.
---- • winbar: Redraw the 'winbar' in `buf`, `win` or all windows.
---- • tabline: Redraw the 'tabline'.
+--- - win: Target a specific `window-ID` as described below.
+--- - buf: Target a specific buffer number as described below.
+--- - flush: Update the screen with pending updates.
+--- - valid: When present mark `win`, `buf`, or all windows for
+--- redraw. When `true`, only redraw changed lines (useful for
+--- decoration providers). When `false`, forcefully redraw.
+--- - range: Redraw a range in `buf`, the buffer in `win` or the
+--- current buffer (useful for decoration providers). Expects a
+--- tuple `[first, last]` with the first and last line number
+--- of the range, 0-based end-exclusive `api-indexing`.
+--- - cursor: Immediately update cursor position on the screen in
+--- `win` or the current window.
+--- - statuscolumn: Redraw the 'statuscolumn' in `buf`, `win` or
+--- all windows.
+--- - statusline: Redraw the 'statusline' in `buf`, `win` or all
+--- windows.
+--- - winbar: Redraw the 'winbar' in `buf`, `win` or all windows.
+--- - tabline: Redraw the 'tabline'.
function vim.api.nvim__redraw(opts) end
--- @private
@@ -136,7 +155,7 @@ function vim.api.nvim__screenshot(path) end
--- @private
--- Gets internal stats.
---
---- @return table<string,any>
+--- @return table<string,any> # Map of various internal stats.
function vim.api.nvim__stats() end
--- @private
@@ -144,50 +163,20 @@ function vim.api.nvim__stats() end
--- @return any
function vim.api.nvim__unpack(str) end
---- @private
---- EXPERIMENTAL: this API will change in the future.
----
---- Scopes a namespace to the a window, so extmarks in the namespace will be
---- active only in the given window.
----
---- @param window integer Window handle, or 0 for current window
---- @param ns_id integer Namespace
---- @return boolean
-function vim.api.nvim__win_add_ns(window, ns_id) end
-
---- @private
---- EXPERIMENTAL: this API will change in the future.
----
---- Unscopes a namespace (un-binds it from the given scope).
----
---- @param window integer Window handle, or 0 for current window
---- @param ns_id integer the namespace to remove
---- @return boolean
-function vim.api.nvim__win_del_ns(window, ns_id) end
-
---- @private
---- EXPERIMENTAL: this API will change in the future.
----
---- Gets the namespace scopes for a given window.
----
---- @param window integer Window handle, or 0 for current window
---- @return integer[]
-function vim.api.nvim__win_get_ns(window) end
-
--- Adds a highlight to buffer.
---
---- Useful for plugins that dynamically generate highlights to a buffer (like
---- a semantic highlighter or linter). The function adds a single highlight to
---- a buffer. Unlike `matchaddpos()` highlights follow changes to line
---- numbering (as lines are inserted/removed above the highlighted line), like
---- signs and marks do.
+--- Useful for plugins that dynamically generate highlights to a buffer
+--- (like a semantic highlighter or linter). The function adds a single
+--- highlight to a buffer. Unlike `matchaddpos()` highlights follow changes to
+--- line numbering (as lines are inserted/removed above the highlighted line),
+--- like signs and marks do.
---
--- Namespaces are used for batch deletion/updating of a set of highlights. To
---- create a namespace, use `nvim_create_namespace()` which returns a
---- namespace id. Pass it in to this function as `ns_id` to add highlights to
---- the namespace. All highlights in the same namespace can then be cleared
---- with single call to `nvim_buf_clear_namespace()`. If the highlight never
---- will be deleted by an API call, pass `ns_id = -1`.
+--- create a namespace, use `nvim_create_namespace()` which returns a namespace
+--- id. Pass it in to this function as `ns_id` to add highlights to the
+--- namespace. All highlights in the same namespace can then be cleared with
+--- single call to `nvim_buf_clear_namespace()`. If the highlight never will be
+--- deleted by an API call, pass `ns_id = -1`.
---
--- As a shorthand, `ns_id = 0` can be used to create a new namespace for the
--- highlight, the allocated id is then returned. If `hl_group` is the empty
@@ -200,98 +189,100 @@ function vim.api.nvim__win_get_ns(window) end
--- @param hl_group string Name of the highlight group to use
--- @param line integer Line to highlight (zero-indexed)
--- @param col_start integer Start of (byte-indexed) column range to highlight
---- @param col_end integer End of (byte-indexed) column range to highlight, or -1 to
---- highlight to end of line
---- @return integer
+--- @param col_end integer End of (byte-indexed) column range to highlight,
+--- or -1 to highlight to end of line
+--- @return integer # The ns_id that was used
function vim.api.nvim_buf_add_highlight(buffer, ns_id, hl_group, line, col_start, col_end) end
--- Activates buffer-update events on a channel, or as Lua callbacks.
---
---- Example (Lua): capture buffer updates in a global `events` variable (use
---- "vim.print(events)" to see its contents):
+--- Example (Lua): capture buffer updates in a global `events` variable
+--- (use "vim.print(events)" to see its contents):
---
--- ```lua
---- events = {}
---- vim.api.nvim_buf_attach(0, false, {
---- on_lines = function(...)
---- table.insert(events, {...})
---- end,
---- })
+--- events = {}
+--- vim.api.nvim_buf_attach(0, false, {
+--- on_lines = function(...)
+--- table.insert(events, {...})
+--- end,
+--- })
--- ```
---
---
+--- @see vim.api.nvim_buf_detach
+--- @see `:help api-buffer-updates-lua`
--- @param buffer integer Buffer handle, or 0 for current buffer
--- @param send_buffer boolean True if the initial notification should contain the
---- whole buffer: first notification will be
---- `nvim_buf_lines_event`. Else the first notification
---- will be `nvim_buf_changedtick_event`. Not for Lua
---- callbacks.
+--- whole buffer: first notification will be `nvim_buf_lines_event`.
+--- Else the first notification will be `nvim_buf_changedtick_event`.
+--- Not for Lua callbacks.
--- @param opts vim.api.keyset.buf_attach Optional parameters.
---- • on_lines: Lua callback invoked on change. Return a truthy
---- value (not `false` or `nil`) to detach. Args:
---- • the string "lines"
---- • buffer handle
---- • b:changedtick
---- • first line that changed (zero-indexed)
---- • last line that was changed
---- • last line in the updated range
---- • byte count of previous contents
---- • deleted_codepoints (if `utf_sizes` is true)
---- • deleted_codeunits (if `utf_sizes` is true)
---- • on_bytes: Lua callback invoked on change. This callback
---- receives more granular information about the change compared
---- to on_lines. Return a truthy value (not `false` or `nil`) to
---- detach. Args:
---- • the string "bytes"
---- • buffer handle
---- • b:changedtick
---- • start row of the changed text (zero-indexed)
---- • start column of the changed text
---- • byte offset of the changed text (from the start of the
---- buffer)
---- • old end row of the changed text (offset from start row)
---- • old end column of the changed text (if old end row = 0,
---- offset from start column)
---- • old end byte length of the changed text
---- • new end row of the changed text (offset from start row)
---- • new end column of the changed text (if new end row = 0,
---- offset from start column)
---- • new end byte length of the changed text
---- • on_changedtick: Lua callback invoked on changedtick
---- increment without text change. Args:
---- • the string "changedtick"
---- • buffer handle
---- • b:changedtick
---- • on_detach: Lua callback invoked on detach. Args:
---- • the string "detach"
---- • buffer handle
---- • on_reload: Lua callback invoked on reload. The entire buffer
---- content should be considered changed. Args:
---- • the string "reload"
---- • buffer handle
---- • utf_sizes: include UTF-32 and UTF-16 size of the replaced
---- region, as args to `on_lines`.
---- • preview: also attach to command preview (i.e. 'inccommand')
---- events.
---- @return boolean
+--- - on_lines: Lua callback invoked on change.
+--- Return a truthy value (not `false` or `nil`) to detach. Args:
+--- - the string "lines"
+--- - buffer handle
+--- - b:changedtick
+--- - first line that changed (zero-indexed)
+--- - last line that was changed
+--- - last line in the updated range
+--- - byte count of previous contents
+--- - deleted_codepoints (if `utf_sizes` is true)
+--- - deleted_codeunits (if `utf_sizes` is true)
+--- - on_bytes: Lua callback invoked on change.
+--- This callback receives more granular information about the
+--- change compared to on_lines.
+--- Return a truthy value (not `false` or `nil`) to detach. Args:
+--- - the string "bytes"
+--- - buffer handle
+--- - b:changedtick
+--- - start row of the changed text (zero-indexed)
+--- - start column of the changed text
+--- - byte offset of the changed text (from the start of
+--- the buffer)
+--- - old end row of the changed text (offset from start row)
+--- - old end column of the changed text
+--- (if old end row = 0, offset from start column)
+--- - old end byte length of the changed text
+--- - new end row of the changed text (offset from start row)
+--- - new end column of the changed text
+--- (if new end row = 0, offset from start column)
+--- - new end byte length of the changed text
+--- - on_changedtick: Lua callback invoked on changedtick
+--- increment without text change. Args:
+--- - the string "changedtick"
+--- - buffer handle
+--- - b:changedtick
+--- - on_detach: Lua callback invoked on detach. Args:
+--- - the string "detach"
+--- - buffer handle
+--- - on_reload: Lua callback invoked on reload. The entire buffer
+--- content should be considered changed. Args:
+--- - the string "reload"
+--- - buffer handle
+--- - utf_sizes: include UTF-32 and UTF-16 size of the replaced
+--- region, as args to `on_lines`.
+--- - preview: also attach to command preview (i.e. 'inccommand')
+--- events.
+--- @return boolean # False if attach failed (invalid parameter, or buffer isn't loaded);
+--- otherwise True. TODO: LUA_API_NO_EVAL
function vim.api.nvim_buf_attach(buffer, send_buffer, opts) end
---- call a function with buffer as temporary current buffer
+--- Call a function with buffer as temporary current buffer.
---
---- This temporarily switches current buffer to "buffer". If the current
---- window already shows "buffer", the window is not switched If a window
---- inside the current tabpage (including a float) already shows the buffer
---- One of these windows will be set as current window temporarily. Otherwise
---- a temporary scratch window (called the "autocmd window" for historical
---- reasons) will be used.
+--- This temporarily switches current buffer to "buffer".
+--- If the current window already shows "buffer", the window is not switched.
+--- If a window inside the current tabpage (including a float) already shows the
+--- buffer, then one of these windows will be set as current window temporarily.
+--- Otherwise a temporary scratch window (called the "autocmd window" for
+--- historical reasons) will be used.
---
--- This is useful e.g. to call Vimscript functions that only work with the
--- current buffer/window currently, like `termopen()`.
---
--- @param buffer integer Buffer handle, or 0 for current buffer
--- @param fun function Function to call inside the buffer (currently Lua callable
---- only)
---- @return any
+--- only)
+--- @return any # Return value of function.
function vim.api.nvim_buf_call(buffer, fun) end
--- @deprecated
@@ -301,21 +292,22 @@ function vim.api.nvim_buf_call(buffer, fun) end
--- @param line_end integer
function vim.api.nvim_buf_clear_highlight(buffer, ns_id, line_start, line_end) end
---- Clears `namespace`d objects (highlights, `extmarks`, virtual text) from a
---- region.
+--- Clears `namespace`d objects (highlights, `extmarks`, virtual text) from
+--- a region.
---
---- Lines are 0-indexed. `api-indexing` To clear the namespace in the entire
+--- Lines are 0-indexed. `api-indexing` To clear the namespace in the entire
--- buffer, specify line_start=0 and line_end=-1.
---
--- @param buffer integer Buffer handle, or 0 for current buffer
--- @param ns_id integer Namespace to clear, or -1 to clear all namespaces.
--- @param line_start integer Start of range of lines to clear
--- @param line_end integer End of range of lines to clear (exclusive) or -1 to clear
---- to end of buffer.
+--- to end of buffer.
function vim.api.nvim_buf_clear_namespace(buffer, ns_id, line_start, line_end) end
--- Creates a buffer-local command `user-commands`.
---
+--- @see vim.api.nvim_create_user_command
--- @param buffer integer Buffer handle, or 0 for current buffer.
--- @param name string
--- @param command any
@@ -327,11 +319,13 @@ function vim.api.nvim_buf_create_user_command(buffer, name, command, opts) end
--- @param buffer integer Buffer handle, or 0 for current buffer
--- @param ns_id integer Namespace id from `nvim_create_namespace()`
--- @param id integer Extmark id
---- @return boolean
+--- @return boolean # true if the extmark was found, else false
function vim.api.nvim_buf_del_extmark(buffer, ns_id, id) end
--- Unmaps a buffer-local `mapping` for the given mode.
---
+---
+--- @see vim.api.nvim_del_keymap
--- @param buffer integer Buffer handle, or 0 for current buffer
--- @param mode string
--- @param lhs string
@@ -339,9 +333,15 @@ function vim.api.nvim_buf_del_keymap(buffer, mode, lhs) end
--- Deletes a named mark in the buffer. See `mark-motions`.
---
+--- Note:
+--- only deletes marks set in the buffer, if the mark is not set
+--- in the buffer it will return false.
+---
+--- @see vim.api.nvim_buf_set_mark
+--- @see vim.api.nvim_del_mark
--- @param buffer integer Buffer to set the mark on
--- @param name string Mark name
---- @return boolean
+--- @return boolean # true if the mark was deleted, else false.
function vim.api.nvim_buf_del_mark(buffer, name) end
--- Delete a buffer-local user-defined command.
@@ -363,21 +363,21 @@ function vim.api.nvim_buf_del_var(buffer, name) end
---
--- @param buffer integer Buffer handle, or 0 for current buffer
--- @param opts vim.api.keyset.buf_delete Optional parameters. Keys:
---- • force: Force deletion and ignore unsaved changes.
---- • unload: Unloaded only, do not delete. See `:bunload`
+--- - force: Force deletion and ignore unsaved changes.
+--- - unload: Unloaded only, do not delete. See `:bunload`
function vim.api.nvim_buf_delete(buffer, opts) end
--- Gets a changed tick of a buffer
---
--- @param buffer integer Buffer handle, or 0 for current buffer
---- @return integer
+--- @return integer # `b:changedtick` value.
function vim.api.nvim_buf_get_changedtick(buffer) end
--- Gets a map of buffer-local `user-commands`.
---
--- @param buffer integer Buffer handle, or 0 for current buffer
--- @param opts vim.api.keyset.get_commands Optional parameters. Currently not used.
---- @return table<string,any>
+--- @return table<string,any> # Map of maps describing commands.
function vim.api.nvim_buf_get_commands(buffer, opts) end
--- Gets the position (0-indexed) of an `extmark`.
@@ -386,10 +386,10 @@ function vim.api.nvim_buf_get_commands(buffer, opts) end
--- @param ns_id integer Namespace id from `nvim_create_namespace()`
--- @param id integer Extmark id
--- @param opts vim.api.keyset.get_extmark Optional parameters. Keys:
---- • details: Whether to include the details dict
---- • hl_name: Whether to include highlight group name instead of
---- id, true if omitted
---- @return vim.api.keyset.get_extmark_item
+--- - details: Whether to include the details dict
+--- - hl_name: Whether to include highlight group name instead of id, true if omitted
+--- @return vim.api.keyset.get_extmark_item_by_id # 0-indexed (row, col) tuple or empty list () if extmark id was
+--- absent
function vim.api.nvim_buf_get_extmark_by_id(buffer, ns_id, id, opts) end
--- Gets `extmarks` in "traversal order" from a `charwise` region defined by
@@ -400,70 +400,67 @@ function vim.api.nvim_buf_get_extmark_by_id(buffer, ns_id, id, opts) end
--- respectively, thus the following are equivalent:
---
--- ```lua
---- vim.api.nvim_buf_get_extmarks(0, my_ns, 0, -1, {})
---- vim.api.nvim_buf_get_extmarks(0, my_ns, {0,0}, {-1,-1}, {})
+--- vim.api.nvim_buf_get_extmarks(0, my_ns, 0, -1, {})
+--- vim.api.nvim_buf_get_extmarks(0, my_ns, {0,0}, {-1,-1}, {})
--- ```
---
---- If `end` is less than `start`, traversal works backwards. (Useful with
---- `limit`, to get the first marks prior to a given position.)
+--- If `end` is less than `start`, traversal works backwards. (Useful
+--- with `limit`, to get the first marks prior to a given position.)
---
--- Note: when using extmark ranges (marks with a end_row/end_col position)
---- the `overlap` option might be useful. Otherwise only the start position of
---- an extmark will be considered.
+--- the `overlap` option might be useful. Otherwise only the start position
+--- of an extmark will be considered.
---
---- Note: legacy signs placed through the `:sign` commands are implemented as
---- extmarks and will show up here. Their details array will contain a
+--- Note: legacy signs placed through the `:sign` commands are implemented
+--- as extmarks and will show up here. Their details array will contain a
--- `sign_name` field.
---
--- Example:
---
--- ```lua
---- local api = vim.api
---- local pos = api.nvim_win_get_cursor(0)
---- local ns = api.nvim_create_namespace('my-plugin')
---- -- Create new extmark at line 1, column 1.
---- local m1 = api.nvim_buf_set_extmark(0, ns, 0, 0, {})
---- -- Create new extmark at line 3, column 1.
---- local m2 = api.nvim_buf_set_extmark(0, ns, 2, 0, {})
---- -- Get extmarks only from line 3.
---- local ms = api.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {})
---- -- Get all marks in this buffer + namespace.
---- local all = api.nvim_buf_get_extmarks(0, ns, 0, -1, {})
---- vim.print(ms)
+--- local api = vim.api
+--- local pos = api.nvim_win_get_cursor(0)
+--- local ns = api.nvim_create_namespace('my-plugin')
+--- -- Create new extmark at line 1, column 1.
+--- local m1 = api.nvim_buf_set_extmark(0, ns, 0, 0, {})
+--- -- Create new extmark at line 3, column 1.
+--- local m2 = api.nvim_buf_set_extmark(0, ns, 2, 0, {})
+--- -- Get extmarks only from line 3.
+--- local ms = api.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {})
+--- -- Get all marks in this buffer + namespace.
+--- local all = api.nvim_buf_get_extmarks(0, ns, 0, -1, {})
+--- vim.print(ms)
--- ```
---
----
--- @param buffer integer Buffer handle, or 0 for current buffer
---- @param ns_id integer Namespace id from `nvim_create_namespace()` or -1 for all
---- namespaces
+--- @param ns_id integer Namespace id from `nvim_create_namespace()` or -1 for all namespaces
--- @param start any Start of range: a 0-indexed (row, col) or valid extmark id
---- (whose position defines the bound). `api-indexing`
+--- (whose position defines the bound). `api-indexing`
--- @param end_ any End of range (inclusive): a 0-indexed (row, col) or valid
---- extmark id (whose position defines the bound). `api-indexing`
+--- extmark id (whose position defines the bound). `api-indexing`
--- @param opts vim.api.keyset.get_extmarks Optional parameters. Keys:
---- • limit: Maximum number of marks to return
---- • details: Whether to include the details dict
---- • hl_name: Whether to include highlight group name instead of
---- id, true if omitted
---- • overlap: Also include marks which overlap the range, even if
---- their start position is less than `start`
---- • type: Filter marks by type: "highlight", "sign", "virt_text"
---- and "virt_lines"
---- @return vim.api.keyset.get_extmark_item[]
+--- - limit: Maximum number of marks to return
+--- - details: Whether to include the details dict
+--- - hl_name: Whether to include highlight group name instead of id, true if omitted
+--- - overlap: Also include marks which overlap the range, even if
+--- their start position is less than `start`
+--- - type: Filter marks by type: "highlight", "sign", "virt_text" and "virt_lines"
+--- @return vim.api.keyset.get_extmark_item[] # List of `[extmark_id, row, col]` tuples in "traversal order".
function vim.api.nvim_buf_get_extmarks(buffer, ns_id, start, end_, opts) end
--- Gets a list of buffer-local `mapping` definitions.
---
--- @param buffer integer Buffer handle, or 0 for current buffer
--- @param mode string Mode short-name ("n", "i", "v", ...)
---- @return vim.api.keyset.keymap[]
+--- @return vim.api.keyset.keymap[] # Array of |maparg()|-like dictionaries describing mappings.
+--- The "buffer" key holds the associated buffer handle.
function vim.api.nvim_buf_get_keymap(buffer, mode) end
--- Gets a line-range from the buffer.
---
---- Indexing is zero-based, end-exclusive. Negative indices are interpreted as
---- length+1+index: -1 refers to the index past the end. So to get the last
---- element use start=-2 and end=-1.
+--- Indexing is zero-based, end-exclusive. Negative indices are interpreted
+--- as length+1+index: -1 refers to the index past the end. So to get the
+--- last element use start=-2 and end=-1.
---
--- Out-of-bounds indices are clamped to the nearest valid value, unless
--- `strict_indexing` is set.
@@ -472,24 +469,27 @@ function vim.api.nvim_buf_get_keymap(buffer, mode) end
--- @param start integer First line index
--- @param end_ integer Last line index, exclusive
--- @param strict_indexing boolean Whether out-of-bounds should be an error.
---- @return string[]
+--- @return string[] # Array of lines, or empty array for unloaded buffer.
function vim.api.nvim_buf_get_lines(buffer, start, end_, strict_indexing) end
--- Returns a `(row,col)` tuple representing the position of the named mark.
---- "End of line" column position is returned as `v:maxcol` (big number). See
---- `mark-motions`.
+--- "End of line" column position is returned as `v:maxcol` (big number).
+--- See `mark-motions`.
---
--- Marks are (1,0)-indexed. `api-indexing`
---
+--- @see vim.api.nvim_buf_set_mark
+--- @see vim.api.nvim_buf_del_mark
--- @param buffer integer Buffer handle, or 0 for current buffer
--- @param name string Mark name
---- @return integer[]
+--- @return integer[] # (row, col) tuple, (0, 0) if the mark is not set, or is an
+--- uppercase/file mark set in another buffer.
function vim.api.nvim_buf_get_mark(buffer, name) end
--- Gets the full file name for the buffer
---
--- @param buffer integer Buffer handle, or 0 for current buffer
---- @return string
+--- @return string # Buffer name
function vim.api.nvim_buf_get_name(buffer) end
--- @deprecated
@@ -504,12 +504,12 @@ function vim.api.nvim_buf_get_number(buffer) end
--- last line gives the total byte-count of the buffer. A final EOL byte is
--- counted if it would be written, see 'eol'.
---
---- Unlike `line2byte()`, throws error for out-of-bounds indexing. Returns -1
---- for unloaded buffer.
+--- Unlike `line2byte()`, throws error for out-of-bounds indexing.
+--- Returns -1 for unloaded buffer.
---
--- @param buffer integer Buffer handle, or 0 for current buffer
--- @param index integer Line index
---- @return integer
+--- @return integer # Integer byte offset, or -1 for unloaded buffer.
function vim.api.nvim_buf_get_offset(buffer, index) end
--- @deprecated
@@ -534,49 +534,54 @@ function vim.api.nvim_buf_get_option(buffer, name) end
--- @param end_row integer Last line index, inclusive
--- @param end_col integer Ending column (byte offset) on last line, exclusive
--- @param opts vim.api.keyset.empty Optional parameters. Currently unused.
---- @return string[]
+--- @return string[] # Array of lines, or empty array for unloaded buffer.
function vim.api.nvim_buf_get_text(buffer, start_row, start_col, end_row, end_col, opts) end
--- Gets a buffer-scoped (b:) variable.
---
--- @param buffer integer Buffer handle, or 0 for current buffer
--- @param name string Variable name
---- @return any
+--- @return any # Variable value
function vim.api.nvim_buf_get_var(buffer, name) end
--- Checks if a buffer is valid and loaded. See `api-buffer` for more info
--- about unloaded buffers.
---
--- @param buffer integer Buffer handle, or 0 for current buffer
---- @return boolean
+--- @return boolean # true if the buffer is valid and loaded, false otherwise.
function vim.api.nvim_buf_is_loaded(buffer) end
--- Checks if a buffer is valid.
---
+--- Note:
+--- Even if a buffer is valid it may have been unloaded. See |api-buffer|
+--- for more info about unloaded buffers.
+---
+---
--- @param buffer integer Buffer handle, or 0 for current buffer
---- @return boolean
+--- @return boolean # true if the buffer is valid, false otherwise.
function vim.api.nvim_buf_is_valid(buffer) end
--- Returns the number of lines in the given buffer.
---
--- @param buffer integer Buffer handle, or 0 for current buffer
---- @return integer
+--- @return integer # Line count, or 0 for unloaded buffer. |api-buffer|
function vim.api.nvim_buf_line_count(buffer) end
--- Creates or updates an `extmark`.
---
---- By default a new extmark is created when no id is passed in, but it is
---- also possible to create a new mark by passing in a previously unused id or
---- move an existing mark by passing in its id. The caller must then keep
---- track of existing and unused ids itself. (Useful over RPC, to avoid
---- waiting for the return value.)
+--- By default a new extmark is created when no id is passed in, but it is also
+--- possible to create a new mark by passing in a previously unused id or move
+--- an existing mark by passing in its id. The caller must then keep track of
+--- existing and unused ids itself. (Useful over RPC, to avoid waiting for the
+--- return value.)
---
---- Using the optional arguments, it is possible to use this to highlight a
---- range of text, and also to associate virtual text to the mark.
+--- Using the optional arguments, it is possible to use this to highlight
+--- a range of text, and also to associate virtual text to the mark.
---
---- If present, the position defined by `end_col` and `end_row` should be
---- after the start position in order for the extmark to cover a range. An
---- earlier end position is not an error, but then it behaves like an empty
+--- If present, the position defined by `end_col` and `end_row` should be after
+--- the start position in order for the extmark to cover a range.
+--- An earlier end position is not an error, but then it behaves like an empty
--- range (no highlighting).
---
--- @param buffer integer Buffer handle, or 0 for current buffer
@@ -584,115 +589,122 @@ function vim.api.nvim_buf_line_count(buffer) end
--- @param line integer Line where to place the mark, 0-based. `api-indexing`
--- @param col integer Column where to place the mark, 0-based. `api-indexing`
--- @param opts vim.api.keyset.set_extmark Optional parameters.
---- • id : id of the extmark to edit.
---- • end_row : ending line of the mark, 0-based inclusive.
---- • end_col : ending col of the mark, 0-based exclusive.
---- • hl_group : name of the highlight group used to highlight
---- this mark.
---- • hl_eol : when true, for a multiline highlight covering the
---- EOL of a line, continue the highlight for the rest of the
---- screen line (just like for diff and cursorline highlight).
---- • virt_text : virtual text to link to this mark. A list of
---- `[text, highlight]` tuples, each representing a text chunk
---- with specified highlight. `highlight` element can either be
---- a single highlight group, or an array of multiple highlight
---- groups that will be stacked (highest priority last). A
---- highlight group can be supplied either as a string or as an
---- integer, the latter which can be obtained using
---- `nvim_get_hl_id_by_name()`.
---- • virt_text_pos : position of virtual text. Possible values:
---- • "eol": right after eol character (default).
---- • "overlay": display over the specified column, without
---- shifting the underlying text.
---- • "right_align": display right aligned in the window.
---- • "inline": display at the specified column, and shift the
---- buffer text to the right as needed.
---- • virt_text_win_col : position the virtual text at a fixed
---- window column (starting from the first text column of the
---- screen line) instead of "virt_text_pos".
---- • virt_text_hide : hide the virtual text when the background
---- text is selected or hidden because of scrolling with
---- 'nowrap' or 'smoothscroll'. Currently only affects "overlay"
---- virt_text.
---- • virt_text_repeat_linebreak : repeat the virtual text on
---- wrapped lines.
---- • hl_mode : control how highlights are combined with the
---- highlights of the text. Currently only affects virt_text
---- highlights, but might affect `hl_group` in later versions.
---- • "replace": only show the virt_text color. This is the
---- default.
---- • "combine": combine with background text color.
---- • "blend": blend with background text color. Not supported
---- for "inline" virt_text.
---- • virt_lines : virtual lines to add next to this mark This
---- should be an array over lines, where each line in turn is an
---- array over `[text, highlight]` tuples. In general, buffer
---- and window options do not affect the display of the text. In
---- particular 'wrap' and 'linebreak' options do not take
---- effect, so the number of extra screen lines will always
---- match the size of the array. However the 'tabstop' buffer
---- option is still used for hard tabs. By default lines are
---- placed below the buffer line containing the mark.
---- • virt_lines_above: place virtual lines above instead.
---- • virt_lines_leftcol: Place extmarks in the leftmost column of
---- the window, bypassing sign and number columns.
---- • ephemeral : for use with `nvim_set_decoration_provider()`
---- callbacks. The mark will only be used for the current redraw
---- cycle, and not be permantently stored in the buffer.
---- • right_gravity : boolean that indicates the direction the
---- extmark will be shifted in when new text is inserted (true
---- for right, false for left). Defaults to true.
---- • end_right_gravity : boolean that indicates the direction the
---- extmark end position (if it exists) will be shifted in when
---- new text is inserted (true for right, false for left).
---- Defaults to false.
---- • undo_restore : Restore the exact position of the mark if
---- text around the mark was deleted and then restored by undo.
---- Defaults to true.
---- • invalidate : boolean that indicates whether to hide the
---- extmark if the entirety of its range is deleted. For hidden
---- marks, an "invalid" key is added to the "details" array of
---- `nvim_buf_get_extmarks()` and family. If "undo_restore" is
---- false, the extmark is deleted instead.
---- • priority: a priority value for the highlight group, sign
---- attribute or virtual text. For virtual text, item with
---- highest priority is drawn last. For example treesitter
---- highlighting uses a value of 100.
---- • strict: boolean that indicates extmark should not be placed
---- if the line or column value is past the end of the buffer or
---- end of the line respectively. Defaults to true.
---- • sign_text: string of length 1-2 used to display in the sign
---- column.
---- • sign_hl_group: name of the highlight group used to highlight
---- the sign column text.
---- • number_hl_group: name of the highlight group used to
---- highlight the number column.
---- • line_hl_group: name of the highlight group used to highlight
---- the whole line.
---- • cursorline_hl_group: name of the highlight group used to
---- highlight the sign column text when the cursor is on the
---- same line as the mark and 'cursorline' is enabled.
---- • conceal: string which should be either empty or a single
---- character. Enable concealing similar to `:syn-conceal`. When
---- a character is supplied it is used as `:syn-cchar`.
---- "hl_group" is used as highlight for the cchar if provided,
---- otherwise it defaults to `hl-Conceal`.
---- • spell: boolean indicating that spell checking should be
---- performed within this extmark
---- • ui_watched: boolean that indicates the mark should be drawn
---- by a UI. When set, the UI will receive win_extmark events.
---- Note: the mark is positioned by virt_text attributes. Can be
---- used together with virt_text.
---- • url: A URL to associate with this extmark. In the TUI, the
---- OSC 8 control sequence is used to generate a clickable
---- hyperlink to this URL.
---- • scoped: boolean (EXPERIMENTAL) enables "scoping" for the
---- extmark. See `nvim__win_add_ns()`
---- @return integer
+--- - id : id of the extmark to edit.
+--- - end_row : ending line of the mark, 0-based inclusive.
+--- - end_col : ending col of the mark, 0-based exclusive.
+--- - hl_group : name of the highlight group used to highlight
+--- this mark.
+--- - hl_eol : when true, for a multiline highlight covering the
+--- EOL of a line, continue the highlight for the rest
+--- of the screen line (just like for diff and
+--- cursorline highlight).
+--- - virt_text : virtual text to link to this mark.
+--- A list of `[text, highlight]` tuples, each representing a
+--- text chunk with specified highlight. `highlight` element
+--- can either be a single highlight group, or an array of
+--- multiple highlight groups that will be stacked
+--- (highest priority last). A highlight group can be supplied
+--- either as a string or as an integer, the latter which
+--- can be obtained using `nvim_get_hl_id_by_name()`.
+--- - virt_text_pos : position of virtual text. Possible values:
+--- - "eol": right after eol character (default).
+--- - "overlay": display over the specified column, without
+--- shifting the underlying text.
+--- - "right_align": display right aligned in the window.
+--- - "inline": display at the specified column, and
+--- shift the buffer text to the right as needed.
+--- - virt_text_win_col : position the virtual text at a fixed
+--- window column (starting from the first
+--- text column of the screen line) instead
+--- of "virt_text_pos".
+--- - virt_text_hide : hide the virtual text when the background
+--- text is selected or hidden because of
+--- scrolling with 'nowrap' or 'smoothscroll'.
+--- Currently only affects "overlay" virt_text.
+--- - virt_text_repeat_linebreak : repeat the virtual text on
+--- wrapped lines.
+--- - hl_mode : control how highlights are combined with the
+--- highlights of the text. Currently only affects
+--- virt_text highlights, but might affect `hl_group`
+--- in later versions.
+--- - "replace": only show the virt_text color. This is the default.
+--- - "combine": combine with background text color.
+--- - "blend": blend with background text color.
+--- Not supported for "inline" virt_text.
+---
+--- - virt_lines : virtual lines to add next to this mark
+--- This should be an array over lines, where each line in
+--- turn is an array over `[text, highlight]` tuples. In
+--- general, buffer and window options do not affect the
+--- display of the text. In particular 'wrap'
+--- and 'linebreak' options do not take effect, so
+--- the number of extra screen lines will always match
+--- the size of the array. However the 'tabstop' buffer
+--- option is still used for hard tabs. By default lines are
+--- placed below the buffer line containing the mark.
+---
+--- - virt_lines_above: place virtual lines above instead.
+--- - virt_lines_leftcol: Place extmarks in the leftmost
+--- column of the window, bypassing
+--- sign and number columns.
+---
+--- - ephemeral : for use with `nvim_set_decoration_provider()`
+--- callbacks. The mark will only be used for the current
+--- redraw cycle, and not be permantently stored in the
+--- buffer.
+--- - right_gravity : boolean that indicates the direction
+--- the extmark will be shifted in when new text is inserted
+--- (true for right, false for left). Defaults to true.
+--- - end_right_gravity : boolean that indicates the direction
+--- the extmark end position (if it exists) will be shifted
+--- in when new text is inserted (true for right, false
+--- for left). Defaults to false.
+--- - undo_restore : Restore the exact position of the mark
+--- if text around the mark was deleted and then restored by undo.
+--- Defaults to true.
+--- - invalidate : boolean that indicates whether to hide the
+--- extmark if the entirety of its range is deleted. For
+--- hidden marks, an "invalid" key is added to the "details"
+--- array of `nvim_buf_get_extmarks()` and family. If
+--- "undo_restore" is false, the extmark is deleted instead.
+--- - priority: a priority value for the highlight group, sign
+--- attribute or virtual text. For virtual text, item with
+--- highest priority is drawn last. For example treesitter
+--- highlighting uses a value of 100.
+--- - strict: boolean that indicates extmark should not be placed
+--- if the line or column value is past the end of the
+--- buffer or end of the line respectively. Defaults to true.
+--- - sign_text: string of length 1-2 used to display in the
+--- sign column.
+--- - sign_hl_group: name of the highlight group used to
+--- highlight the sign column text.
+--- - number_hl_group: name of the highlight group used to
+--- highlight the number column.
+--- - line_hl_group: name of the highlight group used to
+--- highlight the whole line.
+--- - cursorline_hl_group: name of the highlight group used to
+--- highlight the sign column text when the cursor is on
+--- the same line as the mark and 'cursorline' is enabled.
+--- - conceal: string which should be either empty or a single
+--- character. Enable concealing similar to `:syn-conceal`.
+--- When a character is supplied it is used as `:syn-cchar`.
+--- "hl_group" is used as highlight for the cchar if provided,
+--- otherwise it defaults to `hl-Conceal`.
+--- - spell: boolean indicating that spell checking should be
+--- performed within this extmark
+--- - ui_watched: boolean that indicates the mark should be drawn
+--- by a UI. When set, the UI will receive win_extmark events.
+--- Note: the mark is positioned by virt_text attributes. Can be
+--- used together with virt_text.
+--- - url: A URL to associate with this extmark. In the TUI, the OSC 8 control
+--- sequence is used to generate a clickable hyperlink to this URL.
+--- @return integer # Id of the created/updated extmark
function vim.api.nvim_buf_set_extmark(buffer, ns_id, line, col, opts) end
--- Sets a buffer-local `mapping` for the given mode.
---
+---
+--- @see vim.api.nvim_set_keymap
--- @param buffer integer Buffer handle, or 0 for current buffer
--- @param mode string
--- @param lhs string
@@ -702,9 +714,9 @@ function vim.api.nvim_buf_set_keymap(buffer, mode, lhs, rhs, opts) end
--- Sets (replaces) a line-range in the buffer.
---
---- Indexing is zero-based, end-exclusive. Negative indices are interpreted as
---- length+1+index: -1 refers to the index past the end. So to change or
---- delete the last element use start=-2 and end=-1.
+--- Indexing is zero-based, end-exclusive. Negative indices are interpreted
+--- as length+1+index: -1 refers to the index past the end. So to change
+--- or delete the last element use start=-2 and end=-1.
---
--- To insert lines at a given index, set `start` and `end` to the same index.
--- To delete a range of lines, set `replacement` to an empty array.
@@ -712,6 +724,8 @@ function vim.api.nvim_buf_set_keymap(buffer, mode, lhs, rhs, opts) end
--- Out-of-bounds indices are clamped to the nearest valid value, unless
--- `strict_indexing` is set.
---
+---
+--- @see vim.api.nvim_buf_set_text
--- @param buffer integer Buffer handle, or 0 for current buffer
--- @param start integer First line index
--- @param end_ integer Last line index, exclusive
@@ -724,12 +738,18 @@ function vim.api.nvim_buf_set_lines(buffer, start, end_, strict_indexing, replac
---
--- Marks are (1,0)-indexed. `api-indexing`
---
+--- Note:
+--- Passing 0 as line deletes the mark
+---
+---
+--- @see vim.api.nvim_buf_del_mark
+--- @see vim.api.nvim_buf_get_mark
--- @param buffer integer Buffer to set the mark on
--- @param name string Mark name
--- @param line integer Line number
--- @param col integer Column/row number
--- @param opts vim.api.keyset.empty Optional parameters. Reserved for future use.
---- @return boolean
+--- @return boolean # true if the mark was set, else false.
function vim.api.nvim_buf_set_mark(buffer, name, line, col, opts) end
--- Sets the full file name for a buffer, like `:file_f`
@@ -746,21 +766,21 @@ function vim.api.nvim_buf_set_option(buffer, name, value) end
--- Sets (replaces) a range in the buffer
---
---- This is recommended over `nvim_buf_set_lines()` when only modifying parts
---- of a line, as extmarks will be preserved on non-modified parts of the
---- touched lines.
+--- This is recommended over `nvim_buf_set_lines()` when only modifying parts of
+--- a line, as extmarks will be preserved on non-modified parts of the touched
+--- lines.
---
--- Indexing is zero-based. Row indices are end-inclusive, and column indices
--- are end-exclusive.
---
---- To insert text at a given `(row, column)` location, use
---- `start_row = end_row = row` and `start_col = end_col = col`. To delete the
---- text in a range, use `replacement = {}`.
+--- To insert text at a given `(row, column)` location, use `start_row = end_row
+--- = row` and `start_col = end_col = col`. To delete the text in a range, use
+--- `replacement = {}`.
---
---- Prefer `nvim_buf_set_lines()` if you are only adding or deleting entire
---- lines.
+--- Note:
+--- Prefer |nvim_buf_set_lines()| (for performance) to add or delete entire lines.
+--- Prefer |nvim_paste()| or |nvim_put()| to insert (instead of replace) text at cursor.
---
---- Prefer `nvim_put()` if you want to insert text at the cursor position.
---
--- @param buffer integer Buffer handle, or 0 for current buffer
--- @param start_row integer First line index
@@ -790,10 +810,10 @@ function vim.api.nvim_buf_set_virtual_text(buffer, src_id, line, chunks, opts) e
---
--- On execution error: fails with Vimscript error, updates v:errmsg.
---
---- @param dict any Dictionary, or String evaluating to a Vimscript `self` dict
+--- @param dict any Dict, or String evaluating to a Vimscript `self` dict
--- @param fn string Name of the function defined on the Vimscript dict
--- @param args any[] Function arguments packed in an Array
---- @return any
+--- @return any # Result of the function call
function vim.api.nvim_call_dict_function(dict, fn, args) end
--- Calls a Vimscript function with the given arguments.
@@ -802,85 +822,83 @@ function vim.api.nvim_call_dict_function(dict, fn, args) end
---
--- @param fn string Function to call
--- @param args any[] Function arguments packed in an Array
---- @return any
+--- @return any # Result of the function call
function vim.api.nvim_call_function(fn, args) end
---- Send data to channel `id`. For a job, it writes it to the stdin of the
---- process. For the stdio channel `channel-stdio`, it writes to Nvim's
---- stdout. For an internal terminal instance (`nvim_open_term()`) it writes
---- directly to terminal output. See `channel-bytes` for more information.
+--- Send data to channel `id`. For a job, it writes it to the
+--- stdin of the process. For the stdio channel `channel-stdio`,
+--- it writes to Nvim's stdout. For an internal terminal instance
+--- (`nvim_open_term()`) it writes directly to terminal output.
+--- See `channel-bytes` for more information.
---
---- This function writes raw data, not RPC messages. If the channel was
---- created with `rpc=true` then the channel expects RPC messages, use
---- `vim.rpcnotify()` and `vim.rpcrequest()` instead.
+--- This function writes raw data, not RPC messages. If the channel
+--- was created with `rpc=true` then the channel expects RPC
+--- messages, use `vim.rpcnotify()` and `vim.rpcrequest()` instead.
---
--- @param chan integer id of the channel
--- @param data string data to write. 8-bit clean: can contain NUL bytes.
function vim.api.nvim_chan_send(chan, data) end
---- Clears all autocommands selected by {opts}. To delete autocmds see
---- `nvim_del_autocmd()`.
+--- Clears all autocommands selected by {opts}. To delete autocmds see `nvim_del_autocmd()`.
---
--- @param opts vim.api.keyset.clear_autocmds Parameters
---- • event: (string|table) Examples:
---- • event: "pat1"
---- • event: { "pat1" }
---- • event: { "pat1", "pat2", "pat3" }
---- • pattern: (string|table)
---- • pattern or patterns to match exactly.
---- • For example, if you have `*.py` as that pattern for the
---- autocmd, you must pass `*.py` exactly to clear it.
---- `test.py` will not match the pattern.
---- • defaults to clearing all patterns.
---- • NOTE: Cannot be used with {buffer}
---- • buffer: (bufnr)
---- • clear only `autocmd-buflocal` autocommands.
---- • NOTE: Cannot be used with {pattern}
---- • group: (string|int) The augroup name or id.
---- • NOTE: If not passed, will only delete autocmds not in any
---- group.
+--- - event: (string|table)
+--- Examples:
+--- - event: "pat1"
+--- - event: { "pat1" }
+--- - event: { "pat1", "pat2", "pat3" }
+--- - pattern: (string|table)
+--- - pattern or patterns to match exactly.
+--- - For example, if you have `*.py` as that pattern for the autocmd,
+--- you must pass `*.py` exactly to clear it. `test.py` will not
+--- match the pattern.
+--- - defaults to clearing all patterns.
+--- - NOTE: Cannot be used with {buffer}
+--- - buffer: (bufnr)
+--- - clear only `autocmd-buflocal` autocommands.
+--- - NOTE: Cannot be used with {pattern}
+--- - group: (string|int) The augroup name or id.
+--- - NOTE: If not passed, will only delete autocmds *not* in any group.
function vim.api.nvim_clear_autocmds(opts) end
--- Executes an Ex command.
---
---- Unlike `nvim_command()` this command takes a structured Dictionary instead
---- of a String. This allows for easier construction and manipulation of an Ex
---- command. This also allows for things such as having spaces inside a
---- command argument, expanding filenames in a command that otherwise doesn't
---- expand filenames, etc. Command arguments may also be Number, Boolean or
---- String.
+--- Unlike `nvim_command()` this command takes a structured Dict instead of a String. This
+--- allows for easier construction and manipulation of an Ex command. This also allows for things
+--- such as having spaces inside a command argument, expanding filenames in a command that otherwise
+--- doesn't expand filenames, etc. Command arguments may also be Number, Boolean or String.
---
---- The first argument may also be used instead of count for commands that
---- support it in order to make their usage simpler with `vim.cmd()`. For
---- example, instead of `vim.cmd.bdelete{ count = 2 }`, you may do
---- `vim.cmd.bdelete(2)`.
+--- The first argument may also be used instead of count for commands that support it in order to
+--- make their usage simpler with `vim.cmd()`. For example, instead of
+--- `vim.cmd.bdelete{ count = 2 }`, you may do `vim.cmd.bdelete(2)`.
---
--- On execution error: fails with Vimscript error, updates v:errmsg.
---
---- @param cmd vim.api.keyset.cmd Command to execute. Must be a Dictionary that can contain the
---- same values as the return value of `nvim_parse_cmd()` except
---- "addr", "nargs" and "nextcmd" which are ignored if provided.
---- All values except for "cmd" are optional.
+---
+--- @see vim.api.nvim_exec2
+--- @see vim.api.nvim_command
+--- @param cmd vim.api.keyset.cmd Command to execute. Must be a Dict that can contain the same values as
+--- the return value of `nvim_parse_cmd()` except "addr", "nargs" and "nextcmd"
+--- which are ignored if provided. All values except for "cmd" are optional.
--- @param opts vim.api.keyset.cmd_opts Optional parameters.
---- • output: (boolean, default false) Whether to return command
---- output.
---- @return string
+--- - output: (boolean, default false) Whether to return command output.
+--- @return string # Command output (non-error, non-shell |:!|) if `output` is true, else empty string.
function vim.api.nvim_cmd(cmd, opts) end
--- Executes an Ex command.
---
--- On execution error: fails with Vimscript error, updates v:errmsg.
---
---- Prefer using `nvim_cmd()` or `nvim_exec2()` over this. To evaluate
---- multiple lines of Vim script or an Ex command directly, use
---- `nvim_exec2()`. To construct an Ex command using a structured format and
---- then execute it, use `nvim_cmd()`. To modify an Ex command before
---- evaluating it, use `nvim_parse_cmd()` in conjunction with `nvim_cmd()`.
+--- Prefer using `nvim_cmd()` or `nvim_exec2()` over this. To evaluate multiple lines of Vim script
+--- or an Ex command directly, use `nvim_exec2()`. To construct an Ex command using a structured
+--- format and then execute it, use `nvim_cmd()`. To modify an Ex command before evaluating it, use
+--- `nvim_parse_cmd()` in conjunction with `nvim_cmd()`.
---
--- @param command string Ex command string
function vim.api.nvim_command(command) end
--- @deprecated
+--- @see vim.api.nvim_exec2
--- @param command string
--- @return string
function vim.api.nvim_command_output(command) end
@@ -890,106 +908,99 @@ function vim.api.nvim_command_output(command) end
--- To get an existing group id, do:
---
--- ```lua
---- local id = vim.api.nvim_create_augroup("MyGroup", {
---- clear = false
---- })
+--- local id = vim.api.nvim_create_augroup("MyGroup", {
+--- clear = false
+--- })
--- ```
---
----
+--- @see `:help autocmd-groups`
--- @param name string String: The name of the group
---- @param opts vim.api.keyset.create_augroup Dictionary Parameters
---- • clear (bool) optional: defaults to true. Clear existing
---- commands if the group already exists `autocmd-groups`.
---- @return integer
+--- @param opts vim.api.keyset.create_augroup Dict Parameters
+--- - clear (bool) optional: defaults to true. Clear existing
+--- commands if the group already exists `autocmd-groups`.
+--- @return integer # Integer id of the created group.
function vim.api.nvim_create_augroup(name, opts) end
---- Creates an `autocommand` event handler, defined by `callback` (Lua
---- function or Vimscript function name string) or `command` (Ex command
---- string).
+--- Creates an `autocommand` event handler, defined by `callback` (Lua function or Vimscript
+--- function _name_ string) or `command` (Ex command string).
---
--- Example using Lua callback:
---
--- ```lua
---- vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, {
---- pattern = {"*.c", "*.h"},
---- callback = function(ev)
---- print(string.format('event fired: %s', vim.inspect(ev)))
---- end
---- })
+--- vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, {
+--- pattern = {"*.c", "*.h"},
+--- callback = function(ev)
+--- print(string.format('event fired: %s', vim.inspect(ev)))
+--- end
+--- })
--- ```
---
--- Example using an Ex command as the handler:
---
--- ```lua
---- vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, {
---- pattern = {"*.c", "*.h"},
---- command = "echo 'Entering a C or C++ file'",
---- })
+--- vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, {
+--- pattern = {"*.c", "*.h"},
+--- command = "echo 'Entering a C or C++ file'",
+--- })
--- ```
---
---- Note: `pattern` is NOT automatically expanded (unlike with `:autocmd`),
---- thus names like "$HOME" and "~" must be expanded explicitly:
+--- Note: `pattern` is NOT automatically expanded (unlike with `:autocmd`), thus names like "$HOME"
+--- and "~" must be expanded explicitly:
---
--- ```lua
---- pattern = vim.fn.expand("~") .. "/some/path/*.py"
+--- pattern = vim.fn.expand("~") .. "/some/path/*.py"
--- ```
---
----
---- @param event any (string|array) Event(s) that will trigger the handler
---- (`callback` or `command`).
+--- @see `:help autocommand`
+--- @see vim.api.nvim_del_autocmd
+--- @param event any (string|array) Event(s) that will trigger the handler (`callback` or `command`).
--- @param opts vim.api.keyset.create_autocmd Options dict:
---- • group (string|integer) optional: autocommand group name or
---- id to match against.
---- • pattern (string|array) optional: pattern(s) to match
---- literally `autocmd-pattern`.
---- • buffer (integer) optional: buffer number for buffer-local
---- autocommands `autocmd-buflocal`. Cannot be used with
---- {pattern}.
---- • desc (string) optional: description (for documentation and
---- troubleshooting).
---- • callback (function|string) optional: Lua function (or
---- Vimscript function name, if string) called when the event(s)
---- is triggered. Lua callback can return a truthy value (not
---- `false` or `nil`) to delete the autocommand. Receives one
---- argument, a table with these keys: *event-args*
---- • id: (number) autocommand id
---- • event: (string) name of the triggered event
---- `autocmd-events`
---- • group: (number|nil) autocommand group id, if any
---- • match: (string) expanded value of <amatch>
---- • buf: (number) expanded value of <abuf>
---- • file: (string) expanded value of <afile>
---- • data: (any) arbitrary data passed from
---- `nvim_exec_autocmds()` *event-data*
---- • command (string) optional: Vim command to execute on event.
---- Cannot be used with {callback}
---- • once (boolean) optional: defaults to false. Run the
---- autocommand only once `autocmd-once`.
---- • nested (boolean) optional: defaults to false. Run nested
---- autocommands `autocmd-nested`.
---- @return integer
+--- - group (string|integer) optional: autocommand group name or id to match against.
+--- - pattern (string|array) optional: pattern(s) to match literally `autocmd-pattern`.
+--- - buffer (integer) optional: buffer number for buffer-local autocommands
+--- `autocmd-buflocal`. Cannot be used with {pattern}.
+--- - desc (string) optional: description (for documentation and troubleshooting).
+--- - callback (function|string) optional: Lua function (or Vimscript function name, if
+--- string) called when the event(s) is triggered. Lua callback can return a truthy
+--- value (not `false` or `nil`) to delete the autocommand. Receives one argument,
+--- a table with these keys: [event-args]()
+--- - id: (number) autocommand id
+--- - event: (string) name of the triggered event `autocmd-events`
+--- - group: (number|nil) autocommand group id, if any
+--- - match: (string) expanded value of [<amatch>]
+--- - buf: (number) expanded value of [<abuf>]
+--- - file: (string) expanded value of [<afile>]
+--- - data: (any) arbitrary data passed from [nvim_exec_autocmds()] [event-data]()
+--- - command (string) optional: Vim command to execute on event. Cannot be used with
+--- {callback}
+--- - once (boolean) optional: defaults to false. Run the autocommand
+--- only once `autocmd-once`.
+--- - nested (boolean) optional: defaults to false. Run nested
+--- autocommands `autocmd-nested`.
+--- @return integer # Autocommand id (number)
function vim.api.nvim_create_autocmd(event, opts) end
--- Creates a new, empty, unnamed buffer.
---
+--- @see buf_open_scratch
--- @param listed boolean Sets 'buflisted'
--- @param scratch boolean Creates a "throwaway" `scratch-buffer` for temporary work
---- (always 'nomodified'). Also sets 'nomodeline' on the
---- buffer.
---- @return integer
+--- (always 'nomodified'). Also sets 'nomodeline' on the buffer.
+--- @return integer # Buffer handle, or 0 on error
+---
function vim.api.nvim_create_buf(listed, scratch) end
---- Creates a new namespace or gets an existing one. *namespace*
+--- Creates a new namespace or gets an existing one. [namespace]()
---
--- Namespaces are used for buffer highlights and virtual text, see
--- `nvim_buf_add_highlight()` and `nvim_buf_set_extmark()`.
---
--- Namespaces can be named or anonymous. If `name` matches an existing
---- namespace, the associated id is returned. If `name` is an empty string a
---- new, anonymous namespace is created.
+--- namespace, the associated id is returned. If `name` is an empty string
+--- a new, anonymous namespace is created.
---
--- @param name string Namespace name or empty string
---- @return integer
+--- @return integer # Namespace id
function vim.api.nvim_create_namespace(name) end
--- Creates a global `user-commands` command.
@@ -999,70 +1010,57 @@ function vim.api.nvim_create_namespace(name) end
--- Example:
---
--- ```vim
---- :call nvim_create_user_command('SayHello', 'echo "Hello world!"', {'bang': v:true})
---- :SayHello
---- Hello world!
+--- :call nvim_create_user_command('SayHello', 'echo "Hello world!"', {'bang': v:true})
+--- :SayHello
+--- Hello world!
--- ```
---
----
---- @param name string Name of the new user command. Must begin with an uppercase
---- letter.
---- @param command any Replacement command to execute when this user command is
---- executed. When called from Lua, the command can also be a
---- Lua function. The function is called with a single table
---- argument that contains the following keys:
---- • name: (string) Command name
---- • args: (string) The args passed to the command, if any
---- <args>
---- • fargs: (table) The args split by unescaped whitespace
---- (when more than one argument is allowed), if any <f-args>
---- • nargs: (string) Number of arguments `:command-nargs`
---- • bang: (boolean) "true" if the command was executed with a
---- ! modifier <bang>
---- • line1: (number) The starting line of the command range
---- <line1>
---- • line2: (number) The final line of the command range
---- <line2>
---- • range: (number) The number of items in the command range:
---- 0, 1, or 2 <range>
---- • count: (number) Any count supplied <count>
---- • reg: (string) The optional register, if specified <reg>
---- • mods: (string) Command modifiers, if any <mods>
---- • smods: (table) Command modifiers in a structured format.
---- Has the same structure as the "mods" key of
---- `nvim_parse_cmd()`.
+--- @param name string Name of the new user command. Must begin with an uppercase letter.
+--- @param command any Replacement command to execute when this user command is executed. When called
+--- from Lua, the command can also be a Lua function. The function is called with a
+--- single table argument that contains the following keys:
+--- - name: (string) Command name
+--- - args: (string) The args passed to the command, if any [<args>]
+--- - fargs: (table) The args split by unescaped whitespace (when more than one
+--- argument is allowed), if any [<f-args>]
+--- - nargs: (string) Number of arguments `:command-nargs`
+--- - bang: (boolean) "true" if the command was executed with a ! modifier [<bang>]
+--- - line1: (number) The starting line of the command range [<line1>]
+--- - line2: (number) The final line of the command range [<line2>]
+--- - range: (number) The number of items in the command range: 0, 1, or 2 [<range>]
+--- - count: (number) Any count supplied [<count>]
+--- - reg: (string) The optional register, if specified [<reg>]
+--- - mods: (string) Command modifiers, if any [<mods>]
+--- - smods: (table) Command modifiers in a structured format. Has the same
+--- structure as the "mods" key of `nvim_parse_cmd()`.
--- @param opts vim.api.keyset.user_command Optional `command-attributes`.
---- • Set boolean attributes such as `:command-bang` or
---- `:command-bar` to true (but not `:command-buffer`, use
---- `nvim_buf_create_user_command()` instead).
---- • "complete" `:command-complete` also accepts a Lua function
---- which works like `:command-completion-customlist`.
---- • Other parameters:
---- • desc: (string) Used for listing the command when a Lua
---- function is used for {command}.
---- • force: (boolean, default true) Override any previous
---- definition.
---- • preview: (function) Preview callback for 'inccommand'
---- `:command-preview`
+--- - Set boolean attributes such as `:command-bang` or `:command-bar` to true (but
+--- not `:command-buffer`, use `nvim_buf_create_user_command()` instead).
+--- - "complete" `:command-complete` also accepts a Lua function which works like
+--- `:command-completion-customlist`.
+--- - Other parameters:
+--- - desc: (string) Used for listing the command when a Lua function is used for
+--- {command}.
+--- - force: (boolean, default true) Override any previous definition.
+--- - preview: (function) Preview callback for 'inccommand' `:command-preview`
function vim.api.nvim_create_user_command(name, command, opts) end
--- Delete an autocommand group by id.
---
--- To get a group id one can use `nvim_get_autocmds()`.
---
---- NOTE: behavior differs from `:augroup-delete`. When deleting a group,
---- autocommands contained in this group will also be deleted and cleared.
---- This group will no longer exist.
----
+--- NOTE: behavior differs from `:augroup-delete`. When deleting a group, autocommands contained in
+--- this group will also be deleted and cleared. This group will no longer exist.
+--- @see vim.api.nvim_del_augroup_by_name
+--- @see vim.api.nvim_create_augroup
--- @param id integer Integer The id of the group.
function vim.api.nvim_del_augroup_by_id(id) end
--- Delete an autocommand group by name.
---
---- NOTE: behavior differs from `:augroup-delete`. When deleting a group,
---- autocommands contained in this group will also be deleted and cleared.
---- This group will no longer exist.
----
+--- NOTE: behavior differs from `:augroup-delete`. When deleting a group, autocommands contained in
+--- this group will also be deleted and cleared. This group will no longer exist.
+--- @see `:help autocmd-groups`
--- @param name string String The name of the group.
function vim.api.nvim_del_augroup_by_name(name) end
@@ -1079,14 +1077,20 @@ function vim.api.nvim_del_current_line() end
---
--- To unmap a buffer-local mapping, use `nvim_buf_del_keymap()`.
---
+--- @see vim.api.nvim_set_keymap
--- @param mode string
--- @param lhs string
function vim.api.nvim_del_keymap(mode, lhs) end
--- Deletes an uppercase/file named mark. See `mark-motions`.
---
+--- Note:
+--- Lowercase name (or other buffer-local mark) is an error.
+---
+--- @see vim.api.nvim_buf_del_mark
+--- @see vim.api.nvim_get_mark
--- @param name string Mark name
---- @return boolean
+--- @return boolean # true if the mark was deleted, else false.
function vim.api.nvim_del_mark(name) end
--- Delete a user-defined command.
@@ -1102,14 +1106,13 @@ function vim.api.nvim_del_var(name) end
--- Echo a message.
---
--- @param chunks any[] A list of `[text, hl_group]` arrays, each representing a
---- text chunk with specified highlight. `hl_group` element can
---- be omitted for no highlight.
+--- text chunk with specified highlight. `hl_group` element
+--- can be omitted for no highlight.
--- @param history boolean if true, add to `message-history`.
--- @param opts vim.api.keyset.echo_opts Optional parameters.
---- • verbose: Message was printed as a result of 'verbose' option
---- if Nvim was invoked with -V3log_file, the message will be
---- redirected to the log_file and suppressed from direct
---- output.
+--- - verbose: Message was printed as a result of 'verbose' option
+--- if Nvim was invoked with -V3log_file, the message will be
+--- redirected to the log_file and suppressed from direct output.
function vim.api.nvim_echo(chunks, history, opts) end
--- Writes a message to the Vim error buffer. Does not append "\n", the
@@ -1121,39 +1124,43 @@ function vim.api.nvim_err_write(str) end
--- Writes a message to the Vim error buffer. Appends "\n", so the buffer is
--- flushed (and displayed).
---
+--- @see vim.api.nvim_err_write
--- @param str string Message
function vim.api.nvim_err_writeln(str) end
---- Evaluates a Vimscript `expression`. Dictionaries and Lists are recursively
---- expanded.
+--- Evaluates a Vimscript `expression`. Dicts and Lists are recursively expanded.
---
--- On execution error: fails with Vimscript error, updates v:errmsg.
---
--- @param expr string Vimscript expression string
---- @return any
+--- @return any # Evaluation result or expanded object
function vim.api.nvim_eval(expr) end
--- Evaluates statusline string.
---
--- @param str string Statusline string (see 'statusline').
--- @param opts vim.api.keyset.eval_statusline Optional parameters.
---- • winid: (number) `window-ID` of the window to use as context
---- for statusline.
---- • maxwidth: (number) Maximum width of statusline.
---- • fillchar: (string) Character to fill blank spaces in the
---- statusline (see 'fillchars'). Treated as single-width even
---- if it isn't.
---- • highlights: (boolean) Return highlight information.
---- • use_winbar: (boolean) Evaluate winbar instead of statusline.
---- • use_tabline: (boolean) Evaluate tabline instead of
---- statusline. When true, {winid} is ignored. Mutually
---- exclusive with {use_winbar}.
---- • use_statuscol_lnum: (number) Evaluate statuscolumn for this
---- line number instead of statusline.
---- @return table<string,any>
+--- - winid: (number) `window-ID` of the window to use as context for statusline.
+--- - maxwidth: (number) Maximum width of statusline.
+--- - fillchar: (string) Character to fill blank spaces in the statusline (see
+--- 'fillchars'). Treated as single-width even if it isn't.
+--- - highlights: (boolean) Return highlight information.
+--- - use_winbar: (boolean) Evaluate winbar instead of statusline.
+--- - use_tabline: (boolean) Evaluate tabline instead of statusline. When true, {winid}
+--- is ignored. Mutually exclusive with {use_winbar}.
+--- - use_statuscol_lnum: (number) Evaluate statuscolumn for this line number instead of statusline.
+--- @return table<string,any> # Dict containing statusline information, with these keys:
+--- - str: (string) Characters that will be displayed on the statusline.
+--- - width: (number) Display width of the statusline.
+--- - highlights: Array containing highlight information of the statusline. Only included when
+--- the "highlights" key in {opts} is true. Each element of the array is a
+--- |Dict| with these keys:
+--- - start: (number) Byte index (0-based) of first character that uses the highlight.
+--- - group: (string) Name of highlight group.
function vim.api.nvim_eval_statusline(str, opts) end
--- @deprecated
+--- @see vim.api.nvim_exec2
--- @param src string
--- @param output boolean
--- @return string
@@ -1162,33 +1169,38 @@ function vim.api.nvim_exec(src, output) end
--- Executes Vimscript (multiline block of Ex commands), like anonymous
--- `:source`.
---
---- Unlike `nvim_command()` this function supports heredocs, script-scope
---- (s:), etc.
+--- Unlike `nvim_command()` this function supports heredocs, script-scope (s:),
+--- etc.
---
--- On execution error: fails with Vimscript error, updates v:errmsg.
---
+---
+--- @see `:help execute()`
+--- @see vim.api.nvim_command
+--- @see vim.api.nvim_cmd
--- @param src string Vimscript code
--- @param opts vim.api.keyset.exec_opts Optional parameters.
---- • output: (boolean, default false) Whether to capture and
---- return all (non-error, non-shell `:!`) output.
---- @return table<string,any>
+--- - output: (boolean, default false) Whether to capture and return
+--- all (non-error, non-shell `:!`) output.
+--- @return table<string,any> # Dict containing information about execution, with these keys:
+--- - output: (string|nil) Output if `opts.output` is true.
function vim.api.nvim_exec2(src, opts) end
---- Execute all autocommands for {event} that match the corresponding {opts}
---- `autocmd-execute`.
----
+--- Execute all autocommands for {event} that match the corresponding
+--- {opts} `autocmd-execute`.
+--- @see `:help :doautocmd`
--- @param event any (String|Array) The event or events to execute
---- @param opts vim.api.keyset.exec_autocmds Dictionary of autocommand options:
---- • group (string|integer) optional: the autocommand group name
---- or id to match against. `autocmd-groups`.
---- • pattern (string|array) optional: defaults to "*"
---- `autocmd-pattern`. Cannot be used with {buffer}.
---- • buffer (integer) optional: buffer number `autocmd-buflocal`.
---- Cannot be used with {pattern}.
---- • modeline (bool) optional: defaults to true. Process the
---- modeline after the autocommands <nomodeline>.
---- • data (any): arbitrary data to send to the autocommand
---- callback. See `nvim_create_autocmd()` for details.
+--- @param opts vim.api.keyset.exec_autocmds Dict of autocommand options:
+--- - group (string|integer) optional: the autocommand group name or
+--- id to match against. `autocmd-groups`.
+--- - pattern (string|array) optional: defaults to "*" `autocmd-pattern`. Cannot be used
+--- with {buffer}.
+--- - buffer (integer) optional: buffer number `autocmd-buflocal`. Cannot be used with
+--- {pattern}.
+--- - modeline (bool) optional: defaults to true. Process the
+--- modeline after the autocommands [<nomodeline>].
+--- - data (any): arbitrary data to send to the autocommand callback. See
+--- `nvim_create_autocmd()` for details.
function vim.api.nvim_exec_autocmds(event, opts) end
--- Sends input-keys to Nvim, subject to various quirks controlled by `mode`
@@ -1196,31 +1208,34 @@ function vim.api.nvim_exec_autocmds(event, opts) end
---
--- On execution error: does not fail, but updates v:errmsg.
---
---- To input sequences like <C-o> use `nvim_replace_termcodes()` (typically
+--- To input sequences like [<C-o>] use `nvim_replace_termcodes()` (typically
--- with escape_ks=false) to replace `keycodes`, then pass the result to
--- nvim_feedkeys().
---
--- Example:
---
--- ```vim
---- :let key = nvim_replace_termcodes("<C-o>", v:true, v:false, v:true)
---- :call nvim_feedkeys(key, 'n', v:false)
+--- :let key = nvim_replace_termcodes("<C-o>", v:true, v:false, v:true)
+--- :call nvim_feedkeys(key, 'n', v:false)
--- ```
---
----
+--- @see feedkeys()
+--- @see vim_strsave_escape_ks
--- @param keys string to be typed
--- @param mode string behavior flags, see `feedkeys()`
---- @param escape_ks boolean If true, escape K_SPECIAL bytes in `keys`. This should be
---- false if you already used `nvim_replace_termcodes()`, and
---- true otherwise.
+--- @param escape_ks boolean If true, escape K_SPECIAL bytes in `keys`.
+--- This should be false if you already used
+--- `nvim_replace_termcodes()`, and true otherwise.
function vim.api.nvim_feedkeys(keys, mode, escape_ks) end
--- Gets the option information for all options.
---
---- The dictionary has the full option names as keys and option metadata
---- dictionaries as detailed at `nvim_get_option_info2()`.
+--- The dict has the full option names as keys and option metadata dicts as detailed at
+--- `nvim_get_option_info2()`.
---
---- @return table<string,any>
+---
+--- @see vim.api.nvim_get_commands
+--- @return table<string,any> # dict of all options
function vim.api.nvim_get_all_options_info() end
--- Get all autocommands that match the corresponding {opts}.
@@ -1228,39 +1243,68 @@ function vim.api.nvim_get_all_options_info() end
--- These examples will get autocommands matching ALL the given criteria:
---
--- ```lua
---- -- Matches all criteria
---- autocommands = vim.api.nvim_get_autocmds({
---- group = "MyGroup",
---- event = {"BufEnter", "BufWinEnter"},
---- pattern = {"*.c", "*.h"}
---- })
----
---- -- All commands from one group
---- autocommands = vim.api.nvim_get_autocmds({
---- group = "MyGroup",
---- })
+--- -- Matches all criteria
+--- autocommands = vim.api.nvim_get_autocmds({
+--- group = "MyGroup",
+--- event = {"BufEnter", "BufWinEnter"},
+--- pattern = {"*.c", "*.h"}
+--- })
+---
+--- -- All commands from one group
+--- autocommands = vim.api.nvim_get_autocmds({
+--- group = "MyGroup",
+--- })
--- ```
---
---- NOTE: When multiple patterns or events are provided, it will find all the
---- autocommands that match any combination of them.
----
---- @param opts vim.api.keyset.get_autocmds Dictionary with at least one of the following:
---- • group (string|integer): the autocommand group name or id to
---- match against.
---- • event (string|array): event or events to match against
---- `autocmd-events`.
---- • pattern (string|array): pattern or patterns to match against
---- `autocmd-pattern`. Cannot be used with {buffer}
---- • buffer: Buffer number or list of buffer numbers for buffer
---- local autocommands `autocmd-buflocal`. Cannot be used with
---- {pattern}
---- @return vim.api.keyset.get_autocmds.ret[]
+--- NOTE: When multiple patterns or events are provided, it will find all the autocommands that
+--- match any combination of them.
+---
+--- @param opts vim.api.keyset.get_autocmds Dict with at least one of the following:
+--- - group (string|integer): the autocommand group name or id to match against.
+--- - event (string|array): event or events to match against `autocmd-events`.
+--- - pattern (string|array): pattern or patterns to match against `autocmd-pattern`.
+--- Cannot be used with {buffer}
+--- - buffer: Buffer number or list of buffer numbers for buffer local autocommands
+--- `autocmd-buflocal`. Cannot be used with {pattern}
+--- @return vim.api.keyset.get_autocmds.ret[] # Array of autocommands matching the criteria, with each item
+--- containing the following fields:
+--- - id (number): the autocommand id (only when defined with the API).
+--- - group (integer): the autocommand group id.
+--- - group_name (string): the autocommand group name.
+--- - desc (string): the autocommand description.
+--- - event (string): the autocommand event.
+--- - command (string): the autocommand command. Note: this will be empty if a callback is set.
+--- - callback (function|string|nil): Lua function or name of a Vim script function
+--- which is executed when this autocommand is triggered.
+--- - once (boolean): whether the autocommand is only run once.
+--- - pattern (string): the autocommand pattern.
+--- If the autocommand is buffer local |autocmd-buffer-local|:
+--- - buflocal (boolean): true if the autocommand is buffer local.
+--- - buffer (number): the buffer number.
function vim.api.nvim_get_autocmds(opts) end
--- Gets information about a channel.
---
--- @param chan integer channel_id, or 0 for current channel
---- @return table<string,any>
+--- @return table<string,any> # Channel info dict with these keys:
+--- - "id" Channel id.
+--- - "argv" (optional) Job arguments list.
+--- - "stream" Stream underlying the channel.
+--- - "stdio" stdin and stdout of this Nvim instance
+--- - "stderr" stderr of this Nvim instance
+--- - "socket" TCP/IP socket or named pipe
+--- - "job" Job with communication over its stdio.
+--- - "mode" How data received on the channel is interpreted.
+--- - "bytes" Send and receive raw bytes.
+--- - "terminal" |terminal| instance interprets ASCII sequences.
+--- - "rpc" |RPC| communication on the channel is active.
+--- - "pty" (optional) Name of pseudoterminal. On a POSIX system this is a device path like
+--- "/dev/pts/1". If unknown, the key will still be present if a pty is used (e.g.
+--- for conpty on Windows).
+--- - "buffer" (optional) Buffer connected to |terminal| instance.
+--- - "client" (optional) Info about the peer (client on the other end of the RPC channel),
+--- which it provided via |nvim_set_client_info()|.
+---
function vim.api.nvim_get_chan_info(chan) end
--- Returns the 24-bit RGB value of a `nvim_get_color_map()` color name or
@@ -1269,13 +1313,12 @@ function vim.api.nvim_get_chan_info(chan) end
--- Example:
---
--- ```vim
---- :echo nvim_get_color_by_name("Pink")
---- :echo nvim_get_color_by_name("#cbcbcb")
+--- :echo nvim_get_color_by_name("Pink")
+--- :echo nvim_get_color_by_name("#cbcbcb")
--- ```
---
----
--- @param name string Color name or "#rrggbb" string
---- @return integer
+--- @return integer # 24-bit RGB value, or -1 for invalid argument.
function vim.api.nvim_get_color_by_name(name) end
--- Returns a map of color names and RGB values.
@@ -1283,67 +1326,75 @@ function vim.api.nvim_get_color_by_name(name) end
--- Keys are color names (e.g. "Aqua") and values are 24-bit RGB color values
--- (e.g. 65535).
---
---- @return table<string,integer>
+--- @return table<string,integer> # Map of color names and RGB values.
function vim.api.nvim_get_color_map() end
--- Gets a map of global (non-buffer-local) Ex commands.
---
--- Currently only `user-commands` are supported, not builtin Ex commands.
---
---- @param opts vim.api.keyset.get_commands Optional parameters. Currently only supports {"builtin":false}
---- @return table<string,any>
+---
+--- @see vim.api.nvim_get_all_options_info
+--- @param opts vim.api.keyset.get_commands Optional parameters. Currently only supports
+--- {"builtin":false}
+--- @return table<string,any> # Map of maps describing commands.
function vim.api.nvim_get_commands(opts) end
--- Gets a map of the current editor state.
---
--- @param opts vim.api.keyset.context Optional parameters.
---- • types: List of `context-types` ("regs", "jumps", "bufs",
---- "gvars", …) to gather, or empty for "all".
---- @return table<string,any>
+--- - types: List of `context-types` ("regs", "jumps", "bufs",
+--- "gvars", …) to gather, or empty for "all".
+--- @return table<string,any> # map of global |context|.
function vim.api.nvim_get_context(opts) end
--- Gets the current buffer.
---
---- @return integer
+--- @return integer # Buffer handle
function vim.api.nvim_get_current_buf() end
--- Gets the current line.
---
---- @return string
+--- @return string # Current line string
function vim.api.nvim_get_current_line() end
--- Gets the current tabpage.
---
---- @return integer
+--- @return integer # Tabpage handle
function vim.api.nvim_get_current_tabpage() end
--- Gets the current window.
---
---- @return integer
+--- @return integer # Window handle
function vim.api.nvim_get_current_win() end
--- Gets all or specific highlight groups in a namespace.
---
---- @param ns_id integer Get highlight groups for namespace ns_id
---- `nvim_get_namespaces()`. Use 0 to get global highlight groups
---- `:highlight`.
+--- Note:
+--- When the `link` attribute is defined in the highlight definition
+--- map, other attributes will not be taking effect (see |:hi-link|).
+---
+---
+--- @param ns_id integer Get highlight groups for namespace ns_id `nvim_get_namespaces()`.
+--- Use 0 to get global highlight groups `:highlight`.
--- @param opts vim.api.keyset.get_highlight Options dict:
---- • name: (string) Get a highlight definition by name.
---- • id: (integer) Get a highlight definition by id.
---- • link: (boolean, default true) Show linked group name instead
---- of effective definition `:hi-link`.
---- • create: (boolean, default true) When highlight group doesn't
---- exist create it.
---- @return vim.api.keyset.hl_info
+--- - name: (string) Get a highlight definition by name.
+--- - id: (integer) Get a highlight definition by id.
+--- - link: (boolean, default true) Show linked group name instead of effective definition `:hi-link`.
+--- - create: (boolean, default true) When highlight group doesn't exist create it.
+--- @return vim.api.keyset.get_hl_info # Highlight groups as a map from group name to a highlight definition map as in |nvim_set_hl()|,
+--- or only a single highlight definition map if requested by name or id.
function vim.api.nvim_get_hl(ns_id, opts) end
--- @deprecated
+--- @see vim.api.nvim_get_hl_by_name
--- @param hl_id integer
--- @param rgb boolean
--- @return table<string,any>
function vim.api.nvim_get_hl_by_id(hl_id, rgb) end
--- @deprecated
+--- @see vim.api.nvim_get_hl_by_id
--- @param name string
--- @param rgb boolean
--- @return table<string,any>
@@ -1352,7 +1403,6 @@ function vim.api.nvim_get_hl_by_name(name, rgb) end
--- Gets a highlight group by name
---
--- similar to `hlID()`, but allocates a new ID if not present.
----
--- @param name string
--- @return integer
function vim.api.nvim_get_hl_id_by_name(name) end
@@ -1360,39 +1410,46 @@ function vim.api.nvim_get_hl_id_by_name(name) end
--- Gets the active highlight namespace.
---
--- @param opts vim.api.keyset.get_ns Optional parameters
---- • winid: (number) `window-ID` for retrieving a window's
---- highlight namespace. A value of -1 is returned when
---- `nvim_win_set_hl_ns()` has not been called for the window
---- (or was called with a namespace of -1).
---- @return integer
+--- - winid: (number) `window-ID` for retrieving a window's highlight
+--- namespace. A value of -1 is returned when `nvim_win_set_hl_ns()`
+--- has not been called for the window (or was called with a namespace
+--- of -1).
+--- @return integer # Namespace id, or -1
function vim.api.nvim_get_hl_ns(opts) end
--- Gets a list of global (non-buffer-local) `mapping` definitions.
---
--- @param mode string Mode short-name ("n", "i", "v", ...)
---- @return vim.api.keyset.keymap[]
+--- @return vim.api.keyset.keymap[] # Array of |maparg()|-like dictionaries describing mappings.
+--- The "buffer" key is always zero.
function vim.api.nvim_get_keymap(mode) end
--- Returns a `(row, col, buffer, buffername)` tuple representing the position
---- of the uppercase/file named mark. "End of line" column position is
---- returned as `v:maxcol` (big number). See `mark-motions`.
+--- of the uppercase/file named mark. "End of line" column position is returned
+--- as `v:maxcol` (big number). See `mark-motions`.
---
--- Marks are (1,0)-indexed. `api-indexing`
---
+--- Note:
+--- Lowercase name (or other buffer-local mark) is an error.
+---
+--- @see vim.api.nvim_buf_set_mark
+--- @see vim.api.nvim_del_mark
--- @param name string Mark name
--- @param opts vim.api.keyset.empty Optional parameters. Reserved for future use.
---- @return vim.api.keyset.get_mark
+--- @return vim.api.keyset.get_mark # 4-tuple (row, col, buffer, buffername), (0, 0, 0, '') if the mark is
+--- not set.
function vim.api.nvim_get_mark(name, opts) end
---- Gets the current mode. `mode()` "blocking" is true if Nvim is waiting for
---- input.
+--- Gets the current mode. `mode()`
+--- "blocking" is true if Nvim is waiting for input.
---
---- @return vim.api.keyset.get_mode
+--- @return vim.api.keyset.get_mode # Dict { "mode": String, "blocking": Boolean }
function vim.api.nvim_get_mode() end
--- Gets existing, non-anonymous `namespace`s.
---
---- @return table<string,integer>
+--- @return table<string,integer> # dict that maps from names to namespace ids.
function vim.api.nvim_get_namespaces() end
--- @deprecated
@@ -1407,100 +1464,113 @@ function vim.api.nvim_get_option_info(name) end
--- Gets the option information for one option from arbitrary buffer or window
---
---- Resulting dictionary has keys:
---- • name: Name of the option (like 'filetype')
---- • shortname: Shortened name of the option (like 'ft')
---- • type: type of option ("string", "number" or "boolean")
---- • default: The default value for the option
---- • was_set: Whether the option was set.
---- • last_set_sid: Last set script id (if any)
---- • last_set_linenr: line number where option was set
---- • last_set_chan: Channel where option was set (0 for local)
---- • scope: one of "global", "win", or "buf"
---- • global_local: whether win or buf option has a global value
---- • commalist: List of comma separated values
---- • flaglist: List of single char flags
----
---- When {scope} is not provided, the last set information applies to the
---- local value in the current buffer or window if it is available, otherwise
---- the global value information is returned. This behavior can be disabled by
+--- Resulting dict has keys:
+--- - name: Name of the option (like 'filetype')
+--- - shortname: Shortened name of the option (like 'ft')
+--- - type: type of option ("string", "number" or "boolean")
+--- - default: The default value for the option
+--- - was_set: Whether the option was set.
+---
+--- - last_set_sid: Last set script id (if any)
+--- - last_set_linenr: line number where option was set
+--- - last_set_chan: Channel where option was set (0 for local)
+---
+--- - scope: one of "global", "win", or "buf"
+--- - global_local: whether win or buf option has a global value
+---
+--- - commalist: List of comma separated values
+--- - flaglist: List of single char flags
+---
+--- When {scope} is not provided, the last set information applies to the local
+--- value in the current buffer or window if it is available, otherwise the
+--- global value information is returned. This behavior can be disabled by
--- explicitly specifying {scope} in the {opts} table.
---
--- @param name string Option name
--- @param opts vim.api.keyset.option Optional parameters
---- • scope: One of "global" or "local". Analogous to `:setglobal`
---- and `:setlocal`, respectively.
---- • win: `window-ID`. Used for getting window local options.
---- • buf: Buffer number. Used for getting buffer local options.
---- Implies {scope} is "local".
---- @return vim.api.keyset.get_option_info
+--- - scope: One of "global" or "local". Analogous to
+--- `:setglobal` and `:setlocal`, respectively.
+--- - win: `window-ID`. Used for getting window local options.
+--- - buf: Buffer number. Used for getting buffer local options.
+--- Implies {scope} is "local".
+--- @return vim.api.keyset.get_option_info # Option Information
function vim.api.nvim_get_option_info2(name, opts) end
--- Gets the value of an option. The behavior of this function matches that of
--- `:set`: the local value of an option is returned if it exists; otherwise,
---- the global value is returned. Local values always correspond to the
---- current buffer or window, unless "buf" or "win" is set in {opts}.
+--- the global value is returned. Local values always correspond to the current
+--- buffer or window, unless "buf" or "win" is set in {opts}.
---
--- @param name string Option name
--- @param opts vim.api.keyset.option Optional parameters
---- • scope: One of "global" or "local". Analogous to `:setglobal`
---- and `:setlocal`, respectively.
---- • win: `window-ID`. Used for getting window local options.
---- • buf: Buffer number. Used for getting buffer local options.
---- Implies {scope} is "local".
---- • filetype: `filetype`. Used to get the default option for a
---- specific filetype. Cannot be used with any other option.
---- Note: this will trigger `ftplugin` and all `FileType`
---- autocommands for the corresponding filetype.
---- @return any
+--- - scope: One of "global" or "local". Analogous to
+--- `:setglobal` and `:setlocal`, respectively.
+--- - win: `window-ID`. Used for getting window local options.
+--- - buf: Buffer number. Used for getting buffer local options.
+--- Implies {scope} is "local".
+--- - filetype: `filetype`. Used to get the default option for a
+--- specific filetype. Cannot be used with any other option.
+--- Note: this will trigger `ftplugin` and all `FileType`
+--- autocommands for the corresponding filetype.
+--- @return any # Option value
function vim.api.nvim_get_option_value(name, opts) end
--- Gets info describing process `pid`.
---
--- @param pid integer
---- @return any
+--- @return any # Map of process properties, or NIL if process not found.
function vim.api.nvim_get_proc(pid) end
--- Gets the immediate children of process `pid`.
---
--- @param pid integer
---- @return any[]
+--- @return any[] # Array of child process ids, empty if process not found.
function vim.api.nvim_get_proc_children(pid) end
---- Find files in runtime directories
+--- Finds files in runtime directories, in 'runtimepath' order.
---
--- "name" can contain wildcards. For example
---- nvim_get_runtime_file("colors/*.vim", true) will return all color scheme
---- files. Always use forward slashes (/) in the search pattern for
+--- `nvim_get_runtime_file("colors/*.{vim,lua}", true)` will return all color
+--- scheme files. Always use forward slashes (/) in the search pattern for
--- subdirectories regardless of platform.
---
--- It is not an error to not find any files. An empty array is returned then.
---
--- @param name string pattern of files to search for
--- @param all boolean whether to return all matches or only the first
---- @return string[]
+--- @return string[] # list of absolute paths to the found files
function vim.api.nvim_get_runtime_file(name, all) end
--- Gets a global (g:) variable.
---
--- @param name string Variable name
---- @return any
+--- @return any # Variable value
function vim.api.nvim_get_var(name) end
--- Gets a v: variable.
---
--- @param name string Variable name
---- @return any
+--- @return any # Variable value
function vim.api.nvim_get_vvar(name) end
---- Queues raw user-input. Unlike `nvim_feedkeys()`, this uses a low-level
---- input buffer and the call is non-blocking (input is processed
---- asynchronously by the eventloop).
+--- Queues raw user-input. Unlike `nvim_feedkeys()`, this uses a low-level input buffer and the call
+--- is non-blocking (input is processed asynchronously by the eventloop).
+---
+--- To input blocks of text, `nvim_paste()` is much faster and should be preferred.
---
--- On execution error: does not fail, but updates v:errmsg.
---
+--- Note:
+--- |keycodes| like [<CR>] are translated, so "<" is special.
+--- To input a literal "<", send [<LT>].
+---
+--- For mouse events use |nvim_input_mouse()|. The pseudokey form
+--- `<LeftMouse><col,row>` is deprecated since |api-level| 6.
+---
+---
--- @param keys string to be typed
---- @return integer
+--- @return integer # Number of bytes actually written (can be fewer than
+--- requested if the buffer becomes full).
function vim.api.nvim_input(keys) end
--- Send mouse event from GUI.
@@ -1508,15 +1578,22 @@ function vim.api.nvim_input(keys) end
--- Non-blocking: does not wait on any result, but queues the event to be
--- processed soon by the event loop.
---
---- @param button string Mouse button: one of "left", "right", "middle", "wheel",
---- "move", "x1", "x2".
---- @param action string For ordinary buttons, one of "press", "drag", "release". For
---- the wheel, one of "up", "down", "left", "right". Ignored for
---- "move".
---- @param modifier string String of modifiers each represented by a single char. The
---- same specifiers are used as for a key press, except that
---- the "-" separator is optional, so "C-A-", "c-a" and "CA"
---- can all be used to specify Ctrl+Alt+click.
+--- Note:
+--- Currently this doesn't support "scripting" multiple mouse events
+--- by calling it multiple times in a loop: the intermediate mouse
+--- positions will be ignored. It should be used to implement real-time
+--- mouse input in a GUI. The deprecated pseudokey form
+--- (`<LeftMouse><col,row>`) of |nvim_input()| has the same limitation.
+---
+---
+--- @param button string Mouse button: one of "left", "right", "middle", "wheel", "move",
+--- "x1", "x2".
+--- @param action string For ordinary buttons, one of "press", "drag", "release".
+--- For the wheel, one of "up", "down", "left", "right". Ignored for "move".
+--- @param modifier string String of modifiers each represented by a single char.
+--- The same specifiers are used as for a key press, except
+--- that the "-" separator is optional, so "C-A-", "c-a"
+--- and "CA" can all be used to specify Ctrl+Alt+click.
--- @param grid integer Grid number if the client uses `ui-multigrid`, else 0.
--- @param row integer Mouse row-position (zero-based, like redraw events)
--- @param col integer Mouse column-position (zero-based, like redraw events)
@@ -1524,35 +1601,41 @@ function vim.api.nvim_input_mouse(button, action, modifier, grid, row, col) end
--- Gets the current list of buffer handles
---
---- Includes unlisted (unloaded/deleted) buffers, like `:ls!`. Use
---- `nvim_buf_is_loaded()` to check if a buffer is loaded.
+--- Includes unlisted (unloaded/deleted) buffers, like `:ls!`.
+--- Use `nvim_buf_is_loaded()` to check if a buffer is loaded.
---
---- @return integer[]
+--- @return integer[] # List of buffer handles
function vim.api.nvim_list_bufs() end
--- Get information about all open channels.
---
---- @return any[]
+--- @return any[] # Array of Dictionaries, each describing a channel with
+--- the format specified at |nvim_get_chan_info()|.
function vim.api.nvim_list_chans() end
--- Gets the paths contained in `runtime-search-path`.
---
---- @return string[]
+--- @return string[] # List of paths
function vim.api.nvim_list_runtime_paths() end
--- Gets the current list of tabpage handles.
---
---- @return integer[]
+--- @return integer[] # List of tabpage handles
function vim.api.nvim_list_tabpages() end
--- Gets a list of dictionaries representing attached UIs.
---
---- @return any[]
+--- @return any[] # Array of UI dictionaries, each with these keys:
+--- - "height" Requested height of the UI
+--- - "width" Requested width of the UI
+--- - "rgb" true if the UI uses RGB colors (false implies |cterm-colors|)
+--- - "ext_..." Requested UI extensions, see |ui-option|
+--- - "chan" |channel-id| of remote UI
function vim.api.nvim_list_uis() end
--- Gets the current list of window handles.
---
---- @return integer[]
+--- @return integer[] # List of window handles
function vim.api.nvim_list_wins() end
--- Sets the current editor state from the given `context` map.
@@ -1575,210 +1658,194 @@ 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 will
---- be echoed directly by the terminal. This is useful to display ANSI
---- terminal sequences returned as part of a rpc message, or similar.
+--- connected to an external process. Instead, input send 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.
---
--- Note: to directly initiate the terminal using the right size, display the
--- buffer in a configured window before calling this. For instance, for a
--- floating display, first create an empty buffer using `nvim_create_buf()`,
---- then display it using `nvim_open_win()`, and then call this function. Then
---- `nvim_chan_send()` can be called immediately to process sequences in a
---- virtual terminal having the intended size.
+--- then display it using `nvim_open_win()`, and then call this function.
+--- Then `nvim_chan_send()` can be called immediately to process sequences
+--- in a virtual terminal having the intended size.
---
--- @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 mode. Note: keypresses are sent raw as they would
---- be to the pty master end. For instance, a carriage return is
---- sent as a "\r", not as a "\n". `textlock` applies. It is
---- possible to call `nvim_chan_send()` directly in the callback
---- however. `["input", term, bufnr, data]`
---- • force_crlf: (boolean, default true) Convert "\n" to "\r\n".
---- @return integer
+--- - on_input: Lua callback for input sent, i e keypresses in terminal
+--- mode. Note: keypresses are sent raw as they would be to the pty
+--- master end. For instance, a carriage return is sent
+--- as a "\r", not as a "\n". `textlock` applies. It is possible
+--- to call `nvim_chan_send()` directly in the callback however.
+--- `["input", term, bufnr, data]`
+--- - force_crlf: (boolean, default true) Convert "\n" to "\r\n".
+--- @return integer # Channel id, or 0 on error
function vim.api.nvim_open_term(buffer, opts) end
--- Opens a new split window, or a floating window if `relative` is specified,
--- or an external window (managed by the UI) if `external` is specified.
---
--- Floats are windows that are drawn above the split layout, at some anchor
---- position in some other window. Floats can be drawn internally or by
---- external GUI with the `ui-multigrid` extension. External windows are only
---- supported with multigrid GUIs, and are displayed as separate top-level
---- windows.
+--- position in some other window. Floats can be drawn internally or by external
+--- GUI with the `ui-multigrid` extension. External windows are only supported
+--- with multigrid GUIs, and are displayed as separate top-level windows.
---
--- For a general overview of floats, see `api-floatwin`.
---
--- The `width` and `height` of the new window must be specified when opening
--- a floating window, but are optional for normal windows.
---
---- If `relative` and `external` are omitted, a normal "split" window is
---- created. The `win` property determines which window will be split. If no
---- `win` is provided or `win == 0`, a window will be created adjacent to the
---- current window. If -1 is provided, a top-level split will be created.
---- `vertical` and `split` are only valid for normal windows, and are used to
---- control split direction. For `vertical`, the exact direction is determined
---- by `'splitright'` and `'splitbelow'`. Split windows cannot have
---- `bufpos`/`row`/`col`/`border`/`title`/`footer` properties.
+--- If `relative` and `external` are omitted, a normal "split" window is created.
+--- The `win` property determines which window will be split. If no `win` is
+--- provided or `win == 0`, a window will be created adjacent to the current window.
+--- If -1 is provided, a top-level split will be created. `vertical` and `split` are
+--- only valid for normal windows, and are used to control split direction. For `vertical`,
+--- the exact direction is determined by `'splitright'` and `'splitbelow'`.
+--- Split windows cannot have `bufpos`/`row`/`col`/`border`/`title`/`footer`
+--- properties.
---
--- With relative=editor (row=0,col=0) refers to the top-left corner of the
--- screen-grid and (row=Lines-1,col=Columns-1) refers to the bottom-right
--- corner. Fractional values are allowed, but the builtin implementation
--- (used by non-multigrid UIs) will always round down to nearest integer.
---
---- Out-of-bounds values, and configurations that make the float not fit
---- inside the main editor, are allowed. The builtin implementation truncates
---- values so floats are fully within the main screen grid. External GUIs
---- could let floats hover outside of the main window like a tooltip, but this
---- should not be used to specify arbitrary WM screen positions.
+--- Out-of-bounds values, and configurations that make the float not fit inside
+--- the main editor, are allowed. The builtin implementation truncates values
+--- so floats are fully within the main screen grid. External GUIs
+--- could let floats hover outside of the main window like a tooltip, but
+--- this should not be used to specify arbitrary WM screen positions.
---
--- Example (Lua): window-relative float
---
--- ```lua
---- vim.api.nvim_open_win(0, false,
---- {relative='win', row=3, col=3, width=12, height=3})
+--- vim.api.nvim_open_win(0, false,
+--- {relative='win', row=3, col=3, width=12, height=3})
--- ```
---
--- Example (Lua): buffer-relative float (travels as buffer is scrolled)
---
--- ```lua
---- vim.api.nvim_open_win(0, false,
---- {relative='win', width=12, height=3, bufpos={100,10}})
+--- vim.api.nvim_open_win(0, false,
+--- {relative='win', width=12, height=3, bufpos={100,10}})
--- ```
---
--- Example (Lua): vertical split left of the current window
---
--- ```lua
---- vim.api.nvim_open_win(0, false, {
---- split = 'left',
---- win = 0
---- })
+--- vim.api.nvim_open_win(0, false, {
+--- split = 'left',
+--- win = 0
+--- })
--- ```
---
----
--- @param buffer integer Buffer to display, or 0 for current buffer
--- @param enter boolean Enter the window (make it the current window)
--- @param config vim.api.keyset.win_config Map defining the window configuration. Keys:
---- • relative: Sets the window layout to "floating", placed at
---- (row,col) coordinates relative to:
---- • "editor" The global editor grid
---- • "win" Window given by the `win` field, or current
---- window.
---- • "cursor" Cursor position in current window.
---- • "mouse" Mouse position
---- • win: `window-ID` window to split, or relative window when
---- creating a float (relative="win").
---- • anchor: Decides which corner of the float to place at
---- (row,col):
---- • "NW" northwest (default)
---- • "NE" northeast
---- • "SW" southwest
---- • "SE" southeast
---- • width: Window width (in character cells). Minimum of 1.
---- • height: Window height (in character cells). Minimum of 1.
---- • bufpos: Places float relative to buffer text (only when
---- relative="win"). Takes a tuple of zero-indexed
---- `[line, column]`. `row` and `col` if given are applied
---- relative to this position, else they default to:
---- • `row=1` and `col=0` if `anchor` is "NW" or "NE"
---- • `row=0` and `col=0` if `anchor` is "SW" or "SE" (thus
---- like a tooltip near the buffer text).
---- • row: Row position in units of "screen cell height", may be
---- fractional.
---- • col: Column position in units of "screen cell width", may
---- be fractional.
---- • focusable: Enable focus by user actions (wincmds, mouse
---- events). Defaults to true. Non-focusable windows can be
---- entered by `nvim_set_current_win()`.
---- • external: GUI should display the window as an external
---- top-level window. Currently accepts no other positioning
---- configuration together with this.
---- • zindex: Stacking order. floats with higher `zindex` go on
---- top on floats with lower indices. Must be larger than
---- zero. The following screen elements have hard-coded
---- z-indices:
---- • 100: insert completion popupmenu
---- • 200: message scrollback
---- • 250: cmdline completion popupmenu (when
---- wildoptions+=pum) The default value for floats are 50.
---- In general, values below 100 are recommended, unless
---- there is a good reason to overshadow builtin elements.
---- • style: (optional) Configure the appearance of the window.
---- Currently only supports one value:
---- • "minimal" Nvim will display the window with many UI
---- options disabled. This is useful when displaying a
---- temporary float where the text should not be edited.
---- Disables 'number', 'relativenumber', 'cursorline',
---- 'cursorcolumn', 'foldcolumn', 'spell' and 'list'
---- options. 'signcolumn' is changed to `auto` and
---- 'colorcolumn' is cleared. 'statuscolumn' is changed to
---- empty. The end-of-buffer region is hidden by setting
---- `eob` flag of 'fillchars' to a space char, and clearing
---- the `hl-EndOfBuffer` region in 'winhighlight'.
---- • border: Style of (optional) window border. This can either
---- be a string or an array. The string values are
---- • "none": No border (default).
---- • "single": A single line box.
---- • "double": A double line box.
---- • "rounded": Like "single", but with rounded corners
---- ("╭" etc.).
---- • "solid": Adds padding by a single whitespace cell.
---- • "shadow": A drop shadow effect by blending with the
---- background.
---- • If it is an array, it should have a length of eight or
---- any divisor of eight. The array will specify the eight
---- chars building up the border in a clockwise fashion
---- starting with the top-left corner. As an example, the
---- double box style could be specified as:
---- ```
---- [ "╔", "═" ,"╗", "║", "╝", "═", "╚", "║" ].
---- ```
----
---- If the number of chars are less than eight, they will be
---- repeated. Thus an ASCII border could be specified as
---- ```
---- [ "/", "-", \"\\\\\", "|" ],
---- ```
----
---- or all chars the same as
---- ```
---- [ "x" ].
---- ```
----
---- An empty string can be used to turn off a specific border,
---- for instance,
---- ```
---- [ "", "", "", ">", "", "", "", "<" ]
---- ```
----
---- will only make vertical borders but not horizontal ones.
---- By default, `FloatBorder` highlight is used, which links
---- to `WinSeparator` when not defined. It could also be
---- specified by character:
---- ```
---- [ ["+", "MyCorner"], ["x", "MyBorder"] ].
---- ```
----
---- • title: Title (optional) in window border, string or list.
---- List should consist of `[text, highlight]` tuples. If
---- string, the default highlight group is `FloatTitle`.
---- • title_pos: Title position. Must be set with `title`
---- option. Value can be one of "left", "center", or "right".
---- Default is `"left"`.
---- • footer: Footer (optional) in window border, string or
---- list. List should consist of `[text, highlight]` tuples.
---- If string, the default highlight group is `FloatFooter`.
---- • footer_pos: Footer position. Must be set with `footer`
---- option. Value can be one of "left", "center", or "right".
---- Default is `"left"`.
---- • noautocmd: If true then all autocommands are blocked for
---- the duration of the call.
---- • fixed: If true when anchor is NW or SW, the float window
---- would be kept fixed even if the window would be truncated.
---- • hide: If true the floating window will be hidden.
---- • vertical: Split vertically `:vertical`.
---- • split: Split direction: "left", "right", "above", "below".
---- @return integer
+--- - relative: Sets the window layout to "floating", placed at (row,col)
+--- coordinates relative to:
+--- - "editor" The global editor grid
+--- - "win" Window given by the `win` field, or current window.
+--- - "cursor" Cursor position in current window.
+--- - "mouse" Mouse position
+--- - win: `window-ID` window to split, or relative window when creating a
+--- float (relative="win").
+--- - anchor: Decides which corner of the float to place at (row,col):
+--- - "NW" northwest (default)
+--- - "NE" northeast
+--- - "SW" southwest
+--- - "SE" southeast
+--- - width: Window width (in character cells). Minimum of 1.
+--- - height: Window height (in character cells). Minimum of 1.
+--- - bufpos: Places float relative to buffer text (only when
+--- relative="win"). Takes a tuple of zero-indexed `[line, column]`.
+--- `row` and `col` if given are applied relative to this
+--- position, else they default to:
+--- - `row=1` and `col=0` if `anchor` is "NW" or "NE"
+--- - `row=0` and `col=0` if `anchor` is "SW" or "SE"
+--- (thus like a tooltip near the buffer text).
+--- - row: Row position in units of "screen cell height", may be fractional.
+--- - col: Column position in units of "screen cell width", may be
+--- fractional.
+--- - focusable: Enable focus by user actions (wincmds, mouse events).
+--- Defaults to true. Non-focusable windows can be entered by
+--- `nvim_set_current_win()`.
+--- - external: GUI should display the window as an external
+--- top-level window. Currently accepts no other positioning
+--- configuration together with this.
+--- - zindex: Stacking order. floats with higher `zindex` go on top on
+--- floats with lower indices. Must be larger than zero. The
+--- following screen elements have hard-coded z-indices:
+--- - 100: insert completion popupmenu
+--- - 200: message scrollback
+--- - 250: cmdline completion popupmenu (when wildoptions+=pum)
+--- The default value for floats are 50. In general, values below 100 are
+--- recommended, unless there is a good reason to overshadow builtin
+--- elements.
+--- - style: (optional) Configure the appearance of the window. Currently
+--- only supports one value:
+--- - "minimal" Nvim will display the window with many UI options
+--- disabled. This is useful when displaying a temporary
+--- float where the text should not be edited. Disables
+--- 'number', 'relativenumber', 'cursorline', 'cursorcolumn',
+--- 'foldcolumn', 'spell' and 'list' options. 'signcolumn'
+--- is changed to `auto` and 'colorcolumn' is cleared.
+--- 'statuscolumn' is changed to empty. The end-of-buffer
+--- region is hidden by setting `eob` flag of
+--- 'fillchars' to a space char, and clearing the
+--- `hl-EndOfBuffer` region in 'winhighlight'.
+--- - border: Style of (optional) window border. This can either be a string
+--- or an array. The string values are
+--- - "none": No border (default).
+--- - "single": A single line box.
+--- - "double": A double line box.
+--- - "rounded": Like "single", but with rounded corners ("╭" etc.).
+--- - "solid": Adds padding by a single whitespace cell.
+--- - "shadow": A drop shadow effect by blending with the background.
+--- - If it is an array, it should have a length of eight or any divisor of
+--- eight. The array will specify the eight chars building up the border
+--- in a clockwise fashion starting with the top-left corner. As an
+--- example, the double box style could be specified as:
+--- ```
+--- [ "╔", "═" ,"╗", "║", "╝", "═", "╚", "║" ].
+--- ```
+--- If the number of chars are less than eight, they will be repeated. Thus
+--- an ASCII border could be specified as
+--- ```
+--- [ "/", "-", \"\\\\\", "|" ],
+--- ```
+--- or all chars the same as
+--- ```
+--- [ "x" ].
+--- ```
+--- An empty string can be used to turn off a specific border, for instance,
+--- ```
+--- [ "", "", "", ">", "", "", "", "<" ]
+--- ```
+--- will only make vertical borders but not horizontal ones.
+--- By default, `FloatBorder` highlight is used, which links to `WinSeparator`
+--- when not defined. It could also be specified by character:
+--- ```
+--- [ ["+", "MyCorner"], ["x", "MyBorder"] ].
+--- ```
+--- - title: Title (optional) in window border, string or list.
+--- List should consist of `[text, highlight]` tuples.
+--- If string, or a tuple lacks a highlight, the default highlight group is `FloatTitle`.
+--- - title_pos: Title position. Must be set with `title` option.
+--- Value can be one of "left", "center", or "right".
+--- Default is `"left"`.
+--- - footer: Footer (optional) in window border, string or list.
+--- List should consist of `[text, highlight]` tuples.
+--- If string, or a tuple lacks a highlight, the default highlight group is `FloatFooter`.
+--- - footer_pos: Footer position. Must be set with `footer` option.
+--- Value can be one of "left", "center", or "right".
+--- Default is `"left"`.
+--- - noautocmd: If true then all autocommands are blocked for the duration of
+--- the call.
+--- - fixed: If true when anchor is NW or SW, the float window
+--- would be kept fixed even if the window would be truncated.
+--- - hide: If true the floating window will be hidden.
+--- - vertical: Split vertically `:vertical`.
+--- - split: Split direction: "left", "right", "above", "below".
+--- @return integer # Window handle, or 0 on error
function vim.api.nvim_open_win(buffer, enter, config) end
--- Writes a message to the Vim output buffer. Does not append "\n", the
@@ -1793,92 +1860,207 @@ function vim.api.nvim_out_write(str) end
---
--- @param str string Command line string to parse. Cannot contain "\n".
--- @param opts vim.api.keyset.empty Optional parameters. Reserved for future use.
---- @return vim.api.keyset.parse_cmd
+--- @return vim.api.keyset.parse_cmd # Dict containing command information, with these keys:
+--- - cmd: (string) Command name.
+--- - range: (array) (optional) Command range ([<line1>] [<line2>]).
+--- Omitted if command doesn't accept a range.
+--- Otherwise, has no elements if no range was specified, one element if
+--- only a single range item was specified, or two elements if both range
+--- items were specified.
+--- - count: (number) (optional) Command [<count>].
+--- Omitted if command cannot take a count.
+--- - reg: (string) (optional) Command [<register>].
+--- Omitted if command cannot take a register.
+--- - bang: (boolean) Whether command contains a [<bang>] (!) modifier.
+--- - args: (array) Command arguments.
+--- - addr: (string) Value of |:command-addr|. Uses short name or "line" for -addr=lines.
+--- - nargs: (string) Value of |:command-nargs|.
+--- - nextcmd: (string) Next command if there are multiple commands separated by a |:bar|.
+--- Empty if there isn't a next command.
+--- - magic: (dict) Which characters have special meaning in the command arguments.
+--- - file: (boolean) The command expands filenames. Which means characters such as "%",
+--- "#" and wildcards are expanded.
+--- - bar: (boolean) The "|" character is treated as a command separator and the double
+--- quote character (") is treated as the start of a comment.
+--- - mods: (dict) |:command-modifiers|.
+--- - filter: (dict) |:filter|.
+--- - pattern: (string) Filter pattern. Empty string if there is no filter.
+--- - force: (boolean) Whether filter is inverted or not.
+--- - silent: (boolean) |:silent|.
+--- - emsg_silent: (boolean) |:silent!|.
+--- - unsilent: (boolean) |:unsilent|.
+--- - sandbox: (boolean) |:sandbox|.
+--- - noautocmd: (boolean) |:noautocmd|.
+--- - browse: (boolean) |:browse|.
+--- - confirm: (boolean) |:confirm|.
+--- - hide: (boolean) |:hide|.
+--- - horizontal: (boolean) |:horizontal|.
+--- - keepalt: (boolean) |:keepalt|.
+--- - keepjumps: (boolean) |:keepjumps|.
+--- - keepmarks: (boolean) |:keepmarks|.
+--- - keeppatterns: (boolean) |:keeppatterns|.
+--- - lockmarks: (boolean) |:lockmarks|.
+--- - noswapfile: (boolean) |:noswapfile|.
+--- - tab: (integer) |:tab|. -1 when omitted.
+--- - verbose: (integer) |:verbose|. -1 when omitted.
+--- - vertical: (boolean) |:vertical|.
+--- - split: (string) Split modifier string, is an empty string when there's no split
+--- modifier. If there is a split modifier it can be one of:
+--- - "aboveleft": |:aboveleft|.
+--- - "belowright": |:belowright|.
+--- - "topleft": |:topleft|.
+--- - "botright": |:botright|.
function vim.api.nvim_parse_cmd(str, opts) end
--- Parse a Vimscript expression.
---
--- @param expr string Expression to parse. Always treated as a single line.
--- @param flags string Flags:
---- • "m" if multiple expressions in a row are allowed (only the
---- first one will be parsed),
---- • "E" if EOC tokens are not allowed (determines whether they
---- will stop parsing process or be recognized as an
---- operator/space, though also yielding an error).
---- • "l" when needing to start parsing with lvalues for ":let"
---- or ":for". Common flag sets:
---- • "m" to parse like for `":echo"`.
---- • "E" to parse like for `"<C-r>="`.
---- • empty string for ":call".
---- • "lm" to parse for ":let".
---- @param highlight boolean If true, return value will also include "highlight" key
---- containing array of 4-tuples (arrays) (Integer, Integer,
---- Integer, String), where first three numbers define the
---- highlighted region and represent line, starting column
---- and ending column (latter exclusive: one should highlight
---- region [start_col, end_col)).
---- @return table<string,any>
+--- - "m" if multiple expressions in a row are allowed (only
+--- the first one will be parsed),
+--- - "E" if EOC tokens are not allowed (determines whether
+--- they will stop parsing process or be recognized as an
+--- operator/space, though also yielding an error).
+--- - "l" when needing to start parsing with lvalues for
+--- ":let" or ":for".
+--- Common flag sets:
+--- - "m" to parse like for `":echo"`.
+--- - "E" to parse like for `"<C-r>="`.
+--- - empty string for ":call".
+--- - "lm" to parse for ":let".
+--- @param highlight boolean If true, return value will also include "highlight"
+--- key containing array of 4-tuples (arrays) (Integer,
+--- Integer, Integer, String), where first three numbers
+--- define the highlighted region and represent line,
+--- starting column and ending column (latter exclusive:
+--- one should highlight region [start_col, end_col)).
+--- @return table<string,any> #
+--- - AST: top-level dict with these keys:
+--- - "error": Dict with error, present only if parser saw some
+--- error. Contains the following keys:
+--- - "message": String, error message in printf format, translated.
+--- Must contain exactly one "%.*s".
+--- - "arg": String, error message argument.
+--- - "len": Amount of bytes successfully parsed. With flags equal to ""
+--- that should be equal to the length of expr string.
+--- ("Successfully parsed" here means "participated in AST
+--- creation", not "till the first error".)
+--- - "ast": AST, either nil or a dict with these keys:
+--- - "type": node type, one of the value names from ExprASTNodeType
+--- stringified without "kExprNode" prefix.
+--- - "start": a pair `[line, column]` describing where node is "started"
+--- where "line" is always 0 (will not be 0 if you will be
+--- using this API on e.g. ":let", but that is not
+--- present yet). Both elements are Integers.
+--- - "len": “length” of the node. This and "start" are there for
+--- debugging purposes primary (debugging parser and providing
+--- debug information).
+--- - "children": a list of nodes described in top/"ast". There always
+--- is zero, one or two children, key will not be present
+--- if node has no children. Maximum number of children
+--- may be found in node_maxchildren array.
+--- - Local values (present only for certain nodes):
+--- - "scope": a single Integer, specifies scope for "Option" and
+--- "PlainIdentifier" nodes. For "Option" it is one of
+--- ExprOptScope values, for "PlainIdentifier" it is one of
+--- ExprVarScope values.
+--- - "ident": identifier (without scope, if any), present for "Option",
+--- "PlainIdentifier", "PlainKey" and "Environment" nodes.
+--- - "name": Integer, register name (one character) or -1. Only present
+--- for "Register" nodes.
+--- - "cmp_type": String, comparison type, one of the value names from
+--- ExprComparisonType, stringified without "kExprCmp"
+--- prefix. Only present for "Comparison" nodes.
+--- - "ccs_strategy": String, case comparison strategy, one of the
+--- value names from ExprCaseCompareStrategy,
+--- stringified without "kCCStrategy" prefix. Only
+--- present for "Comparison" nodes.
+--- - "augmentation": String, augmentation type for "Assignment" nodes.
+--- Is either an empty string, "Add", "Subtract" or
+--- "Concat" for "=", "+=", "-=" or ".=" respectively.
+--- - "invert": Boolean, true if result of comparison needs to be
+--- inverted. Only present for "Comparison" nodes.
+--- - "ivalue": Integer, integer value for "Integer" nodes.
+--- - "fvalue": Float, floating-point value for "Float" nodes.
+--- - "svalue": String, value for "SingleQuotedString" and
+--- "DoubleQuotedString" nodes.
function vim.api.nvim_parse_expression(expr, flags, highlight) end
---- Pastes at cursor, in any mode.
+--- Pastes at cursor (in any mode), and sets "redo" so dot (`.`) will repeat the input. UIs call
+--- this to implement "paste", but it's also intended for use by scripts to input large,
+--- dot-repeatable blocks of text (as opposed to `nvim_input()` which is subject to mappings/events
+--- and is thus much slower).
+---
+--- Invokes the `vim.paste()` handler, which handles each mode appropriately.
---
---- Invokes the `vim.paste` handler, which handles each mode appropriately.
---- Sets redo/undo. Faster than `nvim_input()`. Lines break at LF ("\n").
+--- Errors ('nomodifiable', `vim.paste()` failure, …) are reflected in `err` but do not affect the
+--- return value (which is strictly decided by `vim.paste()`). On error or cancel, subsequent calls
+--- are ignored ("drained") until the next paste is initiated (phase 1 or -1).
---
---- Errors ('nomodifiable', `vim.paste()` failure, …) are reflected in `err`
---- but do not affect the return value (which is strictly decided by
---- `vim.paste()`). On error, subsequent calls are ignored ("drained") until
---- the next paste is initiated (phase 1 or -1).
+--- Useful in mappings and scripts to insert multiline text. Example:
---
---- @param data string Multiline input. May be binary (containing NUL bytes).
+--- ```lua
+--- vim.keymap.set('n', 'x', function()
+--- vim.api.nvim_paste([[
+--- line1
+--- line2
+--- line3
+--- ]], false, -1)
+--- end, { buffer = true })
+--- ```
+---
+--- @param data string Multiline input. Lines break at LF ("\n"). May be binary (containing NUL bytes).
--- @param crlf boolean Also break lines at CR and CRLF.
---- @param phase integer -1: paste in a single call (i.e. without streaming). To
---- "stream" a paste, call `nvim_paste` sequentially with these
---- `phase` values:
---- • 1: starts the paste (exactly once)
---- • 2: continues the paste (zero or more times)
---- • 3: ends the paste (exactly once)
---- @return boolean
+--- @param phase integer -1: paste in a single call (i.e. without streaming).
+--- To "stream" a paste, call `nvim_paste` sequentially with
+--- these `phase` values:
+--- - 1: starts the paste (exactly once)
+--- - 2: continues the paste (zero or more times)
+--- - 3: ends the paste (exactly once)
+--- @return boolean #
+--- - true: Client may continue pasting.
+--- - false: Client should cancel the paste.
function vim.api.nvim_paste(data, crlf, phase) end
---- Puts text at cursor, in any mode.
+--- Puts text at cursor, in any mode. For dot-repeatable input, use `nvim_paste()`.
---
--- Compare `:put` and `p` which are always linewise.
---
--- @param lines string[] `readfile()`-style list of lines. `channel-lines`
--- @param type string Edit behavior: any `getregtype()` result, or:
---- • "b" `blockwise-visual` mode (may include width, e.g. "b3")
---- • "c" `charwise` mode
---- • "l" `linewise` mode
---- • "" guess by contents, see `setreg()`
+--- - "b" `blockwise-visual` mode (may include width, e.g. "b3")
+--- - "c" `charwise` mode
+--- - "l" `linewise` mode
+--- - "" guess by contents, see `setreg()`
--- @param after boolean If true insert after cursor (like `p`), or before (like `P`).
--- @param follow boolean If true place cursor at end of inserted text.
function vim.api.nvim_put(lines, type, after, follow) end
---- Replaces terminal codes and `keycodes` (<CR>, <Esc>, ...) in a string with
+--- Replaces terminal codes and `keycodes` ([<CR>], [<Esc>], ...) in a string with
--- the internal representation.
---
+--- @see replace_termcodes
+--- @see cpoptions
--- @param str string String to be converted.
--- @param from_part boolean Legacy Vim parameter. Usually true.
---- @param do_lt boolean Also translate <lt>. Ignored if `special` is false.
---- @param special boolean Replace `keycodes`, e.g. <CR> becomes a "\r" char.
+--- @param do_lt boolean Also translate [<lt>]. Ignored if `special` is false.
+--- @param special boolean Replace `keycodes`, e.g. [<CR>] becomes a "\r" char.
--- @return string
function vim.api.nvim_replace_termcodes(str, from_part, do_lt, special) end
--- Selects an item in the completion popup menu.
---
--- If neither `ins-completion` nor `cmdline-completion` popup menu is active
---- this API call is silently ignored. Useful for an external UI using
---- `ui-popupmenu` to control the popup menu with the mouse. Can also be used
---- in a mapping; use <Cmd> `:map-cmd` or a Lua mapping to ensure the mapping
+--- this API call is silently ignored.
+--- Useful for an external UI using `ui-popupmenu` to control the popup menu with the mouse.
+--- Can also be used in a mapping; use [<Cmd>] `:map-cmd` or a Lua mapping to ensure the mapping
--- doesn't end completion mode.
---
---- @param item integer Index (zero-based) of the item to select. Value of -1 selects
---- nothing and restores the original text.
---- @param insert boolean For `ins-completion`, whether the selection should be
---- inserted in the buffer. Ignored for `cmdline-completion`.
---- @param finish boolean Finish the completion and dismiss the popup menu. Implies
---- {insert}.
+--- @param item integer Index (zero-based) of the item to select. Value of -1 selects nothing
+--- and restores the original text.
+--- @param insert boolean For `ins-completion`, whether the selection should be inserted in the buffer.
+--- Ignored for `cmdline-completion`.
+--- @param finish boolean Finish the completion and dismiss the popup menu. Implies {insert}.
--- @param opts vim.api.keyset.empty Optional parameters. Reserved for future use.
function vim.api.nvim_select_popupmenu_item(item, insert, finish, opts) end
@@ -1909,111 +2091,118 @@ function vim.api.nvim_set_current_win(window) end
--- Set or change decoration provider for a `namespace`
---
---- This is a very general purpose interface for having Lua callbacks being
---- triggered during the redraw code.
+--- This is a very general purpose interface for having Lua callbacks
+--- being triggered during the redraw code.
---
---- The expected usage is to set `extmarks` for the currently redrawn buffer.
---- `nvim_buf_set_extmark()` can be called to add marks on a per-window or
---- per-lines basis. Use the `ephemeral` key to only use the mark for the
---- current screen redraw (the callback will be called again for the next
---- redraw).
+--- The expected usage is to set `extmarks` for the currently
+--- redrawn buffer. `nvim_buf_set_extmark()` can be called to add marks
+--- on a per-window or per-lines basis. Use the `ephemeral` key to only
+--- use the mark for the current screen redraw (the callback will be called
+--- again for the next redraw).
---
--- Note: this function should not be called often. Rather, the callbacks
--- themselves can be used to throttle unneeded callbacks. the `on_start`
--- callback can return `false` to disable the provider until the next redraw.
---- Similarly, return `false` in `on_win` will skip the `on_lines` calls for
---- that window (but any extmarks set in `on_win` will still be used). A
---- plugin managing multiple sources of decoration should ideally only set one
---- provider, and merge the sources internally. You can use multiple `ns_id`
+--- Similarly, return `false` in `on_win` will skip the `on_line` calls
+--- for that window (but any extmarks set in `on_win` will still be used).
+--- A plugin managing multiple sources of decoration should ideally only set
+--- one provider, and merge the sources internally. You can use multiple `ns_id`
--- for the extmarks set/modified inside the callback anyway.
---
---- Note: doing anything other than setting extmarks is considered
---- experimental. Doing things like changing options are not explicitly
---- forbidden, but is likely to have unexpected consequences (such as 100% CPU
---- consumption). doing `vim.rpcnotify` should be OK, but `vim.rpcrequest` is
---- quite dubious for the moment.
+--- Note: doing anything other than setting extmarks is considered experimental.
+--- Doing things like changing options are not explicitly forbidden, but is
+--- likely to have unexpected consequences (such as 100% CPU consumption).
+--- Doing `vim.rpcnotify` should be OK, but `vim.rpcrequest` is quite dubious
+--- for the moment.
---
---- Note: It is not allowed to remove or update extmarks in 'on_line'
---- callbacks.
+--- Note: It is not allowed to remove or update extmarks in `on_line` callbacks.
---
--- @param ns_id integer Namespace id from `nvim_create_namespace()`
--- @param opts vim.api.keyset.set_decoration_provider Table of callbacks:
---- • on_start: called first on each screen redraw
---- ```
---- ["start", tick]
---- ```
+--- - on_start: called first on each screen redraw
+--- ```
+--- ["start", tick]
+--- ```
+--- - on_buf: called for each buffer being redrawn (before
+--- window callbacks)
+--- ```
+--- ["buf", bufnr, tick]
+--- ```
+--- - on_win: called when starting to redraw a specific window.
+--- ```
+--- ["win", winid, bufnr, toprow, botrow]
+--- ```
+--- - on_line: called for each buffer line being redrawn.
+--- (The interaction with fold lines is subject to change)
+--- ```
+--- ["line", winid, bufnr, row]
+--- ```
+--- - on_end: called at the end of a redraw cycle
+--- ```
+--- ["end", tick]
+--- ```
+function vim.api.nvim_set_decoration_provider(ns_id, opts) end
+
+--- Sets a highlight group.
---
---- • on_buf: called for each buffer being redrawn (before window
---- callbacks)
---- ```
---- ["buf", bufnr, tick]
---- ```
+--- Note:
+--- Unlike the `:highlight` command which can update a highlight group,
+--- this function completely replaces the definition. For example:
+--- `nvim_set_hl(0, 'Visual', {})` will clear the highlight group
+--- 'Visual'.
---
---- • on_win: called when starting to redraw a specific window.
---- ```
---- ["win", winid, bufnr, topline, botline]
---- ```
+--- The fg and bg keys also accept the string values `"fg"` or `"bg"`
+--- which act as aliases to the corresponding foreground and background
+--- values of the Normal group. If the Normal group has not been defined,
+--- using these values results in an error.
---
---- • on_line: called for each buffer line being redrawn. (The
---- interaction with fold lines is subject to change)
---- ```
---- ["line", winid, bufnr, row]
---- ```
---
---- • on_end: called at the end of a redraw cycle
---- ```
---- ["end", tick]
---- ```
-function vim.api.nvim_set_decoration_provider(ns_id, opts) end
-
---- Sets a highlight group.
+--- If `link` is used in combination with other attributes; only the
+--- `link` will take effect (see |:hi-link|).
+---
---
--- @param ns_id integer Namespace id for this highlight `nvim_create_namespace()`.
---- Use 0 to set a highlight group globally `:highlight`.
---- Highlights from non-global namespaces are not active by
---- default, use `nvim_set_hl_ns()` or `nvim_win_set_hl_ns()` to
---- activate them.
+--- Use 0 to set a highlight group globally `:highlight`.
+--- Highlights from non-global namespaces are not active by default, use
+--- `nvim_set_hl_ns()` or `nvim_win_set_hl_ns()` to activate them.
--- @param name string Highlight group name, e.g. "ErrorMsg"
--- @param val vim.api.keyset.highlight Highlight definition map, accepts the following keys:
---- • fg: color name or "#RRGGBB", see note.
---- • bg: color name or "#RRGGBB", see note.
---- • sp: color name or "#RRGGBB"
---- • blend: integer between 0 and 100
---- • bold: boolean
---- • standout: boolean
---- • underline: boolean
---- • undercurl: boolean
---- • underdouble: boolean
---- • underdotted: boolean
---- • underdashed: boolean
---- • strikethrough: boolean
---- • italic: boolean
---- • reverse: boolean
---- • nocombine: boolean
---- • link: name of another highlight group to link to, see
---- `:hi-link`.
---- • default: Don't override existing definition `:hi-default`
---- • ctermfg: Sets foreground of cterm color `ctermfg`
---- • ctermbg: Sets background of cterm color `ctermbg`
---- • cterm: cterm attribute map, like `highlight-args`. If not
---- set, cterm attributes will match those from the attribute map
---- documented above.
---- • force: if true force update the highlight group when it
---- exists.
+--- - fg: color name or "#RRGGBB", see note.
+--- - bg: color name or "#RRGGBB", see note.
+--- - sp: color name or "#RRGGBB"
+--- - blend: integer between 0 and 100
+--- - bold: boolean
+--- - standout: boolean
+--- - underline: boolean
+--- - undercurl: boolean
+--- - underdouble: boolean
+--- - underdotted: boolean
+--- - underdashed: boolean
+--- - strikethrough: boolean
+--- - italic: boolean
+--- - reverse: boolean
+--- - nocombine: boolean
+--- - link: name of another highlight group to link to, see `:hi-link`.
+--- - default: Don't override existing definition `:hi-default`
+--- - ctermfg: Sets foreground of cterm color `ctermfg`
+--- - ctermbg: Sets background of cterm color `ctermbg`
+--- - cterm: cterm attribute map, like `highlight-args`. If not set,
+--- cterm attributes will match those from the attribute map
+--- documented above.
+--- - force: if true force update the highlight group when it exists.
function vim.api.nvim_set_hl(ns_id, name, val) end
---- Set active namespace for highlights defined with `nvim_set_hl()`. This can
---- be set for a single window, see `nvim_win_set_hl_ns()`.
+--- Set active namespace for highlights defined with `nvim_set_hl()`. This can be set for
+--- a single window, see `nvim_win_set_hl_ns()`.
---
--- @param ns_id integer the namespace to use
function vim.api.nvim_set_hl_ns(ns_id) end
---- Set active namespace for highlights defined with `nvim_set_hl()` while
---- redrawing.
+--- Set active namespace for highlights defined with `nvim_set_hl()` while redrawing.
---
--- This function meant to be called while redrawing, primarily from
---- `nvim_set_decoration_provider()` on_win and on_line callbacks, which are
---- allowed to change the namespace during a redraw cycle.
+--- `nvim_set_decoration_provider()` on_win and on_line callbacks, which
+--- are allowed to change the namespace during a redraw cycle.
---
--- @param ns_id integer the namespace to activate
function vim.api.nvim_set_hl_ns_fast(ns_id) end
@@ -2022,37 +2211,34 @@ function vim.api.nvim_set_hl_ns_fast(ns_id) end
---
--- To set a buffer-local mapping, use `nvim_buf_set_keymap()`.
---
---- Unlike `:map`, leading/trailing whitespace is accepted as part of the
---- {lhs} or {rhs}. Empty {rhs} is <Nop>. `keycodes` are replaced as usual.
+--- Unlike `:map`, leading/trailing whitespace is accepted as part of the {lhs} or {rhs}.
+--- Empty {rhs} is [<Nop>]. `keycodes` are replaced as usual.
---
--- Example:
---
--- ```vim
---- call nvim_set_keymap('n', ' <NL>', '', {'nowait': v:true})
+--- call nvim_set_keymap('n', ' <NL>', '', {'nowait': v:true})
--- ```
---
--- is equivalent to:
---
--- ```vim
---- nmap <nowait> <Space><NL> <Nop>
+--- nmap <nowait> <Space><NL> <Nop>
--- ```
---
----
--- @param mode string Mode short-name (map command prefix: "n", "i", "v", "x", …)
---- or "!" for `:map!`, or empty string for `:map`. "ia", "ca" or
---- "!a" for abbreviation in Insert mode, Cmdline mode, or both,
---- respectively
+--- or "!" for `:map!`, or empty string for `:map`.
+--- "ia", "ca" or "!a" for abbreviation in Insert mode, Cmdline mode, or both, respectively
--- @param lhs string Left-hand-side `{lhs}` of the mapping.
--- @param rhs string Right-hand-side `{rhs}` of the mapping.
---- @param opts vim.api.keyset.keymap Optional parameters map: Accepts all `:map-arguments` as keys
---- except <buffer>, values are booleans (default false). Also:
---- • "noremap" disables `recursive_mapping`, like `:noremap`
---- • "desc" human-readable description.
---- • "callback" Lua function called in place of {rhs}.
---- • "replace_keycodes" (boolean) When "expr" is true, replace
---- keycodes in the resulting string (see
---- `nvim_replace_termcodes()`). Returning nil from the Lua
---- "callback" is equivalent to returning an empty string.
+--- @param opts vim.api.keyset.keymap Optional parameters map: Accepts all `:map-arguments` as keys except [<buffer>],
+--- values are booleans (default false). Also:
+--- - "noremap" disables `recursive_mapping`, like `:noremap`
+--- - "desc" human-readable description.
+--- - "callback" Lua function called in place of {rhs}.
+--- - "replace_keycodes" (boolean) When "expr" is true, replace keycodes in the
+--- resulting string (see `nvim_replace_termcodes()`). Returning nil from the Lua
+--- "callback" is equivalent to returning an empty string.
function vim.api.nvim_set_keymap(mode, lhs, rhs, opts) end
--- @deprecated
@@ -2069,10 +2255,10 @@ function vim.api.nvim_set_option(name, value) end
--- @param name string Option name
--- @param value any New option value
--- @param opts vim.api.keyset.option Optional parameters
---- • scope: One of "global" or "local". Analogous to `:setglobal`
---- and `:setlocal`, respectively.
---- • win: `window-ID`. Used for setting window local option.
---- • buf: Buffer number. Used for setting buffer local option.
+--- - scope: One of "global" or "local". Analogous to
+--- `:setglobal` and `:setlocal`, respectively.
+--- - win: `window-ID`. Used for setting window local option.
+--- - buf: Buffer number. Used for setting buffer local option.
function vim.api.nvim_set_option_value(name, value, opts) end
--- Sets a global (g:) variable.
@@ -2087,11 +2273,11 @@ function vim.api.nvim_set_var(name, value) end
--- @param value any Variable value
function vim.api.nvim_set_vvar(name, value) end
---- Calculates the number of display cells occupied by `text`. Control
---- characters including <Tab> count as one cell.
+--- Calculates the number of display cells occupied by `text`.
+--- Control characters including [<Tab>] count as one cell.
---
--- @param text string Some text
---- @return integer
+--- @return integer # Number of cells
function vim.api.nvim_strwidth(text) end
--- Removes a tab-scoped (t:) variable
@@ -2103,32 +2289,32 @@ function vim.api.nvim_tabpage_del_var(tabpage, name) end
--- Gets the tabpage number
---
--- @param tabpage integer Tabpage handle, or 0 for current tabpage
---- @return integer
+--- @return integer # Tabpage number
function vim.api.nvim_tabpage_get_number(tabpage) end
--- Gets a tab-scoped (t:) variable
---
--- @param tabpage integer Tabpage handle, or 0 for current tabpage
--- @param name string Variable name
---- @return any
+--- @return any # Variable value
function vim.api.nvim_tabpage_get_var(tabpage, name) end
--- Gets the current window in a tabpage
---
--- @param tabpage integer Tabpage handle, or 0 for current tabpage
---- @return integer
+--- @return integer # Window handle
function vim.api.nvim_tabpage_get_win(tabpage) end
--- Checks if a tabpage is valid
---
--- @param tabpage integer Tabpage handle, or 0 for current tabpage
---- @return boolean
+--- @return boolean # true if the tabpage is valid, false otherwise
function vim.api.nvim_tabpage_is_valid(tabpage) end
--- Gets the windows in a tabpage
---
--- @param tabpage integer Tabpage handle, or 0 for current tabpage
---- @return integer[]
+--- @return integer[] # List of windows in `tabpage`
function vim.api.nvim_tabpage_list_wins(tabpage) end
--- Sets a tab-scoped (t:) variable
@@ -2146,18 +2332,21 @@ function vim.api.nvim_tabpage_set_win(tabpage, win) end
--- Calls a function with window as temporary current window.
---
+---
+--- @see `:help win_execute()`
+--- @see vim.api.nvim_buf_call
--- @param window integer Window handle, or 0 for current window
--- @param fun function Function to call inside the window (currently Lua callable
---- only)
---- @return any
+--- only)
+--- @return any # Return value of function.
function vim.api.nvim_win_call(window, fun) end
--- Closes the window (like `:close` with a `window-ID`).
---
--- @param window integer Window handle, or 0 for current window
--- @param force boolean Behave like `:close!` The last window of a buffer with
---- unwritten changes can be closed. The buffer will become
---- hidden, even if 'hidden' is not set.
+--- unwritten changes can be closed. The buffer will become
+--- hidden, even if 'hidden' is not set.
function vim.api.nvim_win_close(window, force) end
--- Removes a window-scoped (w:) variable
@@ -2169,7 +2358,7 @@ function vim.api.nvim_win_del_var(window, name) end
--- Gets the current buffer in a window
---
--- @param window integer Window handle, or 0 for current window
---- @return integer
+--- @return integer # Buffer handle
function vim.api.nvim_win_get_buf(window) end
--- Gets window configuration.
@@ -2179,27 +2368,29 @@ function vim.api.nvim_win_get_buf(window) end
--- `relative` is empty for normal windows.
---
--- @param window integer Window handle, or 0 for current window
---- @return vim.api.keyset.win_config
+--- @return vim.api.keyset.win_config # Map defining the window configuration, see |nvim_open_win()|
function vim.api.nvim_win_get_config(window) end
--- Gets the (1,0)-indexed, buffer-relative cursor position for a given window
--- (different windows showing the same buffer have independent cursor
--- positions). `api-indexing`
---
+---
+--- @see `:help getcurpos()`
--- @param window integer Window handle, or 0 for current window
---- @return integer[]
+--- @return integer[] # (row, col) tuple
function vim.api.nvim_win_get_cursor(window) end
--- Gets the window height
---
--- @param window integer Window handle, or 0 for current window
---- @return integer
+--- @return integer # Height as a count of rows
function vim.api.nvim_win_get_height(window) end
--- Gets the window number
---
--- @param window integer Window handle, or 0 for current window
---- @return integer
+--- @return integer # Window number
function vim.api.nvim_win_get_number(window) end
--- @deprecated
@@ -2211,34 +2402,34 @@ function vim.api.nvim_win_get_option(window, name) end
--- Gets the window position in display cells. First position is zero.
---
--- @param window integer Window handle, or 0 for current window
---- @return integer[]
+--- @return integer[] # (row, col) tuple with the window position
function vim.api.nvim_win_get_position(window) end
--- Gets the window tabpage
---
--- @param window integer Window handle, or 0 for current window
---- @return integer
+--- @return integer # Tabpage that contains the window
function vim.api.nvim_win_get_tabpage(window) end
--- Gets a window-scoped (w:) variable
---
--- @param window integer Window handle, or 0 for current window
--- @param name string Variable name
---- @return any
+--- @return any # Variable value
function vim.api.nvim_win_get_var(window, name) end
--- Gets the window width
---
--- @param window integer Window handle, or 0 for current window
---- @return integer
+--- @return integer # Width as a count of columns
function vim.api.nvim_win_get_width(window) end
--- Closes the window and hide the buffer it contains (like `:hide` with a
--- `window-ID`).
---
---- Like `:hide` the buffer becomes hidden unless another window is editing
---- it, or 'bufhidden' is `unload`, `delete` or `wipe` as opposed to `:close`
---- or `nvim_win_close()`, which will close the buffer.
+--- Like `:hide` the buffer becomes hidden unless another window is editing it,
+--- or 'bufhidden' is `unload`, `delete` or `wipe` as opposed to `:close` or
+--- `nvim_win_close()`, which will close the buffer.
---
--- @param window integer Window handle, or 0 for current window
function vim.api.nvim_win_hide(window) end
@@ -2246,7 +2437,7 @@ function vim.api.nvim_win_hide(window) end
--- Checks if a window is valid
---
--- @param window integer Window handle, or 0 for current window
---- @return boolean
+--- @return boolean # true if the window is valid, false otherwise
function vim.api.nvim_win_is_valid(window) end
--- Sets the current buffer in a window, without side effects
@@ -2261,12 +2452,15 @@ function vim.api.nvim_win_set_buf(window, buffer) end
--- When reconfiguring a window, absent option keys will not be changed.
--- `row`/`col` and `relative` must be reconfigured together.
---
+---
+--- @see vim.api.nvim_open_win
--- @param window integer Window handle, or 0 for current window
---- @param config vim.api.keyset.win_config Map defining the window configuration, see `nvim_open_win()`
+--- @param config vim.api.keyset.win_config Map defining the window configuration,
+--- see `nvim_open_win()`
function vim.api.nvim_win_set_config(window, config) end
---- Sets the (1,0)-indexed cursor position in the window. `api-indexing` This
---- scrolls the window even if it is not the current one.
+--- Sets the (1,0)-indexed cursor position in the window. `api-indexing`
+--- This scrolls the window even if it is not the current one.
---
--- @param window integer Window handle, or 0 for current window
--- @param pos integer[] (row, col) tuple representing the new position
@@ -2278,9 +2472,9 @@ function vim.api.nvim_win_set_cursor(window, pos) end
--- @param height integer Height as a count of rows
function vim.api.nvim_win_set_height(window, height) end
---- Set highlight namespace for a window. This will use highlights defined
---- with `nvim_set_hl()` for this namespace, but fall back to global
---- highlights (ns=0) when missing.
+--- Set highlight namespace for a window. This will use highlights defined with
+--- `nvim_set_hl()` for this namespace, but fall back to global highlights (ns=0) when
+--- missing.
---
--- This takes precedence over the 'winhighlight' option.
---
@@ -2308,28 +2502,32 @@ function vim.api.nvim_win_set_var(window, name, value) end
--- @param width integer Width as a count of columns
function vim.api.nvim_win_set_width(window, width) end
---- Computes the number of screen lines occupied by a range of text in a given
---- window. Works for off-screen text and takes folds into account.
+--- Computes the number of screen lines occupied by a range of text in a given window.
+--- Works for off-screen text and takes folds into account.
---
---- Diff filler or virtual lines above a line are counted as a part of that
---- line, unless the line is on "start_row" and "start_vcol" is specified.
+--- Diff filler or virtual lines above a line are counted as a part of that line,
+--- unless the line is on "start_row" and "start_vcol" is specified.
---
---- Diff filler or virtual lines below the last buffer line are counted in the
---- result when "end_row" is omitted.
+--- Diff filler or virtual lines below the last buffer line are counted in the result
+--- when "end_row" is omitted.
---
--- Line indexing is similar to `nvim_buf_get_text()`.
---
+--- @see `:help virtcol()` for text width.
--- @param window integer Window handle, or 0 for current window.
--- @param opts vim.api.keyset.win_text_height Optional parameters:
---- • start_row: Starting line index, 0-based inclusive. When
---- omitted start at the very top.
---- • end_row: Ending line index, 0-based inclusive. When omitted
---- end at the very bottom.
---- • start_vcol: Starting virtual column index on "start_row",
---- 0-based inclusive, rounded down to full screen lines. When
---- omitted include the whole line.
---- • end_vcol: Ending virtual column index on "end_row", 0-based
---- exclusive, rounded up to full screen lines. When omitted
---- include the whole line.
---- @return table<string,any>
+--- - start_row: Starting line index, 0-based inclusive.
+--- When omitted start at the very top.
+--- - end_row: Ending line index, 0-based inclusive.
+--- When omitted end at the very bottom.
+--- - start_vcol: Starting virtual column index on "start_row",
+--- 0-based inclusive, rounded down to full screen lines.
+--- When omitted include the whole line.
+--- - end_vcol: Ending virtual column index on "end_row",
+--- 0-based exclusive, rounded up to full screen lines.
+--- When omitted include the whole line.
+--- @return table<string,any> # Dict containing text height information, with these keys:
+--- - all: The total number of screen lines occupied by the range.
+--- - fill: The number of diff filler or virtual lines among them.
+---
function vim.api.nvim_win_text_height(window, opts) end
diff --git a/runtime/lua/vim/_meta/api_keysets.lua b/runtime/lua/vim/_meta/api_keysets.lua
index f7cd92a3b2..2fe5c32faf 100644
--- a/runtime/lua/vim/_meta/api_keysets.lua
+++ b/runtime/lua/vim/_meta/api_keysets.lua
@@ -197,6 +197,9 @@ error('Cannot require a meta file')
--- @field desc? string
--- @field replace_keycodes? boolean
+--- @class vim.api.keyset.ns_opts
+--- @field wins? any[]
+
--- @class vim.api.keyset.open_term
--- @field on_input? function
--- @field force_crlf? boolean
diff --git a/runtime/lua/vim/_meta/api_keysets_extra.lua b/runtime/lua/vim/_meta/api_keysets_extra.lua
index 76b56b04e7..81bce50746 100644
--- a/runtime/lua/vim/_meta/api_keysets_extra.lua
+++ b/runtime/lua/vim/_meta/api_keysets_extra.lua
@@ -23,16 +23,16 @@ 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? {[1]: string, [2]: string}[]
+--- @field virt_text? [string, string][]
--- @field virt_text_hide? boolean
--- @field virt_text_repeat_linebreak? boolean
--- @field virt_text_win_col? integer
--- @field virt_text_pos? string
---
---- @field virt_lines? {[1]: string, [2]: string}[][]
+--- @field virt_lines? [string, string][][]
--- @field virt_lines_above? boolean
--- @field virt_lines_leftcol? boolean
---
@@ -43,11 +43,17 @@ error('Cannot require a meta file')
--- @field line_hl_group? string
--- @field cursorline_hl_group? string
---- @class vim.api.keyset.get_extmark_item
+--- @class vim.api.keyset.get_extmark_item_by_id
--- @field [1] integer row
--- @field [2] integer col
--- @field [3] vim.api.keyset.extmark_details?
+--- @class vim.api.keyset.get_extmark_item
+--- @field [1] integer extmark_id
+--- @field [2] integer row
+--- @field [3] integer col
+--- @field [4] vim.api.keyset.extmark_details?
+
--- @class vim.api.keyset.get_mark
--- @field [1] integer row
--- @field [2] integer col
@@ -96,20 +102,29 @@ error('Cannot require a meta file')
--- @field strikethrough? true
--- @field altfont? true
--- @field nocombine? true
-
---- @class vim.api.keyset.hl_info.cterm : vim.api.keyset.hl_info.base
--- @field ctermfg? integer
--- @field ctermbg? integer
+
+--- @class vim.api.keyset.hl_info.cterm : vim.api.keyset.hl_info.base
--- @field foreground? integer
--- @field background? integer
---- @class vim.api.keyset.hl_info : vim.api.keyset.hl_info.base
+--- @class vim.api.keyset.get_hl_info : vim.api.keyset.hl_info.base
--- @field fg? integer
--- @field bg? integer
--- @field sp? integer
--- @field default? true
+--- @field blend? integer
+--- @field cterm? vim.api.keyset.hl_info.cterm
+
+--- @class vim.api.keyset.set_hl_info : vim.api.keyset.hl_info.base
+--- @field fg? integer|string
+--- @field bg? integer|string
+--- @field sp? integer|string
+--- @field default? true
--- @field link? string
--- @field blend? integer
+--- @field force? true
--- @field cterm? vim.api.keyset.hl_info.cterm
--- @class vim.api.keyset.get_mode
diff --git a/runtime/lua/vim/_meta/builtin.lua b/runtime/lua/vim/_meta/builtin.lua
index 75737bd040..13bd1c1294 100644
--- a/runtime/lua/vim/_meta/builtin.lua
+++ b/runtime/lua/vim/_meta/builtin.lua
@@ -121,6 +121,7 @@ function vim.stricmp(a, b) end
--- @param str string
--- @param index integer
--- @param use_utf16? boolean
+--- @return integer
function vim.str_byteindex(str, index, use_utf16) end
--- Gets a list of the starting byte positions of each UTF-8 codepoint in the given string.
@@ -181,8 +182,8 @@ function vim.str_utf_end(str, index) end
--- that sequence.
--- @param str string
--- @param index? integer
---- @return integer UTF-32 index
---- @return integer UTF-16 index
+--- @return integer # UTF-32 index
+--- @return integer # UTF-16 index
function vim.str_utfindex(str, index) end
--- The result is a String, which is the text {str} converted from
@@ -247,7 +248,7 @@ function vim.schedule(fn) end
--- - If {callback} errors, the error is raised.
function vim.wait(time, callback, interval, fast_only) end
---- Attach to ui events, similar to |nvim_ui_attach()| but receive events
+--- Attach to |ui-events|, similar to |nvim_ui_attach()| but receive events
--- as Lua callback. Can be used to implement screen elements like
--- popupmenu or message handling in Lua.
---
@@ -281,6 +282,8 @@ function vim.wait(time, callback, interval, fast_only) end
--- end)
--- ```
---
+--- @since 0
+---
--- @param ns integer
--- @param options table<string, any>
--- @param callback fun()
diff --git a/runtime/lua/vim/_meta/builtin_types.lua b/runtime/lua/vim/_meta/builtin_types.lua
index 9f0d2e7038..aca6649957 100644
--- a/runtime/lua/vim/_meta/builtin_types.lua
+++ b/runtime/lua/vim/_meta/builtin_types.lua
@@ -25,7 +25,7 @@
--- @field variables table<string,any>
--- @field windows integer[]
---- @alias vim.fn.getjumplist.ret {[1]: vim.fn.getjumplist.ret.item[], [2]: integer}
+--- @alias vim.fn.getjumplist.ret [vim.fn.getjumplist.ret.item[], integer]
--- @class vim.fn.getjumplist.ret.item
--- @field bufnr integer
@@ -34,6 +34,11 @@
--- @field filename? string
--- @field lnum integer
+--- @class vim.fn.getmarklist.ret.item
+--- @field mark string
+--- @field pos [integer, integer, integer, integer]
+--- @field file string
+
--- @class vim.fn.getmousepos.ret
--- @field screenrow integer
--- @field screencol integer
@@ -135,3 +140,69 @@
--- @field sid string
--- @field variables? table<string, any>
--- @field version 1
+
+--- @class vim.fn.undotree.entry
+---
+--- Undo sequence number. Same as what appears in
+--- \|:undolist|.
+--- @field seq integer
+---
+--- Timestamp when the change happened. Use
+--- \|strftime()| to convert to something readable.
+--- @field time integer
+---
+--- Only appears in the item that is the last one
+--- that was added. This marks the last change
+--- and where further changes will be added.
+--- @field newhead? integer
+---
+--- Only appears in the item that is the last one
+--- that was undone. This marks the current
+--- position in the undo tree, the block that will
+--- be used by a redo command. When nothing was
+--- undone after the last change this item will
+--- not appear anywhere.
+--- @field curhead? integer
+---
+--- Only appears on the last block before a file
+--- write. The number is the write count. The
+--- first write has number 1, the last one the
+--- "save_last" mentioned above.
+--- @field save integer
+---
+--- Alternate entry. This is again a List of undo
+--- blocks. Each item may again have an "alt"
+--- item.
+--- @field alt vim.fn.undotree.entry[]
+
+--- @class vim.fn.undotree.ret
+---
+--- The highest undo sequence number used.
+--- @field seq_last integer
+---
+--- The sequence number of the current position in
+--- the undo tree. This differs from "seq_last"
+--- when some changes were undone.
+--- @field seq_cur integer
+---
+--- Time last used for |:earlier| and related
+--- commands. Use |strftime()| to convert to
+--- something readable.
+--- @field time_cur integer
+---
+--- Number of the last file write. Zero when no
+--- write yet.
+--- @field save_last integer
+---
+--- Number of the current position in the undo
+--- tree.
+--- @field save_cur integer
+---
+--- Non-zero when the last undo block was synced.
+--- This happens when waiting from input from the
+--- user. See |undo-blocks|.
+--- @field synced integer
+---
+--- A list of dictionaries with information about
+--- undo blocks.
+--- @field entries vim.fn.undotree.entry[]
diff --git a/runtime/lua/vim/_meta/diff.lua b/runtime/lua/vim/_meta/diff.lua
index 617bc87f59..4803ed4775 100644
--- a/runtime/lua/vim/_meta/diff.lua
+++ b/runtime/lua/vim/_meta/diff.lua
@@ -11,19 +11,19 @@
--- - `count_a` (`integer`): Hunk size in {a}.
--- - `start_b` (`integer`): Start line of hunk in {b}.
--- - `count_b` (`integer`): Hunk size in {b}.
---- @field on_hunk fun(start_a: integer, count_a: integer, start_b: integer, count_b: integer): integer
+--- @field on_hunk? fun(start_a: integer, count_a: integer, start_b: integer, count_b: integer): integer?
---
--- Form of the returned diff:
--- - `unified`: String in unified format.
--- - `indices`: Array of hunk locations.
--- Note: This option is ignored if `on_hunk` is used.
--- (default: `'unified'`)
---- @field result_type 'unified'|'indices'
+--- @field result_type? 'unified'|'indices'
---
--- Run linematch on the resulting hunks from xdiff. When integer, only hunks
--- upto this size in lines are run through linematch.
--- Requires `result_type = indices`, ignored otherwise.
---- @field linematch boolean|integer
+--- @field linematch? boolean|integer
---
--- Diff algorithm to use. Values:
--- - `myers`: the default algorithm
@@ -31,15 +31,15 @@
--- - `patience`: patience diff algorithm
--- - `histogram`: histogram diff algorithm
--- (default: `'myers'`)
---- @field algorithm 'myers'|'minimal'|'patience'|'histogram'
---- @field ctxlen integer Context length
---- @field interhunkctxlen integer Inter hunk context length
---- @field ignore_whitespace boolean Ignore whitespace
---- @field ignore_whitespace_change boolean Ignore whitespace change
---- @field ignore_whitespace_change_at_eol boolean Ignore whitespace change at end-of-line.
---- @field ignore_cr_at_eol boolean Ignore carriage return at end-of-line
---- @field ignore_blank_lines boolean Ignore blank lines
---- @field indent_heuristic boolean Use the indent heuristic for the internal diff library.
+--- @field algorithm? 'myers'|'minimal'|'patience'|'histogram'
+--- @field ctxlen? integer Context length
+--- @field interhunkctxlen? integer Inter hunk context length
+--- @field ignore_whitespace? boolean Ignore whitespace
+--- @field ignore_whitespace_change? boolean Ignore whitespace change
+--- @field ignore_whitespace_change_at_eol? boolean Ignore whitespace change at end-of-line.
+--- @field ignore_cr_at_eol? boolean Ignore carriage return at end-of-line
+--- @field ignore_blank_lines? boolean Ignore blank lines
+--- @field indent_heuristic? boolean Use the indent heuristic for the internal diff library.
-- luacheck: no unused args
@@ -65,7 +65,7 @@
---
---@param a string First string to compare
---@param b string Second string to compare
----@param opts vim.diff.Opts
+---@param opts? vim.diff.Opts
---@return string|integer[][]?
--- See {opts.result_type}. `nil` if {opts.on_hunk} is given.
function vim.diff(a, b, opts) end
diff --git a/runtime/lua/vim/_meta/lpeg.lua b/runtime/lua/vim/_meta/lpeg.lua
index 73b3375c82..d354de95df 100644
--- a/runtime/lua/vim/_meta/lpeg.lua
+++ b/runtime/lua/vim/_meta/lpeg.lua
@@ -2,7 +2,7 @@
error('Cannot require a meta file')
-- These types were taken from https://github.com/LuaCATS/lpeg
--- (based on revision e6789e28e5b91a4a277a2a03081d708c403a3e34)
+-- (based on revision 33f4ff5343a64cf613a0634d70092fbc2b64291b)
-- with types being renamed to include the vim namespace and with some descriptions made less verbose.
--- @brief <pre>help
@@ -22,17 +22,18 @@ vim.lpeg = {}
--- @nodoc
--- @class vim.lpeg.Pattern
+--- @operator len: vim.lpeg.Pattern
--- @operator unm: vim.lpeg.Pattern
--- @operator add(vim.lpeg.Pattern): vim.lpeg.Pattern
--- @operator sub(vim.lpeg.Pattern): vim.lpeg.Pattern
--- @operator mul(vim.lpeg.Pattern): vim.lpeg.Pattern
--- @operator mul(vim.lpeg.Capture): vim.lpeg.Pattern
--- @operator div(string): vim.lpeg.Capture
---- @operator div(number): vim.lpeg.Capture
+--- @operator div(integer): vim.lpeg.Capture
--- @operator div(table): vim.lpeg.Capture
--- @operator div(function): vim.lpeg.Capture
---- @operator pow(number): vim.lpeg.Pattern
---- @operator mod(function): nil
+--- @operator pow(integer): vim.lpeg.Pattern
+--- @operator mod(function): vim.lpeg.Capture
local Pattern = {}
--- @alias vim.lpeg.Capture vim.lpeg.Pattern
@@ -55,11 +56,12 @@ local Pattern = {}
--- assert(pattern:match('1 hello') == nil)
--- ```
---
---- @param pattern vim.lpeg.Pattern
+--- @param pattern vim.lpeg.Pattern|string|integer|boolean|table|function
--- @param subject string
--- @param init? integer
---- @return integer|vim.lpeg.Capture|nil
-function vim.lpeg.match(pattern, subject, init) end
+--- @param ... any
+--- @return any ...
+function vim.lpeg.match(pattern, subject, init, ...) end
--- Matches the given `pattern` against the `subject` string. If the match succeeds, returns the
--- index in the subject of the first character after the match, or the captured values (if the
@@ -81,8 +83,9 @@ function vim.lpeg.match(pattern, subject, init) end
---
--- @param subject string
--- @param init? integer
---- @return integer|vim.lpeg.Capture|nil
-function Pattern:match(subject, init) end
+--- @param ... any
+--- @return any ...
+function Pattern:match(subject, init, ...) end
--- Returns the string `"pattern"` if the given value is a pattern, otherwise `nil`.
---
@@ -123,7 +126,7 @@ function vim.lpeg.P(value) end
--- Pattern `patt` must match only strings with some fixed length, and it cannot contain captures.
--- Like the `and` predicate, this pattern never consumes any input, independently of success or failure.
---
---- @param pattern vim.lpeg.Pattern
+--- @param pattern vim.lpeg.Pattern|string|integer|boolean|table
--- @return vim.lpeg.Pattern
function vim.lpeg.B(pattern) end
@@ -163,7 +166,7 @@ function vim.lpeg.S(string) end
--- assert(b:match('(') == nil)
--- ```
---
---- @param v string|integer
+--- @param v boolean|string|number|function|table|thread|userdata|lightuserdata
--- @return vim.lpeg.Pattern
function vim.lpeg.V(v) end
@@ -227,7 +230,7 @@ function vim.lpeg.locale(tab) end
--- assert(c == 'c')
--- ```
---
---- @param patt vim.lpeg.Pattern
+--- @param patt vim.lpeg.Pattern|string|integer|boolean|table|function
--- @return vim.lpeg.Capture
function vim.lpeg.C(patt) end
@@ -258,7 +261,7 @@ function vim.lpeg.Cc(...) end
--- `func(...func(func(C1, C2), C3)...,Cn)`, that is, it will fold (or accumulate, or reduce) the captures from
--- `patt` using function `func`. This capture assumes that `patt` should produce at least one capture with at
--- least one value (of any type), which becomes the initial value of an accumulator. (If you need a specific
---- initial value, you may prefix a constant captureto `patt`.) For each subsequent capture, LPeg calls `func`
+--- initial value, you may prefix a constant capture to `patt`.) For each subsequent capture, LPeg calls `func`
--- with this accumulator as the first argument and all values produced by the capture as extra arguments;
--- the first result from this call becomes the new value for the accumulator. The final value of the accumulator
--- becomes the captured value.
@@ -273,7 +276,7 @@ function vim.lpeg.Cc(...) end
--- assert(sum:match('10,30,43') == 83)
--- ```
---
---- @param patt vim.lpeg.Pattern
+--- @param patt vim.lpeg.Pattern|string|integer|boolean|table|function
--- @param func fun(acc, newvalue)
--- @return vim.lpeg.Capture
function vim.lpeg.Cf(patt, func) end
@@ -282,7 +285,7 @@ function vim.lpeg.Cf(patt, func) end
--- The group may be anonymous (if no name is given) or named with the given name (which
--- can be any non-nil Lua value).
---
---- @param patt vim.lpeg.Pattern
+--- @param patt vim.lpeg.Pattern|string|integer|boolean|table|function
--- @param name? string
--- @return vim.lpeg.Capture
function vim.lpeg.Cg(patt, name) end
@@ -320,7 +323,7 @@ function vim.lpeg.Cp() end
--- assert(gsub('Hello, xxx!', 'xxx', 'World') == 'Hello, World!')
--- ```
---
---- @param patt vim.lpeg.Pattern
+--- @param patt vim.lpeg.Pattern|string|integer|boolean|table|function
--- @return vim.lpeg.Capture
function vim.lpeg.Cs(patt) end
@@ -329,7 +332,7 @@ function vim.lpeg.Cs(patt) end
--- Moreover, for each named capture group created by `patt`, the first value of the group is put into
--- the table with the group name as its key. The captured value is only the table.
---
---- @param patt vim.lpeg.Pattern|''
+--- @param patt vim.lpeg.Pattern|string|integer|boolean|table|function
--- @return vim.lpeg.Capture
function vim.lpeg.Ct(patt) end
@@ -343,7 +346,7 @@ function vim.lpeg.Ct(patt) end
--- (so, to return true is equivalent to return `i`). If the call returns `false`, `nil`, or no value, the match fails.
--- Any extra values returned by the function become the values produced by the capture.
---
---- @param patt vim.lpeg.Pattern
---- @param fn function
+--- @param patt vim.lpeg.Pattern|string|integer|boolean|table|function
+--- @param fn fun(s: string, i: integer, ...: any): (position: boolean|integer, ...: any)
--- @return vim.lpeg.Capture
function vim.lpeg.Cmt(patt, fn) end
diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua
index 428b7c4d4f..ce3ff4f861 100644
--- a/runtime/lua/vim/_meta/options.lua
+++ b/runtime/lua/vim/_meta/options.lua
@@ -544,7 +544,7 @@ vim.wo.bri = vim.wo.breakindent
--- applying 'breakindent', even if the resulting
--- text should normally be narrower. This prevents
--- text indented almost to the right window border
---- occupying lot of vertical space when broken.
+--- occupying lots of vertical space when broken.
--- (default: 20)
--- shift:{n} After applying 'breakindent', the wrapped line's
--- beginning will be shifted by the given number of
@@ -558,9 +558,9 @@ vim.wo.bri = vim.wo.breakindent
--- list:{n} Adds an additional indent for lines that match a
--- numbered or bulleted list (using the
--- 'formatlistpat' setting).
---- list:-1 Uses the length of a match with 'formatlistpat'
---- for indentation.
--- (default: 0)
+--- list:-1 Uses the width of a match with 'formatlistpat' for
+--- indentation.
--- column:{n} Indent at column {n}. Will overrule the other
--- sub-options. Note: an additional indent may be
--- added for the 'showbreak' setting.
@@ -731,11 +731,12 @@ vim.go.cd = vim.go.cdpath
--- The key used in Command-line Mode to open the command-line window.
--- Only non-printable keys are allowed.
--- The key can be specified as a single character, but it is difficult to
---- type. The preferred way is to use the <> notation. Examples:
+--- type. The preferred way is to use `key-notation` (e.g. <Up>, <C-F>) or
+--- a letter preceded with a caret (e.g. `^F` is CTRL-F). Examples:
---
--- ```vim
---- exe "set cedit=\\<C-Y>"
---- exe "set cedit=\\<Esc>"
+--- set cedit=^Y
+--- set cedit=<Esc>
--- ```
--- `Nvi` also has this option, but it only uses the first character.
--- See `cmdwin`.
@@ -785,6 +786,20 @@ vim.bo.channel = vim.o.channel
--- v:fname_in name of the input file
--- v:fname_out name of the output file
--- Note that v:fname_in and v:fname_out will never be the same.
+---
+--- The advantage of using a function call without arguments is that it is
+--- faster, see `expr-option-function`.
+---
+--- If the 'charconvert' expression starts with s: or `<SID>`, then it is
+--- replaced with the script ID (`local-function`). Example:
+---
+--- ```vim
+--- set charconvert=s:MyConvert()
+--- set charconvert=<SID>SomeConvert()
+--- ```
+--- Otherwise the expression is evaluated in the context of the script
+--- where the option was set, thus script-local items are available.
+---
--- This option cannot be set from a `modeline` or in the `sandbox`, for
--- security reasons.
---
@@ -899,11 +914,10 @@ vim.go.cb = vim.go.clipboard
--- used. The command-line will cover the last line of the screen when
--- shown.
---
---- WARNING: `cmdheight=0` is considered experimental. Expect some
---- unwanted behaviour. Some 'shortmess' flags and similar
---- mechanism might fail to take effect, causing unwanted hit-enter
---- prompts. Some informative messages, both from Nvim itself and
---- plugins, will not be displayed.
+--- WARNING: `cmdheight=0` is EXPERIMENTAL. Expect some unwanted behaviour.
+--- Some 'shortmess' flags and similar mechanism might fail to take effect,
+--- causing unwanted hit-enter prompts. Some informative messages, both
+--- from Nvim itself and plugins, will not be displayed.
---
--- @type integer
vim.o.cmdheight = 1
@@ -974,8 +988,8 @@ vim.bo.comments = vim.o.comments
vim.bo.com = vim.bo.comments
--- A template for a comment. The "%s" in the value is replaced with the
---- comment text. For example, C uses "/*%s*/". Used for `commenting` and to
---- add markers for folding, see `fold-marker`.
+--- comment text, and should be padded with a space when possible.
+--- Used for `commenting` and to add markers for folding, see `fold-marker`.
---
--- @type string
vim.o.commentstring = ""
@@ -1040,6 +1054,19 @@ vim.o.cfu = vim.o.completefunc
vim.bo.completefunc = vim.o.completefunc
vim.bo.cfu = vim.bo.completefunc
+--- A comma-separated list of `complete-items` that controls the alignment
+--- and display order of items in the popup menu during Insert mode
+--- completion. The supported values are abbr, kind, and menu. These
+--- options allow to customize how the completion items are shown in the
+--- popup menu. Note: must always contain those three values in any
+--- order.
+---
+--- @type string
+vim.o.completeitemalign = "abbr,kind,menu"
+vim.o.cia = vim.o.completeitemalign
+vim.go.completeitemalign = vim.o.completeitemalign
+vim.go.cia = vim.go.completeitemalign
+
--- A comma-separated list of options for Insert mode completion
--- `ins-completion`. The supported values are:
---
@@ -1061,6 +1088,10 @@ vim.bo.cfu = vim.bo.completefunc
--- completion in the preview window. Only works in
--- combination with "menu" or "menuone".
---
+--- popup Show extra information about the currently selected
+--- completion in a popup window. Only works in combination
+--- with "menu" or "menuone". Overrides "preview".
+---
--- noinsert Do not insert any text for a match until the user selects
--- a match from the menu. Only works in combination with
--- "menu" or "menuone". No effect if "longest" is present.
@@ -1069,13 +1100,19 @@ vim.bo.cfu = vim.bo.completefunc
--- select one from the menu. Only works in combination with
--- "menu" or "menuone".
---
---- popup Show extra information about the currently selected
---- completion in a popup window. Only works in combination
---- with "menu" or "menuone". Overrides "preview".
+--- fuzzy Enable `fuzzy-matching` for completion candidates. This
+--- allows for more flexible and intuitive matching, where
+--- characters can be skipped and matches can be found even
+--- if the exact sequence is not typed. Only makes a
+--- difference how completion candidates are reduced from the
+--- list of alternatives, but not how the candidates are
+--- collected (using different completion types).
---
--- @type string
vim.o.completeopt = "menu,preview"
vim.o.cot = vim.o.completeopt
+vim.bo.completeopt = vim.o.completeopt
+vim.bo.cot = vim.bo.completeopt
vim.go.completeopt = vim.o.completeopt
vim.go.cot = vim.go.completeopt
@@ -1805,9 +1842,12 @@ vim.go.ead = vim.go.eadirection
--- When on all Unicode emoji characters are considered to be full width.
--- This excludes "text emoji" characters, which are normally displayed as
---- single width. Unfortunately there is no good specification for this
---- and it has been determined on trial-and-error basis. Use the
---- `setcellwidths()` function to change the behavior.
+--- single width. However, such "text emoji" are treated as full-width
+--- emoji if they are followed by the U+FE0F variant selector.
+---
+--- Unfortunately there is no good specification for this and it has been
+--- determined on trial-and-error basis. Use the `setcellwidths()`
+--- function to change the behavior.
---
--- @type boolean
vim.o.emoji = true
@@ -2177,7 +2217,7 @@ vim.go.fic = vim.go.fileignorecase
--- ```
--- `FileType` `filetypes`
--- When a dot appears in the value then this separates two filetype
---- names. Example: >c
+--- names, it should therefore not be used for a filetype. Example: >c
--- /* vim: set filetype=c.doxygen : */
--- ```
--- This will use the "c" filetype first, then the "doxygen" filetype.
@@ -2185,7 +2225,7 @@ vim.go.fic = vim.go.fileignorecase
--- one dot may appear.
--- This option is not copied to another buffer, independent of the 's' or
--- 'S' flag in 'cpoptions'.
---- Only normal file name characters can be used, `/\*?[|<>` are illegal.
+--- Only alphanumeric characters, '-' and '_' can be used.
---
--- @type string
vim.o.filetype = ""
@@ -2500,6 +2540,9 @@ vim.wo.fdt = vim.wo.foldtext
--- This will invoke the mylang#Format() function in the
--- autoload/mylang.vim file in 'runtimepath'. `autoload`
---
+--- The advantage of using a function call without arguments is that it is
+--- faster, see `expr-option-function`.
+---
--- The expression is also evaluated when 'textwidth' is set and adding
--- text beyond that limit. This happens under the same conditions as
--- when internal formatting is used. Make sure the cursor is kept in the
@@ -3265,12 +3308,15 @@ vim.go.inc = vim.go.include
--- the script ID (`local-function`). Example:
---
--- ```vim
---- setlocal includeexpr=s:MyIncludeExpr(v:fname)
---- setlocal includeexpr=<SID>SomeIncludeExpr(v:fname)
+--- setlocal includeexpr=s:MyIncludeExpr()
+--- setlocal includeexpr=<SID>SomeIncludeExpr()
--- ```
--- Otherwise, the expression is evaluated in the context of the script
--- where the option was set, thus script-local items are available.
---
+--- It is more efficient if the value is just a function call without
+--- arguments, see `expr-option-function`.
+---
--- The expression will be evaluated in the `sandbox` when set from a
--- modeline, see `sandbox-option`.
--- This option cannot be set in a modeline when 'modelineexpr' is off.
@@ -3330,7 +3376,7 @@ vim.go.is = vim.go.incsearch
--- in Insert mode as specified with the 'indentkeys' option.
--- When this option is not empty, it overrules the 'cindent' and
--- 'smartindent' indenting. When 'lisp' is set, this option is
---- is only used when 'lispoptions' contains "expr:1".
+--- only used when 'lispoptions' contains "expr:1".
--- The expression is evaluated with `v:lnum` set to the line number for
--- which the indent is to be computed. The cursor is also in this line
--- when the expression is evaluated (but it may be moved around).
@@ -3345,6 +3391,9 @@ vim.go.is = vim.go.incsearch
--- Otherwise, the expression is evaluated in the context of the script
--- where the option was set, thus script-local items are available.
---
+--- The advantage of using a function call without arguments is that it is
+--- faster, see `expr-option-function`.
+---
--- The expression must return the number of spaces worth of indent. It
--- can return "-1" to keep the current indent (this means 'autoindent' is
--- used for the indent).
@@ -3541,8 +3590,11 @@ vim.go.js = vim.go.joinspaces
--- |alternate-file` or using `mark-motions` try to
--- restore the `mark-view` in which the action occurred.
---
+--- clean Remove unloaded buffers from the jumplist.
+--- EXPERIMENTAL: this flag may change in the future.
+---
--- @type string
-vim.o.jumpoptions = ""
+vim.o.jumpoptions = "clean"
vim.o.jop = vim.o.jumpoptions
vim.go.jumpoptions = vim.o.jumpoptions
vim.go.jop = vim.go.jumpoptions
@@ -3551,7 +3603,7 @@ vim.go.jop = vim.go.jumpoptions
--- Setting this option to a valid keymap name has the side effect of
--- setting 'iminsert' to one, so that the keymap becomes effective.
--- 'imsearch' is also set to one, unless it was -1
---- Only normal file name characters can be used, `/\*?[|<>` are illegal.
+--- Only alphanumeric characters, '.', '-' and '_' can be used.
---
--- @type string
vim.o.keymap = ""
@@ -3628,7 +3680,7 @@ vim.go.kp = vim.go.keywordprg
--- part can be in one of two forms:
--- 1. A list of pairs. Each pair is a "from" character immediately
--- followed by the "to" character. Examples: "aA", "aAbBcC".
---- 2. A list of "from" characters, a semi-colon and a list of "to"
+--- 2. A list of "from" characters, a semicolon and a list of "to"
--- characters. Example: "abc;ABC"
--- Example: "aA,fgh;FGH,cCdDeE"
--- Special characters need to be preceded with a backslash. These are
@@ -3720,7 +3772,7 @@ vim.go.ls = vim.go.laststatus
--- update use `:redraw`.
--- This may occasionally cause display errors. It is only meant to be set
--- temporarily when performing an operation where redrawing may cause
---- flickering or cause a slow down.
+--- flickering or cause a slowdown.
---
--- @type boolean
vim.o.lazyredraw = false
@@ -3820,6 +3872,9 @@ vim.go.lw = vim.go.lispwords
--- between tabs and spaces and for trailing blanks. Further changed by
--- the 'listchars' option.
---
+--- When 'listchars' does not contain "tab" field, tabs are shown as "^I"
+--- or "<09>", like how unprintable characters are displayed.
+---
--- The cursor is displayed at the start of the space a Tab character
--- occupies, not at the end as usual in Normal mode. To get this cursor
--- position while displaying Tabs with spaces, use:
@@ -4533,6 +4588,20 @@ vim.go.mouset = vim.go.mousetime
--- (without "unsigned" it would become "9-2019").
--- Using CTRL-X on "0" or CTRL-A on "18446744073709551615"
--- (2^64 - 1) has no effect, overflow is prevented.
+--- blank If included, treat numbers as signed or unsigned based on
+--- preceding whitespace. If a number with a leading dash has its
+--- dash immediately preceded by a non-whitespace character (i.e.,
+--- not a tab or a " "), the negative sign won't be considered as
+--- part of the number. For example:
+--- Using CTRL-A on "14" in "Carbon-14" results in "Carbon-15"
+--- (without "blank" it would become "Carbon-13").
+--- Using CTRL-X on "8" in "Carbon -8" results in "Carbon -9"
+--- (because -8 is preceded by whitespace. If "unsigned" was
+--- set, it would result in "Carbon -7").
+--- If this format is included, overflow is prevented as if
+--- "unsigned" were set. If both this format and "unsigned" are
+--- included, "unsigned" will take precedence.
+---
--- Numbers which simply begin with a digit in the range 1-9 are always
--- considered decimal. This also happens for numbers that are not
--- recognized as octal or hex.
@@ -4757,7 +4826,7 @@ vim.go.pm = vim.go.patchmode
--- ```
--- To use an environment variable, you probably need to replace the
--- separator. Here is an example to append $INCL, in which directory
---- names are separated with a semi-colon:
+--- names are separated with a semicolon:
---
--- ```vim
--- let &path = &path .. "," .. substitute($INCL, ';', ',', 'g')
@@ -5200,6 +5269,9 @@ vim.wo.scr = vim.wo.scroll
--- Minimum is 1, maximum is 100000.
--- Only in `terminal` buffers.
---
+--- Note: Lines that are not visible and kept in scrollback are not
+--- reflown when the terminal buffer is resized horizontally.
+---
--- @type integer
vim.o.scrollback = -1
vim.o.scbk = vim.o.scrollback
@@ -5764,7 +5836,7 @@ vim.bo.shiftwidth = vim.o.shiftwidth
vim.bo.sw = vim.bo.shiftwidth
--- This option helps to avoid all the `hit-enter` prompts caused by file
---- messages, for example with CTRL-G, and to avoid some other messages.
+--- messages, for example with CTRL-G, and to avoid some other messages.
--- It is a list of flags:
--- flag meaning when present ~
--- l use "999L, 888B" instead of "999 lines, 888 bytes" *shm-l*
@@ -5781,8 +5853,8 @@ vim.bo.sw = vim.bo.shiftwidth
--- message; also for quickfix message (e.g., ":cn")
--- s don't give "search hit BOTTOM, continuing at TOP" or *shm-s*
--- "search hit TOP, continuing at BOTTOM" messages; when using
---- the search count do not show "W" after the count message (see
---- S below)
+--- the search count do not show "W" before the count message
+--- (see `shm-S` below)
--- t truncate file message at the start if it is too long *shm-t*
--- to fit on the command-line, "<" will appear in the left most
--- column; ignored in Ex mode
@@ -5804,7 +5876,11 @@ vim.bo.sw = vim.bo.shiftwidth
--- `:silent` was used for the command; note that this also
--- affects messages from 'autoread' reloading
--- S do not show search count message when searching, e.g. *shm-S*
---- "[1/5]"
+--- "[1/5]". When the "S" flag is not present (e.g. search count
+--- is shown), the "search hit BOTTOM, continuing at TOP" and
+--- "search hit TOP, continuing at BOTTOM" messages are only
+--- indicated by a "W" (Mnemonic: Wrapped) letter before the
+--- search count statistics.
---
--- This gives you the opportunity to avoid that a change between buffers
--- requires you to hit <Enter>, but still gives as useful a message as
@@ -6249,7 +6325,7 @@ vim.bo.spo = vim.bo.spelloptions
--- minus two.
---
--- timeout:{millisec} Limit the time searching for suggestions to
---- {millisec} milli seconds. Applies to the following
+--- {millisec} milliseconds. Applies to the following
--- methods. When omitted the limit is 5000. When
--- negative there is no limit.
---
@@ -6269,9 +6345,11 @@ vim.bo.spo = vim.bo.spelloptions
--- The file is used for all languages.
---
--- expr:{expr} Evaluate expression {expr}. Use a function to avoid
---- trouble with spaces. `v:val` holds the badly spelled
---- word. The expression must evaluate to a List of
---- Lists, each with a suggestion and a score.
+--- trouble with spaces. Best is to call a function
+--- without arguments, see `expr-option-function|.
+--- |v:val` holds the badly spelled word. The expression
+--- must evaluate to a List of Lists, each with a
+--- suggestion and a score.
--- Example:
--- [['the', 33], ['that', 44]] ~
--- Set 'verbose' and use `z=` to see the scores that the
@@ -6338,7 +6416,8 @@ vim.go.spr = vim.go.splitright
--- non-blank of the line. When off the cursor is kept in the same column
--- (if possible). This applies to the commands:
--- - CTRL-D, CTRL-U, CTRL-B, CTRL-F, "G", "H", "M", "L", "gg"
---- - "d", "<<" and ">>" with a linewise operator
+--- - "d", "<<", "==" and ">>" with a linewise operator
+--- (`operator-resulting-pos`)
--- - "%" with a count
--- - buffer changing commands (CTRL-^, :bnext, :bNext, etc.)
--- - Ex commands that only have a line number, e.g., ":25" or ":+".
@@ -6351,7 +6430,6 @@ vim.o.sol = vim.o.startofline
vim.go.startofline = vim.o.startofline
vim.go.sol = vim.go.startofline
---- EXPERIMENTAL
--- When non-empty, this option determines the content of the area to the
--- side of a window, normally containing the fold, sign and number columns.
--- The format of this option is like that of 'statusline'.
@@ -6359,8 +6437,7 @@ vim.go.sol = vim.go.startofline
--- Some of the items from the 'statusline' format are different for
--- 'statuscolumn':
---
---- %l line number of currently drawn line
---- %r relative line number of currently drawn line
+--- %l line number column for currently drawn line
--- %s sign column for currently drawn line
--- %C fold column for currently drawn line
---
@@ -6389,11 +6466,8 @@ vim.go.sol = vim.go.startofline
--- Examples:
---
--- ```vim
---- " Relative number with bar separator and click handlers:
---- set statuscolumn=%@SignCb@%s%=%T%@NumCb@%r│%T
----
---- " Right aligned relative cursor line number:
---- let &stc='%=%{v:relnum?v:relnum:v:lnum} '
+--- " Line number with bar separator and click handlers:
+--- set statuscolumn=%@SignCb@%s%=%T%@NumCb@%l│%T
---
--- " Line numbers in hexadecimal for non wrapped part of lines:
--- let &stc='%=%{v:virtnum>0?"":printf("%x",v:lnum)} '
@@ -6799,7 +6873,7 @@ vim.bo.smc = vim.bo.synmaxcol
--- Syntax autocommand event is triggered with the value as argument.
--- This option is not copied to another buffer, independent of the 's' or
--- 'S' flag in 'cpoptions'.
---- Only normal file name characters can be used, `/\*?[|<>` are illegal.
+--- Only alphanumeric characters, '.', '-' and '_' can be used.
---
--- @type string
vim.o.syntax = ""
@@ -6807,6 +6881,22 @@ vim.o.syn = vim.o.syntax
vim.bo.syntax = vim.o.syntax
vim.bo.syn = vim.bo.syntax
+--- This option controls the behavior when closing tab pages (e.g., using
+--- `:tabclose`). When empty Vim goes to the next (right) tab page.
+---
+--- Possible values (comma-separated list):
+--- left If included, go to the previous tab page instead of
+--- the next one.
+--- uselast If included, go to the previously used tab page if
+--- possible. This option takes precedence over the
+--- others.
+---
+--- @type string
+vim.o.tabclose = ""
+vim.o.tcl = vim.o.tabclose
+vim.go.tabclose = vim.o.tabclose
+vim.go.tcl = vim.go.tabclose
+
--- When non-empty, this option determines the content of the tab pages
--- line at the top of the Vim window. When empty Vim will use a default
--- tab pages line. See `setting-tabline` for more info.
@@ -7178,7 +7268,7 @@ vim.go.tm = vim.go.timeoutlen
--- When on, the title of the window will be set to the value of
--- 'titlestring' (if it is not empty), or to:
---- filename [+=-] (path) - NVIM
+--- filename [+=-] (path) - Nvim
--- Where:
--- filename the name of the file being edited
--- - indicates the file cannot be modified, 'ma' off
@@ -7186,7 +7276,7 @@ vim.go.tm = vim.go.timeoutlen
--- = indicates the file is read-only
--- =+ indicates the file is read-only and modified
--- (path) is the path of the file being edited
---- - NVIM the server name `v:servername` or "NVIM"
+--- - Nvim the server name `v:servername` or "Nvim"
---
--- @type boolean
vim.o.title = false
@@ -7607,9 +7697,14 @@ vim.go.ww = vim.go.whichwrap
--- Some keys will not work, such as CTRL-C, <CR> and Enter.
--- <Esc> can be used, but hitting it twice in a row will still exit
--- command-line as a failsafe measure.
---- Although 'wc' is a number option, you can set it to a special key:
+--- Although 'wc' is a number option, it can be specified as a number, a
+--- single character, a `key-notation` (e.g. <Up>, <C-F>) or a letter
+--- preceded with a caret (e.g. `^F` is CTRL-F):
---
--- ```vim
+--- :set wc=27
+--- :set wc=X
+--- :set wc=^I
--- set wc=<Tab>
--- ```
---
diff --git a/runtime/lua/vim/_meta/regex.lua b/runtime/lua/vim/_meta/regex.lua
index 595ad96319..9c9cd7d29b 100644
--- a/runtime/lua/vim/_meta/regex.lua
+++ b/runtime/lua/vim/_meta/regex.lua
@@ -5,9 +5,9 @@
--- @brief Vim regexes can be used directly from Lua. Currently they only allow
--- matching within a single line.
---- Parse the Vim regex {re} and return a regex object. Regexes are "magic"
---- and case-sensitive by default, regardless of 'magic' and 'ignorecase'.
---- They can be controlled with flags, see |/magic| and |/ignorecase|.
+--- Parses the Vim regex `re` and returns a regex object. Regexes are "magic" and case-sensitive by
+--- default, regardless of 'magic' and 'ignorecase'. They can be controlled with flags, see |/magic|
+--- and |/ignorecase|.
--- @param re string
--- @return vim.regex
function vim.regex(re) end
@@ -16,20 +16,22 @@ function vim.regex(re) end
--- @class vim.regex
local regex = {} -- luacheck: no unused
---- Match the string against the regex. If the string should match the regex
---- precisely, surround the regex with `^` and `$`. If there was a match, the
---- byte indices for the beginning and end of the match are returned. When
---- there is no match, `nil` is returned. Because any integer is "truthy",
---- `regex:match_str()` can be directly used as a condition in an if-statement.
+--- Matches string `str` against this regex. To match the string precisely, surround the regex with
+--- "^" and "$". Returns the byte indices for the start and end of the match, or `nil` if there is
+--- no match. Because any integer is "truthy", `regex:match_str()` can be directly used as
+--- a condition in an if-statement.
--- @param str string
+--- @return integer? # match start (byte index), or `nil` if no match
+--- @return integer? # match end (byte index), or `nil` if no match
function regex:match_str(str) end
---- Match line {line_idx} (zero-based) in buffer {bufnr}. If {start} and {end}
---- are supplied, match only this byte index range. Otherwise see
---- |regex:match_str()|. If {start} is used, then the returned byte indices
---- will be relative {start}.
+--- Matches line at `line_idx` (zero-based) in buffer `bufnr`. Match is restricted to byte index
+--- range `start` and `end_` if given, otherwise see |regex:match_str()|. Returned byte indices are
+--- relative to `start` if given.
--- @param bufnr integer
--- @param line_idx integer
--- @param start? integer
--- @param end_? integer
+--- @return integer? # match start (byte index) relative to `start`, or `nil` if no match
+--- @return integer? # match end (byte index) relative to `start`, or `nil` if no match
function regex:match_line(bufnr, line_idx, start, end_) end
diff --git a/runtime/lua/vim/_meta/spell.lua b/runtime/lua/vim/_meta/spell.lua
index c636db3b53..b4e3bf6ca4 100644
--- a/runtime/lua/vim/_meta/spell.lua
+++ b/runtime/lua/vim/_meta/spell.lua
@@ -20,7 +20,7 @@
--- ```
---
--- @param str string
---- @return {[1]: string, [2]: 'bad'|'rare'|'local'|'caps', [3]: integer}[]
+--- @return [string, 'bad'|'rare'|'local'|'caps', integer][]
--- List of tuples with three items:
--- - The badly spelled word.
--- - The type of the spelling error:
diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua
index f4daacfb7d..3f6deba092 100644
--- a/runtime/lua/vim/_meta/vimfn.lua
+++ b/runtime/lua/vim/_meta/vimfn.lua
@@ -15,7 +15,7 @@ error('Cannot require a meta file')
--- echo abs(-4)
--- < 4
---
---- @param expr any
+--- @param expr number
--- @return number
function vim.fn.abs(expr) end
@@ -31,7 +31,7 @@ function vim.fn.abs(expr) end
--- echo acos(-0.5)
--- < 2.094395
---
---- @param expr any
+--- @param expr number
--- @return number
function vim.fn.acos(expr) end
@@ -47,7 +47,7 @@ function vim.fn.acos(expr) end
---
--- @param object any
--- @param expr any
---- @return any
+--- @return any # Resulting |List| or |Blob|, or 1 if {object} is not a |List| or a |Blob|.
function vim.fn.add(object, expr) end
--- Bitwise AND on the two arguments. The arguments are converted
@@ -57,8 +57,8 @@ function vim.fn.add(object, expr) end
--- let flag = and(bits, 0x80)
--- <
---
---- @param expr any
---- @param expr1 any
+--- @param expr number
+--- @param expr1 number
--- @return integer
vim.fn['and'] = function(expr, expr1) end
@@ -86,7 +86,7 @@ function vim.fn.api_info() end
--- <
---
--- @param lnum integer
---- @param text any
+--- @param text string|string[]
--- @return 0|1
function vim.fn.append(lnum, text) end
@@ -110,7 +110,7 @@ function vim.fn.append(lnum, text) end
--- <However, when {text} is an empty list then no error is given
--- for an invalid {lnum}, since {lnum} isn't actually used.
---
---- @param buf any
+--- @param buf integer|string
--- @param lnum integer
--- @param text string
--- @return 0|1
@@ -195,7 +195,7 @@ function vim.fn.asin(expr) end
--- Also see |assert_fails()|, |assert_nobeep()| and
--- |assert-return|.
---
---- @param cmd any
+--- @param cmd string
--- @return 0|1
function vim.fn.assert_beeps(cmd) end
@@ -203,16 +203,17 @@ function vim.fn.assert_beeps(cmd) end
--- added to |v:errors| and 1 is returned. Otherwise zero is
--- returned. |assert-return|
--- The error is in the form "Expected {expected} but got
---- {actual}". When {msg} is present it is prefixed to that.
+--- {actual}". When {msg} is present it is prefixed to that,
+--- along with the location of the assert when run from a script.
---
--- There is no automatic conversion, the String "4" is different
--- from the Number 4. And the number 4 is different from the
--- Float 4.0. The value of 'ignorecase' is not used here, case
--- always matters.
--- Example: >vim
---- assert_equal('foo', 'bar')
---- <Will result in a string to be added to |v:errors|:
---- test.vim line 12: Expected 'foo' but got 'bar' ~
+--- call assert_equal('foo', 'bar', 'baz')
+--- <Will add the following to |v:errors|:
+--- test.vim line 12: baz: Expected 'foo' but got 'bar' ~
---
--- @param expected any
--- @param actual any
@@ -226,8 +227,10 @@ function vim.fn.assert_equal(expected, actual, msg) end
--- When {fname-one} or {fname-two} does not exist the error will
--- mention that.
---
+--- @param fname-one string
+--- @param fname-two string
--- @return 0|1
-function vim.fn.assert_equalfile() end
+function vim.fn.assert_equalfile(fname-one, fname-two) end
--- When v:exception does not contain the string {error} an error
--- message is added to |v:errors|. Also see |assert-return|.
@@ -254,25 +257,25 @@ function vim.fn.assert_exception(error, msg) end
--- When {error} is a string it must be found literally in the
--- first reported error. Most often this will be the error code,
--- including the colon, e.g. "E123:". >vim
---- assert_fails('bad cmd', 'E987:')
+--- call assert_fails('bad cmd', 'E987:')
--- <
--- When {error} is a |List| with one or two strings, these are
--- used as patterns. The first pattern is matched against the
--- first reported error: >vim
---- assert_fails('cmd', ['E987:.*expected bool'])
+--- call assert_fails('cmd', ['E987:.*expected bool'])
--- <The second pattern, if present, is matched against the last
--- reported error. To only match the last error use an empty
--- string for the first error: >vim
---- assert_fails('cmd', ['', 'E987:'])
+--- call assert_fails('cmd', ['', 'E987:'])
--- <
--- If {msg} is empty then it is not used. Do this to get the
--- default message when passing the {lnum} argument.
----
+--- *E1115*
--- When {lnum} is present and not negative, and the {error}
--- argument is present and matches, then this is compared with
--- the line number at which the error was reported. That can be
--- the line number in a function or in a script.
----
+--- *E1116*
--- When {context} is present it is used as a pattern and matched
--- against the context (script name or function name) where
--- {lnum} is located in.
@@ -280,7 +283,7 @@ function vim.fn.assert_exception(error, msg) end
--- Note that beeping is not considered an error, and some failing
--- commands only beep. Use |assert_beeps()| for those.
---
---- @param cmd any
+--- @param cmd string
--- @param error? any
--- @param msg? any
--- @param lnum? integer
@@ -291,7 +294,8 @@ function vim.fn.assert_fails(cmd, error, msg, lnum, context) end
--- When {actual} is not false an error message is added to
--- |v:errors|, like with |assert_equal()|.
--- The error is in the form "Expected False but got {actual}".
---- When {msg} is present it is prepended to that.
+--- When {msg} is present it is prefixed to that, along with the
+--- location of the assert when run from a script.
--- Also see |assert-return|.
---
--- A value is false when it is zero. When {actual} is not a
@@ -309,17 +313,18 @@ function vim.fn.assert_false(actual, msg) end
--- but got {actual}". When {msg} is present it is prefixed to
--- that.
---
---- @param lower any
---- @param upper any
---- @param actual any
---- @param msg? any
+--- @param lower number
+--- @param upper number
+--- @param actual number
+--- @param msg? string
--- @return 0|1
function vim.fn.assert_inrange(lower, upper, actual, msg) end
--- When {pattern} does not match {actual} an error message is
--- added to |v:errors|. Also see |assert-return|.
--- The error is in the form "Pattern {pattern} does not match
---- {actual}". When {msg} is present it is prefixed to that.
+--- {actual}". When {msg} is present it is prefixed to that,
+--- along with the location of the assert when run from a script.
---
--- {pattern} is used as with |expr-=~|: The matching is always done
--- like 'magic' was set and 'cpoptions' is empty, no matter what
@@ -330,13 +335,13 @@ function vim.fn.assert_inrange(lower, upper, actual, msg) end
--- Use both to match the whole text.
---
--- Example: >vim
---- assert_match('^f.*o$', 'foobar')
+--- call assert_match('^f.*o$', 'foobar')
--- <Will result in a string to be added to |v:errors|:
--- test.vim line 12: Pattern '^f.*o$' does not match 'foobar' ~
---
---- @param pattern any
---- @param actual any
---- @param msg? any
+--- @param pattern string
+--- @param actual string
+--- @param msg? string
--- @return 0|1
function vim.fn.assert_match(pattern, actual, msg) end
@@ -344,7 +349,7 @@ function vim.fn.assert_match(pattern, actual, msg) end
--- produces a beep or visual bell.
--- Also see |assert_beeps()|.
---
---- @param cmd any
+--- @param cmd string
--- @return 0|1
function vim.fn.assert_nobeep(cmd) end
@@ -362,16 +367,16 @@ function vim.fn.assert_notequal(expected, actual, msg) end
--- |v:errors| when {pattern} matches {actual}.
--- Also see |assert-return|.
---
---- @param pattern any
---- @param actual any
---- @param msg? any
+--- @param pattern string
+--- @param actual string
+--- @param msg? string
--- @return 0|1
function vim.fn.assert_notmatch(pattern, actual, msg) end
--- Report a test failure directly, using String {msg}.
--- Always returns one.
---
---- @param msg any
+--- @param msg string
--- @return 0|1
function vim.fn.assert_report(msg) end
@@ -380,10 +385,11 @@ function vim.fn.assert_report(msg) end
--- Also see |assert-return|.
--- A value is |TRUE| when it is a non-zero number or |v:true|.
--- When {actual} is not a number or |v:true| the assert fails.
---- When {msg} is given it precedes the default message.
+--- When {msg} is given it is prefixed to the default message,
+--- along with the location of the assert when run from a script.
---
--- @param actual any
---- @param msg? any
+--- @param msg? string
--- @return 0|1
function vim.fn.assert_true(actual, msg) end
@@ -397,7 +403,7 @@ function vim.fn.assert_true(actual, msg) end
--- echo atan(-4.01)
--- < -1.326405
---
---- @param expr any
+--- @param expr number
--- @return number
function vim.fn.atan(expr) end
@@ -412,8 +418,8 @@ function vim.fn.atan(expr) end
--- echo atan2(1, -1)
--- < 2.356194
---
---- @param expr1 any
---- @param expr2 any
+--- @param expr1 number
+--- @param expr2 number
--- @return number
function vim.fn.atan2(expr1, expr2) end
@@ -439,9 +445,9 @@ function vim.fn.blob2list(blob) end
--- something went wrong, or browsing is not possible.
---
--- @param save any
---- @param title any
---- @param initdir any
---- @param default any
+--- @param title string
+--- @param initdir string
+--- @param default string
--- @return 0|1
function vim.fn.browse(save, title, initdir, default) end
@@ -456,8 +462,8 @@ function vim.fn.browse(save, title, initdir, default) end
--- When the "Cancel" button is hit, something went wrong, or
--- browsing is not possible, an empty string is returned.
---
---- @param title any
---- @param initdir any
+--- @param title string
+--- @param initdir string
--- @return 0|1
function vim.fn.browsedir(title, initdir) end
@@ -729,7 +735,7 @@ function vim.fn.call(func, arglist, dict) end
---
--- Returns 0.0 if {expr} is not a |Float| or a |Number|.
---
---- @param expr any
+--- @param expr number
--- @return number
function vim.fn.ceil(expr) end
@@ -742,8 +748,8 @@ function vim.fn.ceil(expr) end
--- For a socket, there is only one stream, and {stream} should be
--- omitted.
---
---- @param id any
---- @param stream? any
+--- @param id integer
+--- @param stream? string
--- @return 0|1
function vim.fn.chanclose(id, stream) end
@@ -775,8 +781,8 @@ function vim.fn.changenr() end
--- was created with `"rpc":v:true` then the channel expects RPC
--- messages, use |rpcnotify()| and |rpcrequest()| instead.
---
---- @param id any
---- @param data any
+--- @param id number
+--- @param data string|string[]
--- @return 0|1
function vim.fn.chansend(id, data) end
@@ -820,8 +826,9 @@ function vim.fn.charclass(string) end
--- With the cursor on '세' in line 5 with text "여보세요": >vim
--- echo charcol('.') " returns 3
--- echo col('.') " returns 7
+--- <
---
---- @param expr any
+--- @param expr string|integer[]
--- @param winid? integer
--- @return integer
function vim.fn.charcol(expr, winid) end
@@ -861,8 +868,8 @@ function vim.fn.charcol(expr, winid) end
---
--- @param string string
--- @param idx integer
---- @param countcc? any
---- @param utf16? any
+--- @param countcc? boolean
+--- @param utf16? boolean
--- @return integer
function vim.fn.charidx(string, idx, countcc, utf16) end
@@ -886,6 +893,7 @@ function vim.fn.charidx(string, idx, countcc, utf16) end
--- " ... do some work
--- call chdir(save_dir)
--- endif
+--- <
---
--- @param dir string
--- @return string
@@ -907,37 +915,37 @@ function vim.fn.cindent(lnum) end
--- If {win} is specified, use the window with this number or
--- window ID instead of the current window.
---
---- @param win? any
+--- @param win? integer
function vim.fn.clearmatches(win) end
--- The result is a Number, which is the byte index of the column
---- position given with {expr}. The accepted positions are:
---- . the cursor position
---- $ the end of the cursor line (the result is the
---- number of bytes in the cursor line plus one)
---- 'x position of mark x (if the mark is not set, 0 is
---- returned)
---- v In Visual mode: the start of the Visual area (the
---- cursor is the end). When not in Visual mode
---- returns the cursor position. Differs from |'<| in
---- that it's updated right away.
+--- position given with {expr}.
+--- For accepted positions see |getpos()|.
+--- When {expr} is "$", it means the end of the cursor line, so
+--- the result is the number of bytes in the cursor line plus one.
--- Additionally {expr} can be [lnum, col]: a |List| with the line
--- and column number. Most useful when the column is "$", to get
--- the last column of a specific line. When "lnum" or "col" is
--- out of range then col() returns zero.
+---
--- With the optional {winid} argument the values are obtained for
--- that window instead of the current window.
+---
--- To get the line number use |line()|. To get both use
--- |getpos()|.
+---
--- For the screen column position use |virtcol()|. For the
--- character position use |charcol()|.
+---
--- Note that only marks in the current file can be used.
+---
--- Examples: >vim
--- echo col(".") " column of cursor
--- echo col("$") " length of cursor line plus one
--- echo col("'t") " column of mark t
--- echo col("'" .. markname) " column of mark markname
---- <The first column is 1. Returns 0 if {expr} is invalid or when
+--- <
+--- The first column is 1. Returns 0 if {expr} is invalid or when
--- the window with ID {winid} is not found.
--- For an uppercase mark the column may actually be in another
--- buffer.
@@ -946,8 +954,9 @@ function vim.fn.clearmatches(win) end
--- line. Also, when using a <Cmd> mapping the cursor isn't
--- moved, this can be used to obtain the column in Insert mode: >vim
--- imap <F2> <Cmd>echo col(".").."\n"<CR>
+--- <
---
---- @param expr any
+--- @param expr string|integer[]
--- @param winid? integer
--- @return integer
function vim.fn.col(expr, winid) end
@@ -981,8 +990,8 @@ function vim.fn.col(expr, winid) end
--- <This isn't very useful, but it shows how it works. Note that
--- an empty string is returned to avoid a zero being inserted.
---
---- @param startcol any
---- @param matches any
+--- @param startcol integer
+--- @param matches any[]
function vim.fn.complete(startcol, matches) end
--- Add {expr} to the list of matches. Only to be used by the
@@ -1065,8 +1074,9 @@ function vim.fn.complete_check() end
--- call complete_info(['mode'])
--- " Get only 'mode' and 'pum_visible'
--- call complete_info(['mode', 'pum_visible'])
+--- <
---
---- @param what? any
+--- @param what? any[]
--- @return table
function vim.fn.complete_info(what) end
@@ -1121,10 +1131,10 @@ function vim.fn.complete_info(what) end
--- don't fit, a vertical layout is used anyway. For some systems
--- the horizontal layout is always used.
---
---- @param msg any
---- @param choices? any
---- @param default? any
---- @param type? any
+--- @param msg string
+--- @param choices? string
+--- @param default? integer
+--- @param type? string
--- @return integer
function vim.fn.confirm(msg, choices, default, type) end
@@ -1150,7 +1160,7 @@ function vim.fn.copy(expr) end
--- echo cos(-4.01)
--- < -0.646043
---
---- @param expr any
+--- @param expr number
--- @return number
function vim.fn.cos(expr) end
@@ -1164,7 +1174,7 @@ function vim.fn.cos(expr) end
--- echo cosh(-0.5)
--- < -1.127626
---
---- @param expr any
+--- @param expr number
--- @return number
function vim.fn.cosh(expr) end
@@ -1180,10 +1190,10 @@ function vim.fn.cosh(expr) end
--- occurrences of {expr} is returned. Zero is returned when
--- {expr} is an empty string.
---
---- @param comp any
+--- @param comp string|table|any[]
--- @param expr any
---- @param ic? any
---- @param start? any
+--- @param ic? boolean
+--- @param start? integer
--- @return integer
function vim.fn.count(comp, expr, ic, start) end
@@ -1191,7 +1201,7 @@ function vim.fn.count(comp, expr, ic, start) end
--- from the top of the |context-stack| (see |context-dict|).
--- If {index} is not given, it is assumed to be 0 (i.e.: top).
---
---- @param index? any
+--- @param index? integer
--- @return table
function vim.fn.ctxget(index) end
@@ -1207,7 +1217,7 @@ function vim.fn.ctxpop() end
--- which |context-types| to include in the pushed context.
--- Otherwise, all context types are included.
---
---- @param types? any
+--- @param types? string[]
--- @return any
function vim.fn.ctxpush(types) end
@@ -1216,8 +1226,8 @@ function vim.fn.ctxpush(types) end
--- {context} is a Dictionary with context data (|context-dict|).
--- If {index} is not given, it is assumed to be 0 (i.e.: top).
---
---- @param context any
---- @param index? any
+--- @param context table
+--- @param index? integer
--- @return any
function vim.fn.ctxset(context, index) end
@@ -1228,7 +1238,7 @@ function vim.fn.ctxsize() end
--- @param lnum integer
--- @param col? integer
---- @param off? any
+--- @param off? integer
--- @return any
function vim.fn.cursor(lnum, col, off) end
@@ -1263,7 +1273,7 @@ function vim.fn.cursor(lnum, col, off) end
--- position within a <Tab> or after the last character.
--- Returns 0 when the position could be set, -1 otherwise.
---
---- @param list any
+--- @param list integer[]
--- @return any
function vim.fn.cursor(list) end
@@ -1275,7 +1285,7 @@ function vim.fn.cursor(list) end
--- Returns |TRUE| if successfully interrupted the program.
--- Otherwise returns |FALSE|.
---
---- @param pid any
+--- @param pid integer
--- @return any
function vim.fn.debugbreak(pid) end
@@ -1299,7 +1309,7 @@ function vim.fn.debugbreak(pid) end
--- Also see |copy()|.
---
--- @param expr any
---- @param noref? any
+--- @param noref? boolean
--- @return any
function vim.fn.deepcopy(expr, noref) end
@@ -1339,9 +1349,9 @@ function vim.fn.delete(fname, flags) end
--- when using |line()| this refers to the current buffer. Use "$"
--- to refer to the last line in buffer {buf}.
---
---- @param buf any
---- @param first any
---- @param last? any
+--- @param buf integer|string
+--- @param first integer|string
+--- @param last? integer|string
--- @return any
function vim.fn.deletebufline(buf, first, last) end
@@ -1384,9 +1394,9 @@ function vim.fn.deletebufline(buf, first, last) end
--- This function can be used by plugins to implement options with
--- validation and parsing logic.
---
---- @param dict any
---- @param pattern any
---- @param callback any
+--- @param dict table
+--- @param pattern string
+--- @param callback function
--- @return any
function vim.fn.dictwatcheradd(dict, pattern, callback) end
@@ -1395,8 +1405,8 @@ function vim.fn.dictwatcheradd(dict, pattern, callback) end
--- order for the watcher to be successfully deleted.
---
--- @param dict any
---- @param pattern any
---- @param callback any
+--- @param pattern string
+--- @param callback function
--- @return any
function vim.fn.dictwatcherdel(dict, pattern, callback) end
@@ -1457,7 +1467,7 @@ function vim.fn.diff_hlID(lnum, col) end
--- echo digraph_get('aa') " Returns 'あ'
--- <
---
---- @param chars any
+--- @param chars string
--- @return any
function vim.fn.digraph_get(chars) end
@@ -1475,7 +1485,7 @@ function vim.fn.digraph_get(chars) end
--- echo digraph_getlist(1)
--- <
---
---- @param listall? any
+--- @param listall? boolean
--- @return any
function vim.fn.digraph_getlist(listall) end
@@ -1495,12 +1505,9 @@ function vim.fn.digraph_getlist(listall) end
--- Example: >vim
--- call digraph_set(' ', 'あ')
--- <
---- Can be used as a |method|: >vim
---- GetString()->digraph_set('あ')
---- <
---
---- @param chars any
---- @param digraph any
+--- @param chars string
+--- @param digraph string
--- @return any
function vim.fn.digraph_set(chars, digraph) end
@@ -1518,11 +1525,7 @@ function vim.fn.digraph_set(chars, digraph) end
--- <Except that the function returns after the first error,
--- following digraphs will not be added.
---
---- Can be used as a |method|: >vim
---- GetList()->digraph_setlist()
---- <
----
---- @param digraphlist any
+--- @param digraphlist table<integer,string[]>
--- @return any
function vim.fn.digraph_setlist(digraphlist) end
@@ -1557,7 +1560,7 @@ function vim.fn.environ() end
--- <Also see |shellescape()| and |fnameescape()|.
---
--- @param string string
---- @param chars any
+--- @param chars string
--- @return any
function vim.fn.escape(string, chars) end
@@ -1582,25 +1585,32 @@ function vim.fn.eventhandler() end
--- This function checks if an executable with the name {expr}
--- exists. {expr} must be the name of the program without any
--- arguments.
+---
--- executable() uses the value of $PATH and/or the normal
---- searchpath for programs. *PATHEXT*
+--- searchpath for programs.
+--- *PATHEXT*
--- On MS-Windows the ".exe", ".bat", etc. can optionally be
--- included. Then the extensions in $PATHEXT are tried. Thus if
--- "foo.exe" does not exist, "foo.exe.bat" can be found. If
---- $PATHEXT is not set then ".exe;.com;.bat;.cmd" is used. A dot
+--- $PATHEXT is not set then ".com;.exe;.bat;.cmd" is used. A dot
--- by itself can be used in $PATHEXT to try using the name
--- without an extension. When 'shell' looks like a Unix shell,
--- then the name is also tried without adding an extension.
--- On MS-Windows it only checks if the file exists and is not a
--- directory, not if it's really executable.
---- On Windows an executable in the same directory as Vim is
---- always found (it is added to $PATH at |startup|).
+--- On MS-Windows an executable in the same directory as the Vim
+--- executable is always found (it's added to $PATH at |startup|).
+--- *NoDefaultCurrentDirectoryInExePath*
+--- On MS-Windows an executable in Vim's current working directory
+--- is also normally found, but this can be disabled by setting
+--- the $NoDefaultCurrentDirectoryInExePath environment variable.
+---
--- The result is a Number:
--- 1 exists
--- 0 does not exist
--- |exepath()| can be used to get the full path of an executable.
---
---- @param expr any
+--- @param expr string
--- @return 0|1
function vim.fn.executable(expr) end
@@ -1641,8 +1651,8 @@ function vim.fn.execute(command, silent) end
--- Returns empty string otherwise.
--- If {expr} starts with "./" the |current-directory| is used.
---
---- @param expr any
---- @return any
+--- @param expr string
+--- @return string
function vim.fn.exepath(expr) end
--- The result is a Number, which is |TRUE| if {expr} is
@@ -1733,7 +1743,7 @@ function vim.fn.exepath(expr) end
--- <This doesn't check for existence of the "bufcount" variable,
--- but gets the value of "bufcount", and checks if that exists.
---
---- @param expr any
+--- @param expr string
--- @return 0|1
function vim.fn.exists(expr) end
@@ -1747,7 +1757,7 @@ function vim.fn.exists(expr) end
--- echo exp(-1)
--- < 0.367879
---
---- @param expr any
+--- @param expr number
--- @return any
function vim.fn.exp(expr) end
@@ -1916,9 +1926,9 @@ function vim.fn.expandcmd(string, options) end
--- fails.
--- Returns {expr1}. Returns 0 on error.
---
---- @param expr1 any
---- @param expr2 any
---- @param expr3? any
+--- @param expr1 table
+--- @param expr2 table
+--- @param expr3? table
--- @return any
function vim.fn.extend(expr1, expr2, expr3) end
@@ -1926,9 +1936,9 @@ function vim.fn.extend(expr1, expr2, expr3) end
--- List or Dictionary is created and returned. {expr1} remains
--- unchanged.
---
---- @param expr1 any
---- @param expr2 any
---- @param expr3? any
+--- @param expr1 table
+--- @param expr2 table
+--- @param expr3? table
--- @return any
function vim.fn.extendnew(expr1, expr2, expr3) end
@@ -1989,6 +1999,19 @@ function vim.fn.feedkeys(string, mode) end
--- @return any
function vim.fn.file_readable(file) end
+--- Copy the file pointed to by the name {from} to {to}. The
+--- result is a Number, which is |TRUE| if the file was copied
+--- successfully, and |FALSE| when it failed.
+--- If a file with name {to} already exists, it will fail.
+--- Note that it does not handle directories (yet).
+---
+--- This function is not available in the |sandbox|.
+---
+--- @param from string
+--- @param to string
+--- @return 0|1
+function vim.fn.filecopy(from, to) end
+
--- The result is a Number, which is |TRUE| when a file with the
--- name {file} exists, and can be read. If {file} doesn't exist,
--- or is a directory, the result is |FALSE|. {file} is any
@@ -2070,8 +2093,8 @@ function vim.fn.filewritable(file) end
--- When {expr2} is a Funcref errors inside a function are ignored,
--- unless it was defined with the "abort" flag.
---
---- @param expr1 any
---- @param expr2 any
+--- @param expr1 string|table
+--- @param expr2 string|function
--- @return any
function vim.fn.filter(expr1, expr2) end
@@ -2094,7 +2117,7 @@ function vim.fn.filter(expr1, expr2) end
---
--- @param name string
--- @param path? string
---- @param count? any
+--- @param count? integer
--- @return any
function vim.fn.finddir(name, path, count) end
@@ -2129,15 +2152,15 @@ function vim.fn.findfile(name, path, count) end
--- echo flatten([1, [2, [3, 4]], 5], 1)
--- < [1, 2, [3, 4], 5]
---
---- @param list any
---- @param maxdepth? any
+--- @param list any[]
+--- @param maxdepth? integer
--- @return any[]|0
function vim.fn.flatten(list, maxdepth) end
--- Like |flatten()| but first make a copy of {list}.
---
---- @param list any
---- @param maxdepth? any
+--- @param list any[]
+--- @param maxdepth? integer
--- @return any[]|0
function vim.fn.flattennew(list, maxdepth) end
@@ -2162,7 +2185,7 @@ function vim.fn.flattennew(list, maxdepth) end
--- echo float2nr(1.0e-100)
--- < 0
---
---- @param expr any
+--- @param expr number
--- @return any
function vim.fn.float2nr(expr) end
@@ -2178,7 +2201,7 @@ function vim.fn.float2nr(expr) end
--- echo floor(4.0)
--- < 4.0
---
---- @param expr any
+--- @param expr number
--- @return any
function vim.fn.floor(expr) end
@@ -2197,8 +2220,8 @@ function vim.fn.floor(expr) end
--- echo fmod(-12.33, 1.22)
--- < -0.13
---
---- @param expr1 any
---- @param expr2 any
+--- @param expr1 number
+--- @param expr2 number
--- @return any
function vim.fn.fmod(expr1, expr2) end
@@ -2343,8 +2366,8 @@ function vim.fn.foldtextresult(lnum) end
--- When {expr2} is a Funcref errors inside a function are ignored,
--- unless it was defined with the "abort" flag.
---
---- @param expr1 any
---- @param expr2 any
+--- @param expr1 string|table
+--- @param expr2 string|function
--- @return any
function vim.fn.foreach(expr1, expr2) end
@@ -2486,7 +2509,7 @@ vim.fn['function'] = function(name, arglist, dict) end
--- it's safe to perform. This is when waiting for the user to
--- type a character.
---
---- @param atexit? any
+--- @param atexit? boolean
--- @return any
function vim.fn.garbagecollect(atexit) end
@@ -2523,12 +2546,25 @@ function vim.fn.get(blob, idx, default) end
--- @return any
function vim.fn.get(dict, key, default) end
---- Get item {what} from Funcref {func}. Possible values for
+--- Get item {what} from |Funcref| {func}. Possible values for
--- {what} are:
---- "name" The function name
---- "func" The function
---- "dict" The dictionary
---- "args" The list with arguments
+--- "name" The function name
+--- "func" The function
+--- "dict" The dictionary
+--- "args" The list with arguments
+--- "arity" A dictionary with information about the number of
+--- arguments accepted by the function (minus the
+--- {arglist}) with the following fields:
+--- required the number of positional arguments
+--- optional the number of optional arguments,
+--- in addition to the required ones
+--- varargs |TRUE| if the function accepts a
+--- variable number of arguments |...|
+---
+--- Note: There is no error, if the {arglist} of
+--- the Funcref contains more arguments than the
+--- Funcref expects, it's not validated.
+---
--- Returns zero on error.
---
--- @param func function
@@ -2634,8 +2670,9 @@ function vim.fn.getbufinfo(dict) end
---
--- Example: >vim
--- let lines = getbufline(bufnr("myfile"), 1, "$")
+--- <
---
---- @param buf any
+--- @param buf integer|string
--- @param lnum integer
--- @param end_? integer
--- @return any
@@ -2669,7 +2706,7 @@ function vim.fn.getbufoneline(buf, lnum) end
--- let bufmodified = getbufvar(1, "&mod")
--- echo "todo myvar = " .. getbufvar("todo", "myvar")
---
---- @param buf any
+--- @param buf integer|string
--- @param varname string
--- @param def? any
--- @return any
@@ -2766,8 +2803,9 @@ function vim.fn.getchangelist(buf) end
--- endfunction
--- <
---
+--- @param expr? 0|1
--- @return integer
-function vim.fn.getchar() end
+function vim.fn.getchar(expr) end
--- The result is a Number which is the state of the modifiers for
--- the last obtained character with getchar() or in another way.
@@ -2800,7 +2838,7 @@ function vim.fn.getcharmod() end
--- getpos('.') returns [0, 5, 7, 0]
--- <
---
---- @param expr any
+--- @param expr string
--- @return integer[]
function vim.fn.getcharpos(expr) end
@@ -2823,7 +2861,7 @@ function vim.fn.getcharpos(expr) end
--- nnoremap <expr> , getcharsearch().forward ? ',' : ';'
--- <Also see |setcharsearch()|.
---
---- @return table[]
+--- @return table
function vim.fn.getcharsearch() end
--- Get a single character from the user or input stream as a
@@ -2837,27 +2875,28 @@ function vim.fn.getcharsearch() end
--- Otherwise this works like |getchar()|, except that a number
--- result is converted to a string.
---
+--- @param expr? 0|1
--- @return string
-function vim.fn.getcharstr() end
+function vim.fn.getcharstr(expr) end
--- Return the type of the current command-line completion.
--- Only works when the command line is being edited, thus
--- requires use of |c_CTRL-\_e| or |c_CTRL-R_=|.
--- See |:command-completion| for the return string.
---- Also see |getcmdtype()|, |setcmdpos()|, |getcmdline()| and
---- |setcmdline()|.
+--- Also see |getcmdtype()|, |setcmdpos()|, |getcmdline()|,
+--- |getcmdprompt()| and |setcmdline()|.
--- Returns an empty string when completion is not defined.
---
--- @return string
function vim.fn.getcmdcompltype() end
---- Return the current command-line. Only works when the command
---- line is being edited, thus requires use of |c_CTRL-\_e| or
---- |c_CTRL-R_=|.
+--- Return the current command-line input. Only works when the
+--- command line is being edited, thus requires use of
+--- |c_CTRL-\_e| or |c_CTRL-R_=|.
--- Example: >vim
--- cmap <F7> <C-\>eescape(getcmdline(), ' \')<CR>
---- <Also see |getcmdtype()|, |getcmdpos()|, |setcmdpos()| and
---- |setcmdline()|.
+--- <Also see |getcmdtype()|, |getcmdpos()|, |setcmdpos()|,
+--- |getcmdprompt()| and |setcmdline()|.
--- Returns an empty string when entering a password or using
--- |inputsecret()|.
---
@@ -2869,12 +2908,22 @@ function vim.fn.getcmdline() end
--- Only works when editing the command line, thus requires use of
--- |c_CTRL-\_e| or |c_CTRL-R_=| or an expression mapping.
--- Returns 0 otherwise.
---- Also see |getcmdtype()|, |setcmdpos()|, |getcmdline()| and
---- |setcmdline()|.
+--- Also see |getcmdtype()|, |setcmdpos()|, |getcmdline()|,
+--- |getcmdprompt()| and |setcmdline()|.
---
--- @return integer
function vim.fn.getcmdpos() end
+--- Return the current command-line prompt when using functions
+--- like |input()| or |confirm()|.
+--- Only works when the command line is being edited, thus
+--- requires use of |c_CTRL-\_e| or |c_CTRL-R_=|.
+--- Also see |getcmdtype()|, |getcmdline()|, |getcmdpos()|,
+--- |setcmdpos()| and |setcmdline()|.
+---
+--- @return string
+function vim.fn.getcmdprompt() end
+
--- Return the screen position of the cursor in the command line
--- as a byte count. The first column is 1.
--- Instead of |getcmdpos()|, it adds the prompt position.
@@ -2927,6 +2976,7 @@ function vim.fn.getcmdwintype() end
--- customlist,{func} custom completion, defined via {func}
--- diff_buffer |:diffget| and |:diffput| completion
--- dir directory names
+--- dir_in_path directory names in |'cdpath'|
--- environment environment variable names
--- event autocommand events
--- expression Vim expression
@@ -2979,9 +3029,9 @@ function vim.fn.getcmdwintype() end
--- If there are no matches, an empty list is returned. An
--- invalid value for {type} produces an error.
---
---- @param pat any
---- @param type any
---- @param filtered? any
+--- @param pat string
+--- @param type string
+--- @param filtered? boolean
--- @return string[]
function vim.fn.getcompletion(pat, type, filtered) end
@@ -3227,7 +3277,7 @@ function vim.fn.getline(lnum, end_) end
--- <
---
--- @param nr integer
---- @param what? any
+--- @param what? table
--- @return any
function vim.fn.getloclist(nr, what) end
@@ -3249,8 +3299,8 @@ function vim.fn.getloclist(nr, what) end
--- Refer to |getpos()| for getting information about a specific
--- mark.
---
---- @param buf? any
---- @return any
+--- @param buf? integer?
+--- @return vim.fn.getmarklist.ret.item[]
function vim.fn.getmarklist(buf) end
--- Returns a |List| with all matches previously defined for the
@@ -3284,7 +3334,7 @@ function vim.fn.getmarklist(buf) end
--- unlet m
--- <
---
---- @param win? any
+--- @param win? integer
--- @return any
function vim.fn.getmatches(win) end
@@ -3327,9 +3377,34 @@ function vim.fn.getmousepos() end
--- @return integer
function vim.fn.getpid() end
---- Get the position for String {expr}. For possible values of
---- {expr} see |line()|. For getting the cursor position see
---- |getcurpos()|.
+--- Get the position for String {expr}.
+--- The accepted values for {expr} are:
+--- . The cursor position.
+--- $ The last line in the current buffer.
+--- 'x Position of mark x (if the mark is not set, 0 is
+--- returned for all values).
+--- w0 First line visible in current window (one if the
+--- display isn't updated, e.g. in silent Ex mode).
+--- w$ Last line visible in current window (this is one
+--- less than "w0" if no lines are visible).
+--- v When not in Visual mode, returns the cursor
+--- position. In Visual mode, returns the other end
+--- of the Visual area. A good way to think about
+--- this is that in Visual mode "v" and "." complement
+--- each other. While "." refers to the cursor
+--- position, "v" refers to where |v_o| would move the
+--- cursor. As a result, you can use "v" and "."
+--- together to work on all of a selection in
+--- characterwise Visual mode. If the cursor is at
+--- the end of a characterwise Visual area, "v" refers
+--- to the start of the same Visual area. And if the
+--- cursor is at the start of a characterwise Visual
+--- area, "v" refers to the end of the same Visual
+--- area. "v" differs from |'<| and |'>| in that it's
+--- updated right away.
+--- Note that a mark in another file can be used. The line number
+--- then applies to another buffer.
+---
--- The result is a |List| with four numbers:
--- [bufnum, lnum, col, off]
--- "bufnum" is zero, unless a mark like '0 or 'A is used, then it
@@ -3340,20 +3415,25 @@ function vim.fn.getpid() end
--- it is the offset in screen columns from the start of the
--- character. E.g., a position within a <Tab> or after the last
--- character.
---- Note that for '< and '> Visual mode matters: when it is "V"
---- (visual line mode) the column of '< is zero and the column of
---- '> is a large number equal to |v:maxcol|.
+---
+--- For getting the cursor position see |getcurpos()|.
--- The column number in the returned List is the byte position
--- within the line. To get the character position in the line,
--- use |getcharpos()|.
+---
+--- Note that for '< and '> Visual mode matters: when it is "V"
+--- (visual line mode) the column of '< is zero and the column of
+--- '> is a large number equal to |v:maxcol|.
--- A very large column number equal to |v:maxcol| can be returned,
--- in which case it means "after the end of the line".
--- If {expr} is invalid, returns a list with all zeros.
+---
--- This can be used to save and restore the position of a mark: >vim
--- let save_a_mark = getpos("'a")
--- " ...
--- call setpos("'a", save_a_mark)
---- <Also see |getcharpos()|, |getcurpos()| and |setpos()|.
+--- <
+--- Also see |getcharpos()|, |getcurpos()| and |setpos()|.
---
--- @param expr string
--- @return integer[]
@@ -3462,7 +3542,7 @@ function vim.fn.getpos(expr) end
--- echo getqflist({'lines' : ["F1:10:L10"]})
--- <
---
---- @param what? any
+--- @param what? table
--- @return any
function vim.fn.getqflist(what) end
@@ -3536,14 +3616,14 @@ function vim.fn.getreginfo(regname) end
--- The optional argument {opts} is a Dict and supports the
--- following items:
---
---- type Specify the region's selection type
---- (default: "v"):
---- "v" for |charwise| mode
---- "V" for |linewise| mode
---- "<CTRL-V>" for |blockwise-visual| mode
+--- type Specify the region's selection type.
+--- See |getregtype()| for possible values,
+--- except that the width can be omitted
+--- and an empty string cannot be used.
+--- (default: "v")
---
--- exclusive If |TRUE|, use exclusive selection
---- for the end position
+--- for the end position.
--- (default: follow 'selection')
---
--- You can get the last selection type by |visualmode()|.
@@ -3569,8 +3649,8 @@ function vim.fn.getreginfo(regname) end
--- difference if the buffer is displayed in a window with
--- different 'virtualedit' or 'list' values.
---
---- Examples: >
---- :xnoremap <CR>
+--- Examples: >vim
+--- xnoremap <CR>
--- \ <Cmd>echom getregion(
--- \ getpos('v'), getpos('.'), #{ type: mode() })<CR>
--- <
@@ -3777,7 +3857,7 @@ function vim.fn.gettagstack(winnr) end
--- xgettext does not understand escaping in single quoted
--- strings.
---
---- @param text any
+--- @param text string
--- @return any
function vim.fn.gettext(text) end
@@ -3904,10 +3984,10 @@ function vim.fn.getwinvar(winnr, varname, def) end
--- See |expand()| for expanding special Vim variables. See
--- |system()| for getting the raw output of an external command.
---
---- @param expr any
+--- @param expr string
--- @param nosuf? boolean
---- @param list? any
---- @param alllinks? any
+--- @param list? boolean
+--- @param alllinks? boolean
--- @return any
function vim.fn.glob(expr, nosuf, list, alllinks) end
@@ -3965,10 +4045,10 @@ function vim.fn.glob2regpat(string) end
--- supported, thus using 'path' will not always work properly.
---
--- @param path string
---- @param expr any
+--- @param expr string
--- @param nosuf? boolean
---- @param list? any
---- @param allinks? any
+--- @param list? boolean
+--- @param allinks? boolean
--- @return any
function vim.fn.globpath(path, expr, nosuf, list, allinks) end
@@ -4039,7 +4119,7 @@ function vim.fn.globpath(path, expr, nosuf, list, allinks) end
--- endif
--- <
---
---- @param feature any
+--- @param feature string
--- @return 0|1
function vim.fn.has(feature) end
@@ -4047,8 +4127,8 @@ function vim.fn.has(feature) end
--- has an entry with key {key}. FALSE otherwise. The {key}
--- argument is a string.
---
---- @param dict any
---- @param key any
+--- @param dict table
+--- @param key string
--- @return 0|1
function vim.fn.has_key(dict, key) end
@@ -4105,7 +4185,7 @@ function vim.fn.haslocaldir(winnr, tabnr) end
---
--- @param what any
--- @param mode? string
---- @param abbr? any
+--- @param abbr? boolean
--- @return 0|1
function vim.fn.hasmapto(what, mode, abbr) end
@@ -4143,7 +4223,7 @@ function vim.fn.highlight_exists(name) end
--- let date=input("Enter date: ")
--- <This function is not available in the |sandbox|.
---
---- @param history any
+--- @param history string
--- @param item any
--- @return 0|1
function vim.fn.histadd(history, item) end
@@ -4180,7 +4260,7 @@ function vim.fn.histadd(history, item) end
--- let \@/ = histget("search", -1)
--- <
---
---- @param history any
+--- @param history string
--- @param item? any
--- @return 0|1
function vim.fn.histdel(history, item) end
@@ -4200,8 +4280,8 @@ function vim.fn.histdel(history, item) end
--- command -nargs=1 H execute histget("cmd", 0+<args>)
--- <
---
---- @param history any
---- @param index? any
+--- @param history string
+--- @param index? integer|string
--- @return string
function vim.fn.histget(history, index) end
@@ -4211,8 +4291,9 @@ function vim.fn.histget(history, index) end
---
--- Example: >vim
--- let inp_index = histnr("expr")
+--- <
---
---- @param history any
+--- @param history string
--- @return integer
function vim.fn.histnr(history) end
@@ -4258,8 +4339,8 @@ function vim.fn.hostname() end
--- cannot use UCS-2 in a string anyway, because of the NUL bytes.
---
--- @param string string
---- @param from any
---- @param to any
+--- @param from string
+--- @param to string
--- @return any
function vim.fn.iconv(string, from, to) end
@@ -4270,7 +4351,7 @@ function vim.fn.iconv(string, from, to) end
--- Note that `v:_null_string`, `v:_null_list`, `v:_null_dict` and
--- `v:_null_blob` have the same `id()` with different types
--- because they are internally represented as NULL pointers.
---- `id()` returns a hexadecimal representanion of the pointers to
+--- `id()` returns a hexadecimal representation of the pointers to
--- the containers (i.e. like `0x994a40`), same as `printf("%p",
--- {expr})`, but it is advised against counting on the exact
--- format of the return value.
@@ -4318,11 +4399,12 @@ function vim.fn.indent(lnum) end
--- if index(numbers, 123) >= 0
--- " ...
--- endif
+--- <
---
--- @param object any
--- @param expr any
---- @param start? any
---- @param ic? any
+--- @param start? integer
+--- @param ic? boolean
--- @return any
function vim.fn.index(object, expr, start, ic) end
@@ -4362,6 +4444,7 @@ function vim.fn.index(object, expr, start, ic) end
--- echo indexof(l, "v:val.n == 20")
--- echo indexof(l, {i, v -> v.n == 30})
--- echo indexof(l, "v:val.n == 20", #{startidx: 1})
+--- <
---
--- @param object any
--- @param expr any
@@ -4370,9 +4453,9 @@ function vim.fn.index(object, expr, start, ic) end
function vim.fn.indexof(object, expr, opts) end
---
---- @param prompt any
---- @param text? any
---- @param completion? any
+--- @param prompt string
+--- @param text? string
+--- @param completion? string
--- @return any
function vim.fn.input(prompt, text, completion) end
@@ -4484,6 +4567,7 @@ function vim.fn.input(prompt, text, completion) end
--- let g:Foo = input("enter search pattern: ")
--- call inputrestore()
--- endfunction
+--- <
---
--- @param opts table
--- @return any
@@ -4512,7 +4596,7 @@ function vim.fn.inputdialog(...) end
--- let color = inputlist(['Select color:', '1. red',
--- \ '2. green', '3. blue'])
---
---- @param textlist any
+--- @param textlist string[]
--- @return any
function vim.fn.inputlist(textlist) end
@@ -4544,8 +4628,8 @@ function vim.fn.inputsave() end
--- typed on the command-line in response to the issued prompt.
--- NOTE: Command-line completion is not supported.
---
---- @param prompt any
---- @param text? any
+--- @param prompt string
+--- @param text? string
--- @return any
function vim.fn.inputsecret(prompt, text) end
@@ -4592,16 +4676,34 @@ function vim.fn.interrupt() end
--- let bits = invert(bits)
--- <
---
---- @param expr any
+--- @param expr number
--- @return any
function vim.fn.invert(expr) end
+--- The result is a Number, which is |TRUE| when {path} is an
+--- absolute path.
+--- On Unix, a path is considered absolute when it starts with '/'.
+--- On MS-Windows, it is considered absolute when it starts with an
+--- optional drive prefix and is followed by a '\' or '/'. UNC paths
+--- are always absolute.
+--- Example: >vim
+--- echo isabsolutepath('/usr/share/') " 1
+--- echo isabsolutepath('./foobar') " 0
+--- echo isabsolutepath('C:\Windows') " 1
+--- echo isabsolutepath('foobar') " 0
+--- echo isabsolutepath('\\remote\file') " 1
+--- <
+---
+--- @param path string
+--- @return 0|1
+function vim.fn.isabsolutepath(path) end
+
--- The result is a Number, which is |TRUE| when a directory
--- with the name {directory} exists. If {directory} doesn't
--- exist, or isn't a directory, the result is |FALSE|. {directory}
--- is any expression, which is used as a String.
---
---- @param directory any
+--- @param directory string
--- @return 0|1
function vim.fn.isdirectory(directory) end
@@ -4612,7 +4714,7 @@ function vim.fn.isdirectory(directory) end
--- echo isinf(-1.0 / 0.0)
--- < -1
---
---- @param expr any
+--- @param expr number
--- @return 1|0|-1
function vim.fn.isinf(expr) end
@@ -4637,7 +4739,7 @@ function vim.fn.islocked(expr) end
--- echo isnan(0.0 / 0.0)
--- < 1
---
---- @param expr any
+--- @param expr number
--- @return 0|1
function vim.fn.isnan(expr) end
@@ -4649,6 +4751,10 @@ function vim.fn.isnan(expr) end
--- for [key, value] in items(mydict)
--- echo key .. ': ' .. value
--- endfor
+--- <
+--- A List or a String argument is also supported. In these
+--- cases, items() returns a List with the index and the value at
+--- the index.
---
--- @param dict any
--- @return any
@@ -4663,7 +4769,7 @@ function vim.fn.jobclose(...) end
--- Return the PID (process id) of |job-id| {job}.
---
---- @param job any
+--- @param job integer
--- @return integer
function vim.fn.jobpid(job) end
@@ -4671,7 +4777,7 @@ function vim.fn.jobpid(job) end
--- columns and {height} rows.
--- Fails if the job was not started with `"pty":v:true`.
---
---- @param job any
+--- @param job integer
--- @param width integer
--- @param height integer
--- @return any
@@ -4769,7 +4875,7 @@ function vim.fn.jobsend(...) end
--- - -1 if {cmd}[0] is not executable.
--- See also |job-control|, |channel|, |msgpack-rpc|.
---
---- @param cmd any
+--- @param cmd string|string[]
--- @param opts? table
--- @return any
function vim.fn.jobstart(cmd, opts) end
@@ -4783,7 +4889,7 @@ function vim.fn.jobstart(cmd, opts) end
--- Returns 1 for valid job id, 0 for invalid id, including jobs have
--- exited or stopped.
---
---- @param id any
+--- @param id integer
--- @return any
function vim.fn.jobstop(id) end
@@ -4807,7 +4913,7 @@ function vim.fn.jobstop(id) end
--- -2 if the job was interrupted (by |CTRL-C|)
--- -3 if the job-id is invalid
---
---- @param jobs any
+--- @param jobs integer[]
--- @param timeout? integer
--- @return any
function vim.fn.jobwait(jobs, timeout) end
@@ -4822,8 +4928,8 @@ function vim.fn.jobwait(jobs, timeout) end
--- converted into a string like with |string()|.
--- The opposite function is |split()|.
---
---- @param list any
---- @param sep? any
+--- @param list any[]
+--- @param sep? string
--- @return any
function vim.fn.join(list, sep) end
@@ -4863,7 +4969,7 @@ function vim.fn.json_encode(expr) end
--- Return a |List| with all the keys of {dict}. The |List| is in
--- arbitrary order. Also see |items()| and |values()|.
---
---- @param dict any
+--- @param dict table
--- @return any
function vim.fn.keys(dict) end
@@ -4958,28 +5064,16 @@ function vim.fn.libcall(libname, funcname, argument) end
--- @return any
function vim.fn.libcallnr(libname, funcname, argument) end
---- The result is a Number, which is the line number of the file
---- position given with {expr}. The {expr} argument is a string.
---- The accepted positions are:
---- . the cursor position
---- $ the last line in the current buffer
---- 'x position of mark x (if the mark is not set, 0 is
---- returned)
---- w0 first line visible in current window (one if the
---- display isn't updated, e.g. in silent Ex mode)
---- w$ last line visible in current window (this is one
---- less than "w0" if no lines are visible)
---- v In Visual mode: the start of the Visual area (the
---- cursor is the end). When not in Visual mode
---- returns the cursor position. Differs from |'<| in
---- that it's updated right away.
---- Note that a mark in another file can be used. The line number
---- then applies to another buffer.
+--- See |getpos()| for accepted positions.
+---
--- To get the column number use |col()|. To get both use
--- |getpos()|.
+---
--- With the optional {winid} argument the values are obtained for
--- that window instead of the current window.
+---
--- Returns 0 for invalid values of {expr} and {winid}.
+---
--- Examples: >vim
--- echo line(".") " line number of the cursor
--- echo line(".", winid) " idem, in window "winid"
@@ -4989,7 +5083,7 @@ function vim.fn.libcallnr(libname, funcname, argument) end
--- To jump to the last known position when opening a file see
--- |last-position-jump|.
---
---- @param expr any
+--- @param expr string|integer[]
--- @param winid? integer
--- @return integer
function vim.fn.line(expr, winid) end
@@ -5029,7 +5123,7 @@ function vim.fn.lispindent(lnum) end
---
--- |blob2list()| does the opposite.
---
---- @param list any
+--- @param list any[]
--- @return any
function vim.fn.list2blob(list) end
@@ -5048,8 +5142,8 @@ function vim.fn.list2blob(list) end
--- <
--- Returns an empty string on error.
---
---- @param list any
---- @param utf8? any
+--- @param list any[]
+--- @param utf8? boolean
--- @return any
function vim.fn.list2str(list, utf8) end
@@ -5069,7 +5163,7 @@ function vim.fn.localtime() end
--- echo log(exp(5))
--- < 5.0
---
---- @param expr any
+--- @param expr number
--- @return any
function vim.fn.log(expr) end
@@ -5082,7 +5176,7 @@ function vim.fn.log(expr) end
--- echo log10(0.01)
--- < -2.0
---
---- @param expr any
+--- @param expr number
--- @return any
function vim.fn.log10(expr) end
@@ -5139,8 +5233,8 @@ function vim.fn.log10(expr) end
--- When {expr2} is a Funcref errors inside a function are ignored,
--- unless it was defined with the "abort" flag.
---
---- @param expr1 any
---- @param expr2 any
+--- @param expr1 string|table|any[]
+--- @param expr2 string|function
--- @return any
function vim.fn.map(expr1, expr2) end
@@ -5182,6 +5276,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>.
@@ -5214,6 +5309,7 @@ function vim.fn.map(expr1, expr2) end
--- This function can be used to map a key even when it's already
--- mapped, and have it do the original mapping too. Sketch: >vim
--- exe 'nnoremap <Tab> ==' .. maparg('<Tab>', 'n')
+--- <
---
--- @param name string
--- @param mode? string
@@ -5263,7 +5359,7 @@ function vim.fn.maparg(name, mode, abbr, dict) end
---
--- @param name string
--- @param mode? string
---- @param abbr? any
+--- @param abbr? boolean
--- @return any
function vim.fn.mapcheck(name, mode, abbr) end
@@ -5296,9 +5392,11 @@ function vim.fn.mapcheck(name, mode, abbr) end
--- \ {_, m -> m.lhs == 'xyzzy'})[0].mode_bits
--- ounmap xyzzy
--- echo printf("Operator-pending mode bit: 0x%x", op_bit)
+--- <
---
---- @return any
-function vim.fn.maplist() end
+--- @param abbr? 0|1
+--- @return table[]
+function vim.fn.maplist(abbr) end
--- Like |map()| but instead of replacing items in {expr1} a new
--- List or Dictionary is created and returned. {expr1} remains
@@ -5311,8 +5409,8 @@ function vim.fn.maplist() end
function vim.fn.mapnew(expr1, expr2) end
--- @param mode string
---- @param abbr? any
---- @param dict? any
+--- @param abbr? boolean
+--- @param dict? boolean
--- @return any
function vim.fn.mapset(mode, abbr, dict) end
@@ -5350,8 +5448,9 @@ function vim.fn.mapset(mode, abbr, dict) end
--- for d in save_maps
--- call mapset(d)
--- endfor
+--- <
---
---- @param dict any
+--- @param dict boolean
--- @return any
function vim.fn.mapset(dict) end
@@ -5417,10 +5516,10 @@ function vim.fn.mapset(dict) end
--- zero matches at the start instead of a number of matches
--- further down in the text.
---
---- @param expr any
---- @param pat any
---- @param start? any
---- @param count? any
+--- @param expr string|any[]
+--- @param pat string
+--- @param start? integer
+--- @param count? integer
--- @return any
function vim.fn.match(expr, pat, start, count) end
@@ -5481,20 +5580,20 @@ function vim.fn.match(expr, pat, start, count) end
--- available from |getmatches()|. All matches can be deleted in
--- one operation by |clearmatches()|.
---
---- @param group any
---- @param pattern any
---- @param priority? any
---- @param id? any
---- @param dict? any
+--- @param group integer|string
+--- @param pattern string
+--- @param priority? integer
+--- @param id? integer
+--- @param dict? string
--- @return any
function vim.fn.matchadd(group, pattern, priority, id, dict) end
--- Same as |matchadd()|, but requires a list of positions {pos}
--- instead of a pattern. This command is faster than |matchadd()|
---- because it does not require to handle regular expressions and
---- sets buffer line boundaries to redraw screen. It is supposed
---- to be used when fast match additions and deletions are
---- required, for example to highlight matching parentheses.
+--- because it does not handle regular expressions and it sets
+--- buffer line boundaries to redraw screen. It is supposed to be
+--- used when fast match additions and deletions are required, for
+--- example to highlight matching parentheses.
--- *E5030* *E5031*
--- {pos} is a list of positions. Each position can be one of
--- these:
@@ -5525,11 +5624,11 @@ function vim.fn.matchadd(group, pattern, priority, id, dict) end
--- <Matches added by |matchaddpos()| are returned by
--- |getmatches()|.
---
---- @param group any
---- @param pos any
---- @param priority? any
---- @param id? any
---- @param dict? any
+--- @param group integer|string
+--- @param pos any[]
+--- @param priority? integer
+--- @param id? integer
+--- @param dict? string
--- @return any
function vim.fn.matchaddpos(group, pos, priority, id, dict) end
@@ -5575,19 +5674,19 @@ function vim.fn.matcharg(nr) end
---
--- Examples: >vim
--- " Assuming line 3 in buffer 5 contains "a"
---- :echo matchbufline(5, '\<\k\+\>', 3, 3)
---- [{'lnum': 3, 'byteidx': 0, 'text': 'a'}]
+--- echo matchbufline(5, '\<\k\+\>', 3, 3)
+--- < `[{'lnum': 3, 'byteidx': 0, 'text': 'a'}]` >vim
--- " Assuming line 4 in buffer 10 contains "tik tok"
---- :echo matchbufline(10, '\<\k\+\>', 1, 4)
---- [{'lnum': 4, 'byteidx': 0, 'text': 'tik'}, {'lnum': 4, 'byteidx': 4, 'text': 'tok'}]
---- <
+--- echo matchbufline(10, '\<\k\+\>', 1, 4)
+--- < `[{'lnum': 4, 'byteidx': 0, 'text': 'tik'}, {'lnum': 4, 'byteidx': 4, 'text': 'tok'}]`
+---
--- If {submatch} is present and is v:true, then submatches like
--- "\1", "\2", etc. are also returned. Example: >vim
--- " Assuming line 2 in buffer 2 contains "acd"
---- :echo matchbufline(2, '\(a\)\?\(b\)\?\(c\)\?\(.*\)', 2, 2
+--- echo matchbufline(2, '\(a\)\?\(b\)\?\(c\)\?\(.*\)', 2, 2
--- \ {'submatches': v:true})
---- [{'lnum': 2, 'byteidx': 0, 'text': 'acd', 'submatches': ['a', '', 'c', 'd', '', '', '', '', '']}]
---- <The "submatches" List always contains 9 items. If a submatch
+--- < `[{'lnum': 2, 'byteidx': 0, 'text': 'acd', 'submatches': ['a', '', 'c', 'd', '', '', '', '', '']}]`
+--- The "submatches" List always contains 9 items. If a submatch
--- is not found, then an empty string is returned for that
--- submatch.
---
@@ -5606,8 +5705,8 @@ function vim.fn.matchbufline(buf, pat, lnum, end_, dict) end
--- If {win} is specified, use the window with this number or
--- window ID instead of the current window.
---
---- @param id any
---- @param win? any
+--- @param id integer
+--- @param win? integer
--- @return any
function vim.fn.matchdelete(id, win) end
@@ -5630,9 +5729,9 @@ function vim.fn.matchdelete(id, win) end
--- When {expr} is a |List| the result is equal to |match()|.
---
--- @param expr any
---- @param pat any
---- @param start? any
---- @param count? any
+--- @param pat string
+--- @param start? integer
+--- @param count? integer
--- @return any
function vim.fn.matchend(expr, pat, start, count) end
@@ -5698,9 +5797,9 @@ function vim.fn.matchend(expr, pat, start, count) end
--- \ {'matchseq': 1})
--- <results in `['two one']`.
---
---- @param list any
---- @param str any
---- @param dict? any
+--- @param list any[]
+--- @param str string
+--- @param dict? string
--- @return any
function vim.fn.matchfuzzy(list, str, dict) end
@@ -5725,9 +5824,9 @@ function vim.fn.matchfuzzy(list, str, dict) end
--- \ ->matchfuzzypos('ll', {'key' : 'text'})
--- <results in `[[{"id": 10, "text": "hello"}], [[2, 3]], [127]]`
---
---- @param list any
---- @param str any
---- @param dict? any
+--- @param list any[]
+--- @param str string
+--- @param dict? string
--- @return any
function vim.fn.matchfuzzypos(list, str, dict) end
@@ -5743,9 +5842,9 @@ function vim.fn.matchfuzzypos(list, str, dict) end
--- You can pass in a List, but that is not very useful.
---
--- @param expr any
---- @param pat any
---- @param start? any
---- @param count? any
+--- @param pat string
+--- @param start? integer
+--- @param count? integer
--- @return any
function vim.fn.matchlist(expr, pat, start, count) end
@@ -5762,9 +5861,9 @@ function vim.fn.matchlist(expr, pat, start, count) end
--- The type isn't changed, it's not necessarily a String.
---
--- @param expr any
---- @param pat any
---- @param start? any
---- @param count? any
+--- @param pat string
+--- @param start? integer
+--- @param count? integer
--- @return any
function vim.fn.matchstr(expr, pat, start, count) end
@@ -5786,17 +5885,17 @@ function vim.fn.matchstr(expr, pat, start, count) end
--- option settings on the pattern.
---
--- Example: >vim
---- :echo matchstrlist(['tik tok'], '\<\k\+\>')
---- [{'idx': 0, 'byteidx': 0, 'text': 'tik'}, {'idx': 0, 'byteidx': 4, 'text': 'tok'}]
---- :echo matchstrlist(['a', 'b'], '\<\k\+\>')
---- [{'idx': 0, 'byteidx': 0, 'text': 'a'}, {'idx': 1, 'byteidx': 0, 'text': 'b'}]
---- <
+--- echo matchstrlist(['tik tok'], '\<\k\+\>')
+--- < `[{'idx': 0, 'byteidx': 0, 'text': 'tik'}, {'idx': 0, 'byteidx': 4, 'text': 'tok'}]` >vim
+--- echo matchstrlist(['a', 'b'], '\<\k\+\>')
+--- < `[{'idx': 0, 'byteidx': 0, 'text': 'a'}, {'idx': 1, 'byteidx': 0, 'text': 'b'}]`
+---
--- If "submatches" is present and is v:true, then submatches like
--- "\1", "\2", etc. are also returned. Example: >vim
---- :echo matchstrlist(['acd'], '\(a\)\?\(b\)\?\(c\)\?\(.*\)',
+--- echo matchstrlist(['acd'], '\(a\)\?\(b\)\?\(c\)\?\(.*\)',
--- \ #{submatches: v:true})
---- [{'idx': 0, 'byteidx': 0, 'text': 'acd', 'submatches': ['a', '', 'c', 'd', '', '', '', '', '']}]
---- <The "submatches" List always contains 9 items. If a submatch
+--- < `[{'idx': 0, 'byteidx': 0, 'text': 'acd', 'submatches': ['a', '', 'c', 'd', '', '', '', '', '']}]`
+--- The "submatches" List always contains 9 items. If a submatch
--- is not found, then an empty string is returned for that
--- submatch.
---
@@ -5824,9 +5923,9 @@ function vim.fn.matchstrlist(list, pat, dict) end
--- The type isn't changed, it's not necessarily a String.
---
--- @param expr any
---- @param pat any
---- @param start? any
---- @param count? any
+--- @param pat string
+--- @param start? integer
+--- @param count? integer
--- @return any
function vim.fn.matchstrpos(expr, pat, start, count) end
@@ -5889,7 +5988,7 @@ function vim.fn.max(expr) end
--- <
---
--- @param path string
---- @param modes? any
+--- @param modes? string
--- @return any
function vim.fn.menu_get(path, modes) end
@@ -5986,17 +6085,14 @@ function vim.fn.min(expr) end
--- When {flags} is present it must be a string. An empty string
--- has no effect.
---
---- If {flags} contains "p" then intermediate directories are
---- created as necessary.
+--- {flags} can contain these character flags:
+--- "p" intermediate directories will be created as necessary
+--- "D" {name} will be deleted at the end of the current
+--- function, but not recursively |:defer|
+--- "R" {name} will be deleted recursively at the end of the
+--- current function |:defer|
---
---- If {flags} contains "D" then {name} is deleted at the end of
---- the current function, as with: >vim
---- defer delete({name}, 'd')
---- <
---- If {flags} contains "R" then {name} is deleted recursively at
---- the end of the current function, as with: >vim
---- defer delete({name}, 'rf')
---- <Note that when {name} has more than one part and "p" is used
+--- Note that when {name} has more than one part and "p" is used
--- some directories may already exist. Only the first one that
--- is created and what it contains is scheduled to be deleted.
--- E.g. when using: >vim
@@ -6025,7 +6121,7 @@ function vim.fn.min(expr) end
---
--- @param name string
--- @param flags? string
---- @param prot? any
+--- @param prot? string
--- @return any
function vim.fn.mkdir(name, flags, prot) end
@@ -6159,12 +6255,7 @@ function vim.fn.msgpackdump(list, type) end
--- C parser does not support such values.
--- float |Float|. This value cannot possibly appear in
--- |msgpackparse()| output.
---- string |readfile()|-style list of strings. This value will
---- appear in |msgpackparse()| output if string contains
---- zero byte or if string is a mapping key and mapping is
---- being represented as special dictionary for other
---- reasons.
---- binary |String|, or |Blob| if binary string contains zero
+--- string |String|, or |Blob| if binary string contains zero
--- byte. This value cannot appear in |msgpackparse()|
--- output since blobs were introduced.
--- array |List|. This value cannot appear in |msgpackparse()|
@@ -6211,8 +6302,8 @@ function vim.fn.nextnonblank(lnum) end
--- characters. nr2char(0) is a real NUL and terminates the
--- string, thus results in an empty string.
---
---- @param expr any
---- @param utf8? any
+--- @param expr integer
+--- @param utf8? boolean
--- @return any
function vim.fn.nr2char(expr, utf8) end
@@ -6227,8 +6318,8 @@ function vim.fn.nr2char(expr, utf8) end
--- to separate commands. In many places it would not be clear if
--- "|" is an operator or a command separator.
---
---- @param expr any
---- @param expr1 any
+--- @param expr number
+--- @param expr1 number
--- @return any
vim.fn['or'] = function(expr, expr1) end
@@ -6246,7 +6337,7 @@ vim.fn['or'] = function(expr, expr1) end
--- Returns an empty string on error.
---
--- @param path string
---- @param len? any
+--- @param len? integer
--- @return any
function vim.fn.pathshorten(path, len) end
@@ -6279,8 +6370,8 @@ function vim.fn.perleval(expr) end
--- echo pow(32, 0.20)
--- < 2.0
---
---- @param x any
---- @param y any
+--- @param x number
+--- @param y number
--- @return any
function vim.fn.pow(x, y) end
@@ -6618,7 +6709,7 @@ function vim.fn.prevnonblank(lnum) end
--- into this, copying the exact format string and parameters that
--- were used.
---
---- @param fmt any
+--- @param fmt string
--- @param expr1? any
--- @return string
function vim.fn.printf(fmt, expr1) end
@@ -6629,7 +6720,7 @@ function vim.fn.printf(fmt, expr1) end
--- If the buffer doesn't exist or isn't a prompt buffer, an empty
--- string is returned.
---
---- @param buf any
+--- @param buf integer|string
--- @return any
function vim.fn.prompt_getprompt(buf) end
@@ -6664,8 +6755,8 @@ function vim.fn.prompt_getprompt(buf) end
--- endfunc
--- call prompt_setcallback(bufnr(), function('s:TextEntered'))
---
---- @param buf any
---- @param expr any
+--- @param buf integer|string
+--- @param expr string|function
--- @return any
function vim.fn.prompt_setcallback(buf, expr) end
@@ -6677,8 +6768,8 @@ function vim.fn.prompt_setcallback(buf, expr) end
--- mode. Without setting a callback Vim will exit Insert mode,
--- as in any buffer.
---
---- @param buf any
---- @param expr any
+--- @param buf integer|string
+--- @param expr string|function
--- @return any
function vim.fn.prompt_setinterrupt(buf, expr) end
@@ -6689,8 +6780,8 @@ function vim.fn.prompt_setinterrupt(buf, expr) end
--- call prompt_setprompt(bufnr(''), 'command: ')
--- <
---
---- @param buf any
---- @param text any
+--- @param buf integer|string
+--- @param text string
--- @return any
function vim.fn.prompt_setprompt(buf, text) end
@@ -6766,7 +6857,7 @@ function vim.fn.pyxeval(expr) end
--- echo rand(seed) % 16 " random number 0 - 15
--- <
---
---- @param expr? any
+--- @param expr? number
--- @return any
function vim.fn.rand(expr) end
@@ -6789,8 +6880,8 @@ function vim.fn.rand(expr) end
--- <
---
--- @param expr any
---- @param max? any
---- @param stride? any
+--- @param max? integer
+--- @param stride? integer
--- @return any
function vim.fn.range(expr, max, stride) end
@@ -6818,8 +6909,8 @@ function vim.fn.range(expr, max, stride) end
--- Also see |readfile()| and |writefile()|.
---
--- @param fname string
---- @param offset? any
---- @param size? any
+--- @param offset? integer
+--- @param size? integer
--- @return any
function vim.fn.readblob(fname, offset, size) end
@@ -6852,8 +6943,8 @@ function vim.fn.readblob(fname, offset, size) end
--- <
--- Returns an empty List on error.
---
---- @param directory any
---- @param expr? any
+--- @param directory string
+--- @param expr? integer
--- @return any
function vim.fn.readdir(directory, expr) end
@@ -6890,8 +6981,8 @@ function vim.fn.readdir(directory, expr) end
--- Also see |writefile()|.
---
--- @param fname string
---- @param type? any
---- @param max? any
+--- @param type? string
+--- @param max? integer
--- @return any
function vim.fn.readfile(fname, type, max) end
@@ -6913,7 +7004,7 @@ function vim.fn.readfile(fname, type, max) end
--- <
---
--- @param object any
---- @param func any
+--- @param func function
--- @param initial? any
--- @return any
function vim.fn.reduce(object, func, initial) end
@@ -7019,9 +7110,9 @@ function vim.fn.remove(list, idx) end
--- <
--- Use |delete()| to remove a file.
---
---- @param list any
+--- @param list any[]
--- @param idx integer
---- @param end_? any
+--- @param end_? integer
--- @return any
function vim.fn.remove(list, idx, end_) end
@@ -7044,7 +7135,7 @@ function vim.fn.remove(blob, idx) end
---
--- @param blob any
--- @param idx integer
---- @param end_? any
+--- @param end_? integer
--- @return any
function vim.fn.remove(blob, idx, end_) end
@@ -7055,7 +7146,7 @@ function vim.fn.remove(blob, idx, end_) end
--- Returns zero on error.
---
--- @param dict any
---- @param key any
+--- @param key string
--- @return any
function vim.fn.remove(dict, key) end
@@ -7066,8 +7157,8 @@ function vim.fn.remove(dict, key) end
--- NOTE: If {to} exists it is overwritten without warning.
--- This function is not available in the |sandbox|.
---
---- @param from any
---- @param to any
+--- @param from string
+--- @param to string
--- @return any
function vim.fn.rename(from, to) end
@@ -7081,7 +7172,7 @@ function vim.fn.rename(from, to) end
--- <Results in ['a', 'b', 'a', 'b', 'a', 'b'].
---
--- @param expr any
---- @param count any
+--- @param count integer
--- @return any
vim.fn['repeat'] = function(expr, count) end
@@ -7097,7 +7188,7 @@ vim.fn['repeat'] = function(expr, count) end
--- current directory (provided the result is still a relative
--- path name) and also keeps a trailing path separator.
---
---- @param filename any
+--- @param filename string
--- @return any
function vim.fn.resolve(filename) end
@@ -7128,7 +7219,7 @@ function vim.fn.reverse(object) end
--- echo round(-4.5)
--- < -5.0
---
---- @param expr any
+--- @param expr number
--- @return any
function vim.fn.round(expr) end
@@ -7138,8 +7229,8 @@ function vim.fn.round(expr) end
--- au VimLeave call rpcnotify(0, "leaving")
--- <
---
---- @param channel any
---- @param event any
+--- @param channel integer
+--- @param event string
--- @param args? any
--- @return any
function vim.fn.rpcnotify(channel, event, args) end
@@ -7150,19 +7241,20 @@ function vim.fn.rpcnotify(channel, event, args) end
--- let result = rpcrequest(rpc_chan, "func", 1, 2, 3)
--- <
---
---- @param channel any
---- @param method any
+--- @param channel integer
+--- @param method string
--- @param args? any
--- @return any
function vim.fn.rpcrequest(channel, method, args) end
+--- @deprecated
--- Deprecated. Replace >vim
--- let id = rpcstart('prog', ['arg1', 'arg2'])
--- <with >vim
--- let id = jobstart(['prog', 'arg1', 'arg2'], {'rpc': v:true})
--- <
---
---- @param prog any
+--- @param prog string
--- @param argv? any
--- @return any
function vim.fn.rpcstart(prog, argv) end
@@ -7195,7 +7287,7 @@ function vim.fn.rubyeval(expr) end
--- attribute at other positions.
--- Returns -1 when row or col is out of range.
---
---- @param row any
+--- @param row integer
--- @param col integer
--- @return any
function vim.fn.screenattr(row, col) end
@@ -7209,7 +7301,7 @@ function vim.fn.screenattr(row, col) end
--- This is mainly to be used for testing.
--- Returns -1 when row or col is out of range.
---
---- @param row any
+--- @param row integer
--- @param col integer
--- @return any
function vim.fn.screenchar(row, col) end
@@ -7220,7 +7312,7 @@ function vim.fn.screenchar(row, col) end
--- This is mainly to be used for testing.
--- Returns an empty List when row or col is out of range.
---
---- @param row any
+--- @param row integer
--- @param col integer
--- @return any
function vim.fn.screenchars(row, col) end
@@ -7236,7 +7328,7 @@ function vim.fn.screenchars(row, col) end
--- the following mappings: >vim
--- nnoremap <expr> GG ":echom " .. screencol() .. "\n"
--- nnoremap <silent> GG :echom screencol()<CR>
---- noremap GG <Cmd>echom screencol()<Cr>
+--- noremap GG <Cmd>echom screencol()<CR>
--- <
---
--- @return any
@@ -7288,7 +7380,7 @@ function vim.fn.screenrow() end
--- This is mainly to be used for testing.
--- Returns an empty String when row or col is out of range.
---
---- @param row any
+--- @param row integer
--- @param col integer
--- @return any
function vim.fn.screenstring(row, col) end
@@ -7347,6 +7439,9 @@ function vim.fn.screenstring(row, col) end
--- The value must not be negative. A zero value is like not
--- giving the argument.
---
+--- Note: the timeout is only considered when searching, not
+--- while evaluating the {skip} expression.
+---
--- If the {skip} expression is given it is evaluated with the
--- cursor positioned on the start of a match. If it evaluates to
--- non-zero this match is skipped. This can be used, for
@@ -7394,11 +7489,11 @@ function vim.fn.screenstring(row, col) end
--- without the 'e' flag if the cursor is on the "f" of "if".
--- The 'n' flag tells the function not to move the cursor.
---
---- @param pattern any
+--- @param pattern string
--- @param flags? string
---- @param stopline? any
+--- @param stopline? integer
--- @param timeout? integer
---- @param skip? any
+--- @param skip? string|function
--- @return any
function vim.fn.search(pattern, flags, stopline, timeout, skip) end
@@ -7545,8 +7640,8 @@ function vim.fn.searchcount(options) end
--- <
---
--- @param name string
---- @param global? any
---- @param thisblock? any
+--- @param global? boolean
+--- @param thisblock? boolean
--- @return any
function vim.fn.searchdecl(name, global, thisblock) end
@@ -7634,8 +7729,15 @@ function vim.fn.searchdecl(name, global, thisblock) end
--- \ 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string"')
--- <
---
---- @return any
-function vim.fn.searchpair() end
+--- @param start string
+--- @param middle string
+--- @param end_ string
+--- @param flags? string
+--- @param skip? string|function
+--- @param stopline? integer
+--- @param timeout? integer
+--- @return integer
+function vim.fn.searchpair(start, middle, end_, flags, skip, stopline, timeout) end
--- Same as |searchpair()|, but returns a |List| with the line and
--- column position of the match. The first element of the |List|
@@ -7647,8 +7749,15 @@ function vim.fn.searchpair() end
--- <
--- See |match-parens| for a bigger and more useful example.
---
---- @return any
-function vim.fn.searchpairpos() end
+--- @param start string
+--- @param middle string
+--- @param end_ string
+--- @param flags? string
+--- @param skip? string|function
+--- @param stopline? integer
+--- @param timeout? integer
+--- @return [integer, integer]
+function vim.fn.searchpairpos(start, middle, end_, flags, skip, stopline, timeout) end
--- Same as |search()|, but returns a |List| with the line and
--- column position of the match. The first element of the |List|
@@ -7664,11 +7773,11 @@ function vim.fn.searchpairpos() end
--- <In this example "submatch" is 2 when a lowercase letter is
--- found |/\l|, 3 when an uppercase letter is found |/\u|.
---
---- @param pattern any
+--- @param pattern string
--- @param flags? string
---- @param stopline? any
+--- @param stopline? integer
--- @param timeout? integer
---- @param skip? any
+--- @param skip? string|function
--- @return any
function vim.fn.searchpos(pattern, flags, stopline, timeout, skip) end
@@ -7714,7 +7823,7 @@ function vim.fn.serverlist() end
--- echo serverstart('::1:12345')
--- <
---
---- @param address? any
+--- @param address? string
--- @return any
function vim.fn.serverstart(address) end
@@ -7723,7 +7832,7 @@ function vim.fn.serverstart(address) end
--- If |v:servername| is stopped it is set to the next available
--- address in |serverlist()|.
---
---- @param address any
+--- @param address string
--- @return any
function vim.fn.serverstop(address) end
@@ -7751,9 +7860,9 @@ function vim.fn.serverstop(address) end
--- If {buf} is not a valid buffer or {lnum} is not valid, an
--- error message is given.
---
---- @param buf any
+--- @param buf integer|string
--- @param lnum integer
---- @param text any
+--- @param text string|string[]
--- @return any
function vim.fn.setbufline(buf, lnum, text) end
@@ -7770,7 +7879,7 @@ function vim.fn.setbufline(buf, lnum, text) end
--- call setbufvar("todo", "myvar", "foobar")
--- <This function is not available in the |sandbox|.
---
---- @param buf any
+--- @param buf integer|string
--- @param varname string
--- @param val any
--- @return any
@@ -7803,13 +7912,13 @@ function vim.fn.setbufvar(buf, varname, val) end
--- To clear the overrides pass an empty {list}: >vim
--- call setcellwidths([])
---
---- <You can use the script $VIMRUNTIME/tools/emoji_list.vim to see
+--- <You can use the script $VIMRUNTIME/tools/emoji_list.lua to see
--- the effect for known emoji characters. Move the cursor
--- through the text to check if the cell widths of your terminal
--- match with what Vim knows about each emoji. If it doesn't
--- look right you need to adjust the {list} argument.
---
---- @param list any
+--- @param list any[]
--- @return any
function vim.fn.setcellwidths(list) end
@@ -7823,8 +7932,8 @@ function vim.fn.setcellwidths(list) end
--- call setpos('.', [0, 8, 4, 0])
--- <positions the cursor on the second character '보'.
---
---- @param expr any
---- @param list any
+--- @param expr string
+--- @param list integer[]
--- @return any
function vim.fn.setcharpos(expr, list) end
@@ -7847,7 +7956,7 @@ function vim.fn.setcharpos(expr, list) end
--- call setcharsearch(prevsearch)
--- <Also see |getcharsearch()|.
---
---- @param dict any
+--- @param dict string
--- @return any
function vim.fn.setcharsearch(dict) end
@@ -7857,8 +7966,8 @@ function vim.fn.setcharsearch(dict) end
--- Returns 0 when successful, 1 when not editing the command
--- line.
---
---- @param str any
---- @param pos? any
+--- @param str string
+--- @param pos? integer
--- @return any
function vim.fn.setcmdline(str, pos) end
@@ -7876,13 +7985,13 @@ function vim.fn.setcmdline(str, pos) end
--- Returns 0 when successful, 1 when not editing the command
--- line.
---
---- @param pos any
+--- @param pos integer
--- @return any
function vim.fn.setcmdpos(pos) end
--- @param lnum integer
--- @param col? integer
---- @param off? any
+--- @param off? integer
--- @return any
function vim.fn.setcursorcharpos(lnum, col, off) end
@@ -7896,7 +8005,7 @@ function vim.fn.setcursorcharpos(lnum, col, off) end
--- call cursor(4, 3)
--- <positions the cursor on the first character '여'.
---
---- @param list any
+--- @param list integer[]
--- @return any
function vim.fn.setcursorcharpos(list) end
@@ -7907,7 +8016,7 @@ function vim.fn.setcursorcharpos(list) end
--- See also |expr-env|.
---
--- @param name string
---- @param val any
+--- @param val string
--- @return any
function vim.fn.setenv(name, val) end
@@ -7981,8 +8090,8 @@ function vim.fn.setline(lnum, text) end
---
--- @param nr integer
--- @param list any
---- @param action? any
---- @param what? any
+--- @param action? string
+--- @param what? table
--- @return any
function vim.fn.setloclist(nr, list, action, what) end
@@ -7994,7 +8103,7 @@ function vim.fn.setloclist(nr, list, action, what) end
--- window ID instead of the current window.
---
--- @param list any
---- @param win? any
+--- @param win? integer
--- @return any
function vim.fn.setmatches(list, win) end
@@ -8046,8 +8155,8 @@ function vim.fn.setmatches(list, win) end
--- also set the preferred column. Also see the "curswant" key in
--- |winrestview()|.
---
---- @param expr any
---- @param list any
+--- @param expr string
+--- @param list integer[]
--- @return any
function vim.fn.setpos(expr, list) end
@@ -8164,9 +8273,9 @@ function vim.fn.setpos(expr, list) end
--- independent of the 'errorformat' setting. Use a command like
--- `:cc 1` to jump to the first position.
---
---- @param list any
---- @param action? any
---- @param what? any
+--- @param list any[]
+--- @param action? string
+--- @param what? table
--- @return any
function vim.fn.setqflist(list, action, what) end
@@ -8301,7 +8410,7 @@ function vim.fn.settabwinvar(tabnr, winnr, varname, val) end
---
--- @param nr integer
--- @param dict any
---- @param action? any
+--- @param action? string
--- @return any
function vim.fn.settagstack(nr, dict, action) end
@@ -8355,7 +8464,7 @@ function vim.fn.sha256(string) end
--- <See also |::S|.
---
--- @param string string
---- @param special? any
+--- @param special? boolean
--- @return any
function vim.fn.shellescape(string, special) end
@@ -8400,6 +8509,7 @@ function vim.fn.sign_define(name, dict) end
--- icon full path to the bitmap file for the sign.
--- linehl highlight group used for the whole line the
--- sign is placed in.
+--- priority default priority value of the sign
--- numhl highlight group used for the line number where
--- the sign is placed.
--- text text that is displayed when there is no icon
@@ -8450,6 +8560,7 @@ function vim.fn.sign_define(list) end
--- linehl highlight group used for the whole line the
--- sign is placed in; not present if not set.
--- name name of the sign
+--- priority default priority value of the sign
--- numhl highlight group used for the line number where
--- the sign is placed; not present if not set.
--- text text that is displayed when there is no icon
@@ -8536,7 +8647,7 @@ function vim.fn.sign_getdefined(name) end
--- echo sign_getplaced()
--- <
---
---- @param buf? any
+--- @param buf? integer|string
--- @param dict? vim.fn.sign_getplaced.dict
--- @return vim.fn.sign_getplaced.ret.item[]
function vim.fn.sign_getplaced(buf, dict) end
@@ -8610,10 +8721,10 @@ function vim.fn.sign_jump(id, group, buf) end
--- \ {'lnum' : 40, 'priority' : 90})
--- <
---
---- @param id any
---- @param group any
+--- @param id integer
+--- @param group string
--- @param name string
---- @param buf any
+--- @param buf integer|string
--- @param dict? vim.fn.sign_place.dict
--- @return integer
function vim.fn.sign_place(id, group, name, buf, dict) end
@@ -8641,7 +8752,8 @@ function vim.fn.sign_place(id, group, name, buf, dict) end
--- priority Priority of the sign. When multiple signs are
--- placed on a line, the sign with the highest
--- priority is used. If not specified, the
---- default value of 10 is used. See
+--- default value of 10 is used, unless specified
+--- otherwise by the sign definition. See
--- |sign-priority| for more information.
---
--- If {id} refers to an existing sign, then the existing sign is
@@ -8804,7 +8916,7 @@ function vim.fn.sign_unplacelist(list) end
--- directory. In order to resolve all the involved symbolic
--- links before simplifying the path name, use |resolve()|.
---
---- @param filename any
+--- @param filename string
--- @return any
function vim.fn.simplify(filename) end
@@ -8817,7 +8929,7 @@ function vim.fn.simplify(filename) end
--- echo sin(-4.01)
--- < 0.763301
---
---- @param expr any
+--- @param expr number
--- @return any
function vim.fn.sin(expr) end
@@ -8831,7 +8943,7 @@ function vim.fn.sin(expr) end
--- echo sinh(-0.9)
--- < -1.026517
---
---- @param expr any
+--- @param expr number
--- @return any
function vim.fn.sinh(expr) end
@@ -8845,8 +8957,8 @@ function vim.fn.sinh(expr) end
--- Returns an empty value if {start} or {end} are invalid.
---
--- @param expr any
---- @param start any
---- @param end_? any
+--- @param start integer
+--- @param end_? integer
--- @return any
function vim.fn.slice(expr, start, end_) end
@@ -8875,7 +8987,7 @@ function vim.fn.slice(expr, start, end_) end
--- - 0 on invalid arguments or connection failure.
---
--- @param mode string
---- @param address any
+--- @param address string
--- @param opts? table
--- @return any
function vim.fn.sockconnect(mode, address, opts) end
@@ -8953,7 +9065,7 @@ function vim.fn.sockconnect(mode, address, opts) end
--- <
---
--- @param list any
---- @param how? any
+--- @param how? string|function
--- @param dict? any
--- @return any
function vim.fn.sort(list, how, dict) end
@@ -8965,7 +9077,7 @@ function vim.fn.sort(list, how, dict) end
--- This can be used for making spelling suggestions. Note that
--- the method can be quite slow.
---
---- @param word any
+--- @param word string
--- @return any
function vim.fn.soundfold(word) end
@@ -8992,7 +9104,7 @@ function vim.fn.soundfold(word) end
--- The spelling information for the current window and the value
--- of 'spelllang' are used.
---
---- @param sentence? any
+--- @param sentence? string
--- @return any
function vim.fn.spellbadword(sentence) end
@@ -9016,15 +9128,15 @@ function vim.fn.spellbadword(sentence) end
--- The spelling information for the current window is used. The
--- values of 'spelllang' and 'spellsuggest' are used.
---
---- @param word any
---- @param max? any
---- @param capital? any
+--- @param word string
+--- @param max? integer
+--- @param capital? boolean
--- @return any
function vim.fn.spellsuggest(word, max, capital) end
--- Make a |List| out of {string}. When {pattern} is omitted or
---- empty each white-separated sequence of characters becomes an
---- item.
+--- empty each white space separated sequence of characters
+--- becomes an item.
--- Otherwise the string is split where {pattern} matches,
--- removing the matched characters. 'ignorecase' is not used
--- here, add \c to ignore case. |/\c|
@@ -9047,8 +9159,8 @@ function vim.fn.spellsuggest(word, max, capital) end
--- <The opposite function is |join()|.
---
--- @param string string
---- @param pattern? any
---- @param keepempty? any
+--- @param pattern? string
+--- @param keepempty? boolean
--- @return any
function vim.fn.split(string, pattern, keepempty) end
@@ -9064,7 +9176,7 @@ function vim.fn.split(string, pattern, keepempty) end
--- < str2float("nan")
--- NaN may be different, it depends on system libraries.
---
---- @param expr any
+--- @param expr number
--- @return any
function vim.fn.sqrt(expr) end
@@ -9082,7 +9194,7 @@ function vim.fn.sqrt(expr) end
--- echo rand(seed)
--- <
---
---- @param expr? any
+--- @param expr? number
--- @return any
function vim.fn.srand(expr) end
@@ -9187,7 +9299,7 @@ function vim.fn.stdpath(what) end
--- Returns 0.0 if the conversion fails.
---
--- @param string string
---- @param quoted? any
+--- @param quoted? boolean
--- @return any
function vim.fn.str2float(string, quoted) end
@@ -9203,7 +9315,7 @@ function vim.fn.str2float(string, quoted) end
--- echo str2list("á") " returns [97, 769]
---
--- @param string string
---- @param utf8? any
+--- @param utf8? boolean
--- @return any
function vim.fn.str2list(string, utf8) end
@@ -9226,7 +9338,7 @@ function vim.fn.str2list(string, utf8) end
--- Returns 0 if {string} is empty or on error.
---
--- @param string string
---- @param base? any
+--- @param base? integer
--- @return any
function vim.fn.str2nr(string, base) end
@@ -9257,10 +9369,10 @@ function vim.fn.strcharlen(string) end
---
--- Returns an empty string on error.
---
---- @param src any
---- @param start any
---- @param len? any
---- @param skipcc? any
+--- @param src string
+--- @param start integer
+--- @param len? integer
+--- @param skipcc? boolean
--- @return any
function vim.fn.strcharpart(src, start, len, skipcc) end
@@ -9293,7 +9405,7 @@ function vim.fn.strcharpart(src, start, len, skipcc) end
--- <
---
--- @param string string
---- @param skipcc? any
+--- @param skipcc? boolean
--- @return integer
function vim.fn.strchars(string, skipcc) end
@@ -9331,8 +9443,8 @@ function vim.fn.strdisplaywidth(string, col) end
--- echo strftime("%c", getftime("file.c"))
--- " Show mod time of file.c.
---
---- @param format any
---- @param time? any
+--- @param format string
+--- @param time? number
--- @return string
function vim.fn.strftime(format, time) end
@@ -9796,7 +9908,7 @@ function vim.fn.synIDtrans(synID) end
---
--- @param lnum integer
--- @param col integer
---- @return {[1]: integer, [2]: string, [3]: integer}
+--- @return [integer, string, integer]
function vim.fn.synconcealed(lnum, col) end
--- Return a |List|, which is the stack of syntax items at the
@@ -9906,7 +10018,7 @@ function vim.fn.systemlist(cmd, input, keepempty) end
--- endfor
--- <Note that a buffer may appear in more than one window.
---
---- @param arg? any
+--- @param arg? integer
--- @return any
function vim.fn.tabpagebuflist(arg) end
@@ -10049,7 +10161,7 @@ function vim.fn.tempname() end
--- except $TERM is set to "xterm-256color". Full behavior is
--- described in |terminal|.
---
---- @param cmd any
+--- @param cmd string|string[]
--- @param opts? table
--- @return any
function vim.fn.termopen(cmd, opts) end
@@ -10068,7 +10180,7 @@ function vim.fn.termopen(cmd, opts) end
--- -1 means forever
--- "callback" the callback
---
---- @param id? any
+--- @param id? integer
--- @return any
function vim.fn.timer_info(id) end
@@ -10084,8 +10196,8 @@ function vim.fn.timer_info(id) end
--- String, then the timer is paused, otherwise it is unpaused.
--- See |non-zero-arg|.
---
---- @param timer any
---- @param paused any
+--- @param timer integer
+--- @param paused boolean
--- @return any
function vim.fn.timer_pause(timer, paused) end
@@ -10118,8 +10230,8 @@ function vim.fn.timer_pause(timer, paused) end
--- \ {'repeat': 3})
--- <This invokes MyHandler() three times at 500 msec intervals.
---
---- @param time any
---- @param callback any
+--- @param time number
+--- @param callback string|function
--- @param options? table
--- @return any
function vim.fn.timer_start(time, callback, options) end
@@ -10128,7 +10240,7 @@ function vim.fn.timer_start(time, callback, options) end
--- {timer} is an ID returned by timer_start(), thus it must be a
--- Number. If {timer} does not exist there is no error.
---
---- @param timer any
+--- @param timer integer
--- @return any
function vim.fn.timer_stop(timer) end
@@ -10143,7 +10255,7 @@ function vim.fn.timer_stopall() end
--- characters turned into lowercase (just like applying |gu| to
--- the string). Returns an empty string on error.
---
---- @param expr any
+--- @param expr string
--- @return string
function vim.fn.tolower(expr) end
@@ -10151,7 +10263,7 @@ function vim.fn.tolower(expr) end
--- characters turned into uppercase (just like applying |gU| to
--- the string). Returns an empty string on error.
---
---- @param expr any
+--- @param expr string
--- @return string
function vim.fn.toupper(expr) end
@@ -10203,7 +10315,7 @@ function vim.fn.tr(src, fromstr, tostr) end
--- echo trim(" vim ", " ", 2)
--- <returns " vim"
---
---- @param text any
+--- @param text string
--- @param mask? string
--- @param dir? 0|1|2
--- @return string
@@ -10221,7 +10333,7 @@ function vim.fn.trim(text, mask, dir) end
--- echo trunc(4.0)
--- < 4.0
---
---- @param expr any
+--- @param expr number
--- @return integer
function vim.fn.trunc(expr) end
@@ -10250,6 +10362,7 @@ function vim.fn.trunc(expr) end
--- if myvar is v:null | endif
--- <To check if the v:t_ variables exist use this: >vim
--- if exists('v:t_number') | endif
+--- <
---
--- @param expr any
--- @return integer
@@ -10313,7 +10426,7 @@ function vim.fn.undofile(name) end
--- item.
---
--- @param buf? integer|string
---- @return any
+--- @return vim.fn.undotree.ret
function vim.fn.undotree(buf) end
--- Remove second and succeeding copies of repeated adjacent
@@ -10360,8 +10473,8 @@ function vim.fn.uniq(list, func, dict) end
---
--- @param string string
--- @param idx integer
---- @param countcc? any
---- @param charidx? any
+--- @param countcc? boolean
+--- @param charidx? boolean
--- @return integer
function vim.fn.utf16idx(string, idx, countcc, charidx) end
@@ -10382,7 +10495,9 @@ function vim.fn.values(dict) end
--- set to 8, it returns 8. |conceal| is ignored.
--- For the byte position use |col()|.
---
---- For the use of {expr} see |col()|.
+--- For the use of {expr} see |getpos()| and |col()|.
+--- When {expr} is "$", it means the end of the cursor line, so
+--- the result is the number of cells in the cursor line plus one.
---
--- When 'virtualedit' is used {expr} can be [lnum, col, off],
--- where "off" is the offset in screen columns from the start of
@@ -10392,18 +10507,6 @@ function vim.fn.values(dict) end
--- beyond the end of the line can be returned. Also see
--- |'virtualedit'|
---
---- The accepted positions are:
---- . the cursor position
---- $ the end of the cursor line (the result is the
---- number of displayed characters in the cursor line
---- plus one)
---- 'x position of mark x (if the mark is not set, 0 is
---- returned)
---- v In Visual mode: the start of the Visual area (the
---- cursor is the end). When not in Visual mode
---- returns the cursor position. Differs from |'<| in
---- that it's updated right away.
----
--- If {list} is present and non-zero then virtcol() returns a
--- List with the first and last screen position occupied by the
--- character.
@@ -10422,13 +10525,16 @@ function vim.fn.values(dict) end
--- " With text " there", with 't at 'h':
---
--- echo virtcol("'t") " returns 6
---- <The first column is 1. 0 or [0, 0] is returned for an error.
+--- <
+--- The first column is 1. 0 or [0, 0] is returned for an error.
+---
--- A more advanced example that echoes the maximum length of
--- all lines: >vim
--- echo max(map(range(1, line('$')), "virtcol([v:val, '$'])"))
+--- <
---
---- @param expr any
---- @param list? any
+--- @param expr string|integer[]
+--- @param list? boolean
--- @param winid? integer
--- @return any
function vim.fn.virtcol(expr, list, winid) end
@@ -10477,7 +10583,7 @@ function vim.fn.virtcol2col(winid, lnum, col) end
--- a non-empty String, then the Visual mode will be cleared and
--- the old value is returned. See |non-zero-arg|.
---
---- @param expr? any
+--- @param expr? boolean
--- @return any
function vim.fn.visualmode(expr) end
@@ -10498,7 +10604,7 @@ function vim.fn.visualmode(expr) end
---
--- @param timeout integer
--- @param condition any
---- @param interval? any
+--- @param interval? number
--- @return any
function vim.fn.wait(timeout, condition, interval) end
@@ -10528,8 +10634,8 @@ function vim.fn.wildmenumode() end
--- When window {id} does not exist then no error is given and
--- an empty string is returned.
---
---- @param id any
---- @param command any
+--- @param id integer
+--- @param command string
--- @param silent? boolean
--- @return any
function vim.fn.win_execute(id, command, silent) end
@@ -10537,7 +10643,7 @@ function vim.fn.win_execute(id, command, silent) end
--- Returns a |List| with |window-ID|s for windows that contain
--- buffer {bufnr}. When there is none the list is empty.
---
---- @param bufnr any
+--- @param bufnr integer
--- @return integer[]
function vim.fn.win_findbuf(bufnr) end
@@ -10549,8 +10655,8 @@ function vim.fn.win_findbuf(bufnr) end
--- number {tab}. The first tab has number one.
--- Return zero if the window cannot be found.
---
---- @param win? any
---- @param tab? any
+--- @param win? integer
+--- @param tab? integer
--- @return integer
function vim.fn.win_getid(win, tab) end
@@ -10579,7 +10685,7 @@ function vim.fn.win_gettype(nr) end
--- tabpage.
--- Return TRUE if successful, FALSE if the window cannot be found.
---
---- @param expr any
+--- @param expr integer
--- @return 0|1
function vim.fn.win_gotoid(expr) end
@@ -10587,14 +10693,14 @@ function vim.fn.win_gotoid(expr) end
--- with ID {expr}: [tabnr, winnr].
--- Return [0, 0] if the window cannot be found.
---
---- @param expr any
+--- @param expr integer
--- @return any
function vim.fn.win_id2tabwin(expr) end
--- Return the window number of window with ID {expr}.
--- Return 0 if the window cannot be found in the current tabpage.
---
---- @param expr any
+--- @param expr integer
--- @return any
function vim.fn.win_id2win(expr) end
@@ -10613,7 +10719,7 @@ function vim.fn.win_id2win(expr) end
--- Only works for the current tab page. *E1308*
---
--- @param nr integer
---- @param offset any
+--- @param offset integer
--- @return any
function vim.fn.win_move_separator(nr, offset) end
@@ -10629,7 +10735,7 @@ function vim.fn.win_move_separator(nr, offset) end
--- Only works for the current tab page.
---
--- @param nr integer
---- @param offset any
+--- @param offset integer
--- @return any
function vim.fn.win_move_statusline(nr, offset) end
@@ -10664,7 +10770,7 @@ function vim.fn.win_screenpos(nr) end
--- 'splitright' are used.
---
--- @param nr integer
---- @param target any
+--- @param target integer
--- @param options? table
--- @return any
function vim.fn.win_splitmove(nr, target, options) end
@@ -10706,6 +10812,7 @@ function vim.fn.windowsversion() end
--- This excludes any window toolbar line.
--- Examples: >vim
--- echo "The current window has " .. winheight(0) .. " lines."
+--- <
---
--- @param nr integer
--- @return integer
@@ -10789,8 +10896,9 @@ function vim.fn.winline() end
--- let window_count = winnr('$')
--- let prev_window = winnr('#')
--- let wnum = winnr('3k')
+--- <
---
---- @param arg? any
+--- @param arg? string|integer
--- @return any
function vim.fn.winnr(arg) end
@@ -10939,6 +11047,7 @@ function vim.fn.wordcount() end
--- To copy a file byte for byte: >vim
--- let fl = readfile("foo", "b")
--- call writefile(fl, "foocopy", "b")
+--- <
---
--- @param object any
--- @param fname string
@@ -10953,7 +11062,7 @@ function vim.fn.writefile(object, fname, flags) end
--- let bits = xor(bits, 0x80)
--- <
---
---- @param expr any
---- @param expr1 any
+--- @param expr number
+--- @param expr1 number
--- @return any
function vim.fn.xor(expr, expr1) end
diff --git a/runtime/lua/vim/_options.lua b/runtime/lua/vim/_options.lua
index b41e298dd7..a61fa61256 100644
--- a/runtime/lua/vim/_options.lua
+++ b/runtime/lua/vim/_options.lua
@@ -95,7 +95,6 @@
local api = vim.api
-- TODO(tjdevries): Improve option metadata so that this doesn't have to be hardcoded.
--- Can be done in a separate PR.
local key_value_options = {
fillchars = true,
fcs = true,
@@ -175,6 +174,11 @@ local function new_buf_opt_accessor(bufnr)
end
local function new_win_opt_accessor(winid, bufnr)
+ -- TODO(lewis6991): allow passing both buf and win to nvim_get_option_value
+ if bufnr ~= nil and bufnr ~= 0 then
+ error('only bufnr=0 is supported')
+ end
+
return setmetatable({}, {
__index = function(_, k)
if bufnr == nil and type(k) == 'number' then
@@ -185,11 +189,6 @@ local function new_win_opt_accessor(winid, bufnr)
end
end
- if bufnr ~= nil and bufnr ~= 0 then
- error('only bufnr=0 is supported')
- end
-
- -- TODO(lewis6991): allow passing both buf and win to nvim_get_option_value
return api.nvim_get_option_value(k, {
scope = bufnr and 'local' or nil,
win = winid or 0,
@@ -197,7 +196,6 @@ local function new_win_opt_accessor(winid, bufnr)
end,
__newindex = function(_, k, v)
- -- TODO(lewis6991): allow passing both buf and win to nvim_set_option_value
return api.nvim_set_option_value(k, v, {
scope = bufnr and 'local' or nil,
win = winid or 0,
diff --git a/runtime/lua/vim/_watch.lua b/runtime/lua/vim/_watch.lua
index 02b3f536c2..11f6742941 100644
--- a/runtime/lua/vim/_watch.lua
+++ b/runtime/lua/vim/_watch.lua
@@ -30,6 +30,8 @@ M.FileChangeType = {
--- @class vim._watch.watch.Opts : vim._watch.Opts
--- @field uvflags? uv.fs_event_start.flags
+--- Decides if `path` should be skipped.
+---
--- @param path string
--- @param opts? vim._watch.Opts
local function skip(path, opts)
@@ -69,7 +71,7 @@ function M.watch(path, opts, callback)
local uvflags = opts and opts.uvflags or {}
local handle = assert(uv.new_fs_event())
- local _, start_err = handle:start(path, uvflags, function(err, filename, events)
+ local _, start_err, start_errname = handle:start(path, uvflags, function(err, filename, events)
assert(not err, err)
local fullpath = path
if filename then
@@ -96,7 +98,15 @@ function M.watch(path, opts, callback)
callback(fullpath, change_type)
end)
- assert(not start_err, start_err)
+ if start_err then
+ if start_errname == 'ENOENT' then
+ -- Server may send "workspace/didChangeWatchedFiles" with nonexistent `baseUri` path.
+ -- This is mostly a placeholder until we have `nvim_log` API.
+ vim.notify_once(('watch.watch: %s'):format(start_err), vim.log.levels.INFO)
+ end
+ -- TODO(justinmk): log important errors once we have `nvim_log` API.
+ return function() end
+ end
return function()
local _, stop_err = handle:stop()
@@ -193,7 +203,18 @@ function M.watchdirs(path, opts, callback)
local root_handle = assert(uv.new_fs_event())
handles[path] = root_handle
- root_handle:start(path, {}, create_on_change(path))
+ local _, start_err, start_errname = root_handle:start(path, {}, create_on_change(path))
+
+ if start_err then
+ if start_errname == 'ENOENT' then
+ -- Server may send "workspace/didChangeWatchedFiles" with nonexistent `baseUri` path.
+ -- This is mostly a placeholder until we have `nvim_log` API.
+ vim.notify_once(('watch.watchdirs: %s'):format(start_err), vim.log.levels.INFO)
+ end
+ -- TODO(justinmk): log important errors once we have `nvim_log` API.
+
+ -- Continue. vim.fs.dir() will return nothing, so the code below is harmless.
+ end
--- "640K ought to be enough for anyone"
--- Who has folders this deep?
@@ -227,11 +248,12 @@ end
--- @param data string
--- @param opts vim._watch.Opts?
--- @param callback vim._watch.Callback
-local function fswatch_output_handler(data, opts, callback)
+local function on_inotifywait_output(data, opts, callback)
local d = vim.split(data, '%s+')
-- only consider the last reported event
- local fullpath, event = d[1], d[#d]
+ local path, event, file = d[1], d[2], d[#d]
+ local fullpath = vim.fs.joinpath(path, file)
if skip(fullpath, opts) then
return
@@ -240,20 +262,16 @@ local function fswatch_output_handler(data, opts, callback)
--- @type integer
local change_type
- if event == 'Created' then
+ if event == 'CREATE' then
change_type = M.FileChangeType.Created
- elseif event == 'Removed' then
+ elseif event == 'DELETE' then
change_type = M.FileChangeType.Deleted
- elseif event == 'Updated' then
+ elseif event == 'MODIFY' then
change_type = M.FileChangeType.Changed
- elseif event == 'Renamed' then
- local _, staterr, staterrname = uv.fs_stat(fullpath)
- if staterrname == 'ENOENT' then
- change_type = M.FileChangeType.Deleted
- else
- assert(not staterr, staterr)
- change_type = M.FileChangeType.Created
- end
+ elseif event == 'MOVED_FROM' then
+ change_type = M.FileChangeType.Deleted
+ elseif event == 'MOVED_TO' then
+ change_type = M.FileChangeType.Created
end
if change_type then
@@ -265,24 +283,22 @@ end
--- @param opts vim._watch.Opts?
--- @param callback vim._watch.Callback Callback for new events
--- @return fun() cancel Stops the watcher
-function M.fswatch(path, opts, callback)
- -- debounce isn't the same as latency but close enough
- local latency = 0.5 -- seconds
- if opts and opts.debounce then
- latency = opts.debounce / 1000
- end
-
+function M.inotify(path, opts, callback)
local obj = vim.system({
- 'fswatch',
- '--event=Created',
- '--event=Removed',
- '--event=Updated',
- '--event=Renamed',
- '--event-flags',
+ 'inotifywait',
+ '--quiet', -- suppress startup messages
+ '--no-dereference', -- don't follow symlinks
+ '--monitor', -- keep listening for events forever
'--recursive',
- '--latency=' .. tostring(latency),
- '--exclude',
- '/.git/',
+ '--event',
+ 'create',
+ '--event',
+ 'delete',
+ '--event',
+ 'modify',
+ '--event',
+ 'move',
+ string.format('@%s/.git', path), -- ignore git directory
path,
}, {
stderr = function(err, data)
@@ -292,11 +308,11 @@ function M.fswatch(path, opts, callback)
if data and #vim.trim(data) > 0 then
vim.schedule(function()
- if vim.fn.has('linux') == 1 and vim.startswith(data, 'Event queue overflow') then
- data = 'inotify(7) limit reached, see :h fswatch-limitations for more info.'
+ if vim.fn.has('linux') == 1 and vim.startswith(data, 'Failed to watch') then
+ data = 'inotify(7) limit reached, see :h inotify-limitations for more info.'
end
- vim.notify('fswatch: ' .. data, vim.log.levels.ERROR)
+ vim.notify('inotify: ' .. data, vim.log.levels.ERROR)
end)
end
end,
@@ -306,7 +322,7 @@ function M.fswatch(path, opts, callback)
end
for line in vim.gsplit(data or '', '\n', { plain = true, trimempty = true }) do
- fswatch_output_handler(line, opts, callback)
+ on_inotifywait_output(line, opts, callback)
end
end,
-- --latency is locale dependent but tostring() isn't and will always have '.' as decimal point.
diff --git a/runtime/lua/vim/deprecated/health.lua b/runtime/lua/vim/deprecated/health.lua
index 0f6b1f578c..eed889d90a 100644
--- a/runtime/lua/vim/deprecated/health.lua
+++ b/runtime/lua/vim/deprecated/health.lua
@@ -1,7 +1,7 @@
local M = {}
local health = vim.health
-local deprecated = {}
+local deprecated = {} ---@type [string, table, string][]
function M.check()
if next(deprecated) == nil then
diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index 348204abb7..ef00a1fa51 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -42,7 +42,7 @@ local M = {}
---
--- @field namespace? integer
---- Each of the configuration options below accepts one of the following:
+--- Many of the configuration options below accept one of the following:
--- - `false`: Disable this feature
--- - `true`: Enable this feature, use default settings.
--- - `table`: Enable this feature with overrides. Use an empty table to use default values.
@@ -78,6 +78,9 @@ local M = {}
--- - {reverse}? (boolean) Reverse sort order
--- (default: `false`)
--- @field severity_sort? boolean|{reverse?:boolean}
+---
+--- Default values for |vim.diagnostic.jump()|. See |vim.diagnostic.Opts.Jump|.
+--- @field jump? vim.diagnostic.Opts.Jump
--- @class (private) vim.diagnostic.OptsResolved
--- @field float vim.diagnostic.Opts.Float
@@ -105,7 +108,7 @@ local M = {}
--- If {scope} is "line" or "cursor", use this position rather than the cursor
--- position. If a number, interpreted as a line number; otherwise, a
--- (row, col) tuple.
---- @field pos? integer|{[1]:integer,[2]:integer}
+--- @field pos? integer|[integer,integer]
---
--- Sort diagnostics by severity.
--- Overrides the setting from |vim.diagnostic.config()|.
@@ -119,7 +122,7 @@ local M = {}
--- String to use as the header for the floating window. If a table, it is
--- interpreted as a `[text, hl_group]` tuple.
--- Overrides the setting from |vim.diagnostic.config()|.
---- @field header? string|{[1]:string,[2]:any}
+--- @field header? string|[string,any]
---
--- Include the diagnostic source in the message.
--- Use "if_many" to only show sources if there is more than one source of
@@ -200,7 +203,7 @@ local M = {}
--- @field hl_mode? 'replace'|'combine'|'blend'
---
--- See |nvim_buf_set_extmark()|.
---- @field virt_text? {[1]:string,[2]:any}[]
+--- @field virt_text? [string,any][]
---
--- See |nvim_buf_set_extmark()|.
--- @field virt_text_pos? 'eol'|'overlay'|'right_align'|'inline'
@@ -241,6 +244,22 @@ local M = {}
--- whole line the sign is placed in.
--- @field linehl? table<vim.diagnostic.Severity,string>
+--- @class vim.diagnostic.Opts.Jump
+---
+--- Default value of the {float} parameter of |vim.diagnostic.jump()|.
+--- (default: false)
+--- @field float? boolean|vim.diagnostic.Opts.Float
+---
+--- Default value of the {wrap} parameter of |vim.diagnostic.jump()|.
+--- (default: true)
+--- @field wrap? boolean
+---
+--- Default value of the {severity} parameter of |vim.diagnostic.jump()|.
+--- @field severity? vim.diagnostic.SeverityFilter
+---
+--- Default value of the {_highest} parameter of |vim.diagnostic.jump()|.
+--- @field package _highest? boolean
+
-- TODO: inherit from `vim.diagnostic.Opts`, implement its fields.
--- Optional filters |kwargs|, or `nil` for all.
--- @class vim.diagnostic.Filter
@@ -284,6 +303,13 @@ local global_diagnostic_options = {
float = true,
update_in_insert = false,
severity_sort = false,
+ jump = {
+ -- Do not show floating window
+ float = false,
+
+ -- Wrap around buffer
+ wrap = true,
+ },
}
--- @class (private) vim.diagnostic.Handler
@@ -835,21 +861,36 @@ local function filter_highest(diagnostics)
end
end
---- @param position {[1]: integer, [2]: integer}
--- @param search_forward boolean
---- @param bufnr integer
---- @param opts vim.diagnostic.GotoOpts
---- @param namespace integer[]|integer
+--- @param opts vim.diagnostic.JumpOpts?
--- @return vim.Diagnostic?
-local function next_diagnostic(position, search_forward, bufnr, opts, namespace)
+local function next_diagnostic(search_forward, opts)
+ opts = opts or {}
+
+ -- Support deprecated win_id alias
+ if opts.win_id then
+ vim.deprecate('opts.win_id', 'opts.winid', '0.13')
+ opts.winid = opts.win_id
+ opts.win_id = nil
+ end
+
+ -- Support deprecated cursor_position alias
+ if opts.cursor_position then
+ vim.deprecate('opts.cursor_position', 'opts.pos', '0.13')
+ opts.pos = opts.cursor_position
+ opts.cursor_position = nil
+ end
+
+ local winid = opts.winid or api.nvim_get_current_win()
+ local bufnr = api.nvim_win_get_buf(winid)
+ local position = opts.pos or api.nvim_win_get_cursor(winid)
+
+ -- Adjust row to be 0-indexed
position[1] = position[1] - 1
- bufnr = get_bufnr(bufnr)
- local wrap = if_nil(opts.wrap, true)
- local get_opts = vim.deepcopy(opts)
- get_opts.namespace = get_opts.namespace or namespace
+ local wrap = if_nil(opts.wrap, true)
- local diagnostics = get_diagnostics(bufnr, get_opts, true)
+ local diagnostics = get_diagnostics(bufnr, opts, true)
if opts._highest then
filter_highest(diagnostics)
@@ -902,32 +943,40 @@ local function next_diagnostic(position, search_forward, bufnr, opts, namespace)
end
end
---- @param opts vim.diagnostic.GotoOpts?
---- @param pos {[1]:integer,[2]:integer}|false
-local function diagnostic_move_pos(opts, pos)
- opts = opts or {}
-
- local float = if_nil(opts.float, true)
- local win_id = opts.win_id or api.nvim_get_current_win()
-
- if not pos then
+--- Move the cursor to the given diagnostic.
+---
+--- @param diagnostic vim.Diagnostic?
+--- @param opts vim.diagnostic.JumpOpts?
+local function goto_diagnostic(diagnostic, opts)
+ if not diagnostic then
api.nvim_echo({ { 'No more valid diagnostics to move to', 'WarningMsg' } }, true, {})
return
end
- api.nvim_win_call(win_id, function()
+ opts = opts or {}
+
+ -- Support deprecated win_id alias
+ if opts.win_id then
+ vim.deprecate('opts.win_id', 'opts.winid', '0.13')
+ opts.winid = opts.win_id
+ opts.win_id = nil
+ end
+
+ local winid = opts.winid or api.nvim_get_current_win()
+
+ vim._with({ win = winid }, function()
-- Save position in the window's jumplist
vim.cmd("normal! m'")
- api.nvim_win_set_cursor(win_id, { pos[1] + 1, pos[2] })
+ api.nvim_win_set_cursor(winid, { diagnostic.lnum + 1, diagnostic.col })
-- Open folds under the cursor
vim.cmd('normal! zv')
end)
- if float then
- local float_opts = type(float) == 'table' and float or {}
+ if opts.float then
+ local float_opts = type(opts.float) == 'table' and opts.float or {}
vim.schedule(function()
M.open_float(vim.tbl_extend('keep', float_opts, {
- bufnr = api.nvim_win_get_buf(win_id),
+ bufnr = api.nvim_win_get_buf(winid),
scope = 'cursor',
focus = false,
}))
@@ -1114,24 +1163,24 @@ end
--- Get the previous diagnostic closest to the cursor position.
---
----@param opts? vim.diagnostic.GotoOpts
+---@param opts? vim.diagnostic.JumpOpts
---@return vim.Diagnostic? : Previous diagnostic
function M.get_prev(opts)
- opts = opts or {}
-
- local win_id = opts.win_id or api.nvim_get_current_win()
- local bufnr = api.nvim_win_get_buf(win_id)
- local cursor_position = opts.cursor_position or api.nvim_win_get_cursor(win_id)
-
- return next_diagnostic(cursor_position, false, bufnr, opts, opts.namespace)
+ return next_diagnostic(false, opts)
end
--- Return the position of the previous diagnostic in the current buffer.
---
----@param opts? vim.diagnostic.GotoOpts
+---@param opts? vim.diagnostic.JumpOpts
---@return table|false: Previous diagnostic position as a `(row, col)` tuple
--- or `false` if there is no prior diagnostic.
+---@deprecated
function M.get_prev_pos(opts)
+ vim.deprecate(
+ 'vim.diagnostic.get_prev_pos()',
+ 'access the lnum and col fields from get_prev() instead',
+ '0.13'
+ )
local prev = M.get_prev(opts)
if not prev then
return false
@@ -1141,31 +1190,35 @@ function M.get_prev_pos(opts)
end
--- Move to the previous diagnostic in the current buffer.
----@param opts? vim.diagnostic.GotoOpts
+---@param opts? vim.diagnostic.JumpOpts
+---@deprecated
function M.goto_prev(opts)
- return diagnostic_move_pos(opts, M.get_prev_pos(opts))
+ vim.deprecate('vim.diagnostic.goto_prev()', 'vim.diagnostic.jump()', '0.13')
+ opts = opts or {}
+ opts.float = if_nil(opts.float, true)
+ goto_diagnostic(M.get_prev(opts), opts)
end
--- Get the next diagnostic closest to the cursor position.
---
----@param opts? vim.diagnostic.GotoOpts
+---@param opts? vim.diagnostic.JumpOpts
---@return vim.Diagnostic? : Next diagnostic
function M.get_next(opts)
- opts = opts or {}
-
- local win_id = opts.win_id or api.nvim_get_current_win()
- local bufnr = api.nvim_win_get_buf(win_id)
- local cursor_position = opts.cursor_position or api.nvim_win_get_cursor(win_id)
-
- return next_diagnostic(cursor_position, true, bufnr, opts, opts.namespace)
+ return next_diagnostic(true, opts)
end
--- Return the position of the next diagnostic in the current buffer.
---
----@param opts? vim.diagnostic.GotoOpts
+---@param opts? vim.diagnostic.JumpOpts
---@return table|false : Next diagnostic position as a `(row, col)` tuple or false if no next
--- diagnostic.
+---@deprecated
function M.get_next_pos(opts)
+ vim.deprecate(
+ 'vim.diagnostic.get_next_pos()',
+ 'access the lnum and col fields from get_next() instead',
+ '0.13'
+ )
local next = M.get_next(opts)
if not next then
return false
@@ -1186,13 +1239,23 @@ end
--- See |diagnostic-severity|.
--- @field severity? vim.diagnostic.SeverityFilter
---- Configuration table with the following keys:
---- @class vim.diagnostic.GotoOpts : vim.diagnostic.GetOpts
+--- Configuration table with the keys listed below. Some parameters can have their default values
+--- changed with |vim.diagnostic.config()|.
+--- @class vim.diagnostic.JumpOpts : vim.diagnostic.GetOpts
+---
+--- The diagnostic to jump to. Mutually exclusive with {count}, {namespace},
+--- and {severity}.
+--- @field diagnostic? vim.Diagnostic
---
---- Cursor position as a `(row, col)` tuple.
---- See |nvim_win_get_cursor()|.
---- (default: current cursor position)
---- @field cursor_position? {[1]:integer,[2]:integer}
+--- The number of diagnostics to move by, starting from {pos}. A positive
+--- integer moves forward by {count} diagnostics, while a negative integer moves
+--- backward by {count} diagnostics. Mutually exclusive with {diagnostic}.
+--- @field count? integer
+---
+--- Cursor position as a `(row, col)` tuple. See |nvim_win_get_cursor()|. Used
+--- to find the nearest diagnostic when {count} is used. Only used when {count}
+--- is non-nil. Default is the current cursor position.
+--- @field pos? [integer,integer]
---
--- Whether to loop around file or not. Similar to 'wrapscan'.
--- (default: `true`)
@@ -1209,18 +1272,78 @@ end
--- If a table, pass the table as the {opts} parameter to |vim.diagnostic.open_float()|.
--- Unless overridden, the float will show diagnostics at the new cursor
--- position (as if "cursor" were passed to the "scope" option).
---- (default: `true`)
+--- (default: `false`)
--- @field float? boolean|vim.diagnostic.Opts.Float
---
--- Window ID
--- (default: `0`)
---- @field win_id? integer
+--- @field winid? integer
+
+--- Move to a diagnostic.
+---
+--- @param opts vim.diagnostic.JumpOpts
+--- @return vim.Diagnostic? # The diagnostic that was moved to.
+function M.jump(opts)
+ vim.validate('opts', opts, 'table')
+
+ -- One of "diagnostic" or "count" must be provided
+ assert(
+ opts.diagnostic or opts.count,
+ 'One of "diagnostic" or "count" must be specified in the options to vim.diagnostic.jump()'
+ )
+
+ -- Apply configuration options from vim.diagnostic.config()
+ opts = vim.tbl_deep_extend('keep', opts, global_diagnostic_options.jump)
+
+ if opts.diagnostic then
+ goto_diagnostic(opts.diagnostic, opts)
+ return opts.diagnostic
+ end
+
+ local count = opts.count
+ if count == 0 then
+ return nil
+ end
+
+ -- Support deprecated cursor_position alias
+ if opts.cursor_position then
+ vim.deprecate('opts.cursor_position', 'opts.pos', '0.13')
+ opts.pos = opts.cursor_position
+ opts.cursor_position = nil
+ end
+
+ local diag = nil
+ while count ~= 0 do
+ local next = next_diagnostic(count > 0, opts)
+ if not next then
+ break
+ end
+
+ -- Update cursor position
+ opts.pos = { next.lnum + 1, next.col }
+
+ if count > 0 then
+ count = count - 1
+ else
+ count = count + 1
+ end
+ diag = next
+ end
+
+ goto_diagnostic(diag, opts)
+
+ return diag
+end
--- Move to the next diagnostic.
---
----@param opts? vim.diagnostic.GotoOpts
+---@param opts? vim.diagnostic.JumpOpts
+---@deprecated
function M.goto_next(opts)
- diagnostic_move_pos(opts, M.get_next_pos(opts))
+ vim.deprecate('vim.diagnostic.goto_next()', 'vim.diagnostic.jump()', '0.13')
+ opts = opts or {}
+ opts.float = if_nil(opts.float, true)
+ goto_diagnostic(M.get_next(opts), opts)
end
M.handlers.signs = {
@@ -1239,6 +1362,10 @@ M.handlers.signs = {
bufnr = get_bufnr(bufnr)
opts = opts or {}
+ if not api.nvim_buf_is_loaded(bufnr) then
+ return
+ end
+
if opts.signs and opts.signs.severity then
diagnostics = filter_by_severity(opts.signs.severity, diagnostics)
end
@@ -1321,8 +1448,10 @@ M.handlers.signs = {
local numhl = opts.signs.numhl or {}
local linehl = opts.signs.linehl or {}
+ local line_count = api.nvim_buf_line_count(bufnr)
+
for _, diagnostic in ipairs(diagnostics) do
- if api.nvim_buf_is_loaded(diagnostic.bufnr) then
+ if diagnostic.lnum <= line_count then
api.nvim_buf_set_extmark(bufnr, ns.user_data.sign_ns, diagnostic.lnum, 0, {
sign_text = text[diagnostic.severity] or text[M.severity[diagnostic.severity]] or 'U',
sign_hl_group = sign_highlight_map[diagnostic.severity],
@@ -1688,7 +1817,7 @@ end
---
---@param opts vim.diagnostic.Opts.Float?
---@return integer? float_bufnr
----@return integer? win_id
+---@return integer? winid
function M.open_float(opts, ...)
-- Support old (bufnr, opts) signature
local bufnr --- @type integer?
@@ -1739,16 +1868,19 @@ function M.open_float(opts, ...)
if scope == 'line' then
--- @param d vim.Diagnostic
diagnostics = vim.tbl_filter(function(d)
- return lnum >= d.lnum and lnum <= d.end_lnum
+ return lnum >= d.lnum
+ and lnum <= d.end_lnum
+ and (d.lnum == d.end_lnum or lnum ~= d.end_lnum or d.end_col ~= 0)
end, diagnostics)
elseif scope == 'cursor' then
- -- LSP servers can send diagnostics with `end_col` past the length of the line
+ -- If `col` is past the end of the line, show if the cursor is on the last char in the line
local line_length = #api.nvim_buf_get_lines(bufnr, lnum, lnum + 1, true)[1]
--- @param d vim.Diagnostic
diagnostics = vim.tbl_filter(function(d)
- return d.lnum == lnum
- and math.min(d.col, line_length - 1) <= col
- and (d.end_col >= col or d.end_lnum > lnum)
+ return lnum >= d.lnum
+ and lnum <= d.end_lnum
+ and (lnum ~= d.lnum or col >= math.min(d.col, line_length - 1))
+ and ((d.lnum == d.end_lnum and d.col == d.end_col) or lnum ~= d.end_lnum or col < d.end_col)
end, diagnostics)
end
@@ -1946,7 +2078,7 @@ end
--- @field title? string
---
--- See |diagnostic-severity|.
---- @field severity? vim.diagnostic.Severity
+--- @field severity? vim.diagnostic.SeverityFilter
--- Add all diagnostics to the quickfix list.
---
@@ -1974,7 +2106,7 @@ end
--- @field title? string
---
--- See |diagnostic-severity|.
---- @field severity? vim.diagnostic.Severity
+--- @field severity? vim.diagnostic.SeverityFilter
--- Add buffer diagnostics to the location list.
---
diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index d1fdd0aa16..d3910e26eb 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -4,12 +4,13 @@ local fn = vim.fn
local M = {}
--- @alias vim.filetype.mapfn fun(path:string,bufnr:integer, ...):string?, fun(b:integer)?
---- @alias vim.filetype.maptbl {[1]:string|vim.filetype.mapfn, [2]:{priority:integer}}
+--- @alias vim.filetype.mapopts { parent: string, priority: number }
+--- @alias vim.filetype.maptbl [string|vim.filetype.mapfn, vim.filetype.mapopts]
--- @alias vim.filetype.mapping.value string|vim.filetype.mapfn|vim.filetype.maptbl
--- @alias vim.filetype.mapping table<string,vim.filetype.mapping.value>
--- @param ft string|vim.filetype.mapfn
---- @param opts? {priority:integer}
+--- @param opts? vim.filetype.mapopts
--- @return vim.filetype.maptbl
local function starsetf(ft, opts)
return {
@@ -26,6 +27,9 @@ local function starsetf(ft, opts)
end
end,
{
+ -- Allow setting "parent" to be reused in closures, but don't have default as it will be
+ -- assigned later from grouping
+ parent = opts and opts.parent,
-- Starset matches should have lowest priority by default
priority = (opts and opts.priority) or -math.huge,
},
@@ -144,6 +148,9 @@ end
local function detect_noext(path, bufnr)
local root = fn.fnamemodify(path, ':r')
+ if root == path then
+ return
+ end
return M.match({ buf = bufnr, filename = root })
end
@@ -170,9 +177,15 @@ end
-- luacheck: push no unused args
-- luacheck: push ignore 122
--- Filetypes based on file extension
+-- Filetype detection logic is encoded in three tables:
+-- 1. `extension` for literal extension lookup
+-- 2. `filename` for literal full path or basename lookup;
+-- 3. `pattern` for matching filenames or paths against Lua patterns,
+-- optimized for fast lookup.
+-- See `:h dev-vimpatch-filetype` for guidance when porting Vim filetype patches.
+
---@diagnostic disable: unused-local
---- @type vim.filetype.mapping
+---@type vim.filetype.mapping
local extension = {
-- BEGIN EXTENSION
['8th'] = '8th',
@@ -190,6 +203,7 @@ local extension = {
aidl = 'aidl',
aml = 'aml',
run = 'ampl',
+ g4 = 'antlr4',
scpt = 'applescript',
ino = 'arduino',
pde = 'arduino',
@@ -203,12 +217,17 @@ local extension = {
return 'aspvbs'
end,
asm = detect.asm,
+ s = detect.asm,
+ S = detect.asm,
+ a = detect.asm,
+ A = detect.asm,
lst = detect.asm,
mac = detect.asm,
asn1 = 'asn',
asn = 'asn',
asp = detect.asp,
astro = 'astro',
+ asy = 'asy',
atl = 'atlas',
as = 'atlas',
zed = 'authzed',
@@ -232,6 +251,7 @@ local extension = {
db = detect.bindzone,
bicep = 'bicep',
bicepparam = 'bicep',
+ zone = 'bindzone',
bb = 'bitbake',
bbappend = 'bitbake',
bbclass = 'bitbake',
@@ -257,10 +277,14 @@ local extension = {
cdc = 'cdc',
cdl = 'cdl',
toc = detect_line1('\\contentsline', 'tex', 'cdrtoc'),
+ cedar = 'cedar',
cfc = 'cf',
cfm = 'cf',
cfi = 'cf',
hgrc = 'cfg',
+ cfg = detect.cfg,
+ Cfg = detect.cfg,
+ CFG = detect.cfg,
chf = 'ch',
chai = 'chaiscript',
ch = detect.change,
@@ -281,7 +305,6 @@ local extension = {
cook = 'cook',
cmake = 'cmake',
cmod = 'cmod',
- lib = 'cobol',
cob = 'cobol',
cbl = 'cobol',
atg = 'coco',
@@ -344,6 +367,9 @@ local extension = {
dart = 'dart',
drt = 'dart',
ds = 'datascript',
+ dat = detect.dat,
+ Dat = detect.dat,
+ DAT = detect.dat,
dcd = 'dcd',
decl = detect.decl,
dec = detect.decl,
@@ -366,6 +392,9 @@ local extension = {
gv = 'dot',
drac = 'dracula',
drc = 'dracula',
+ lvs = 'dracula',
+ lpe = 'dracula',
+ dsp = detect.dsp,
dtd = 'dtd',
d = detect.dtrace,
dts = 'dts',
@@ -417,6 +446,7 @@ local extension = {
fal = 'falcon',
fan = 'fan',
fwt = 'fan',
+ lib = 'faust',
fnl = 'fennel',
m4gl = 'fgl',
['4gl'] = 'fgl',
@@ -468,8 +498,21 @@ local extension = {
gmi = 'gemtext',
gemini = 'gemtext',
gift = 'gift',
+ prettierignore = 'gitignore',
gleam = 'gleam',
+ vert = 'glsl',
+ tesc = 'glsl',
+ tese = 'glsl',
glsl = 'glsl',
+ geom = 'glsl',
+ frag = 'glsl',
+ comp = 'glsl',
+ rgen = 'glsl',
+ rmiss = 'glsl',
+ rchit = 'glsl',
+ rahit = 'glsl',
+ rint = 'glsl',
+ rcall = 'glsl',
gn = 'gn',
gni = 'gn',
gnuplot = 'gnuplot',
@@ -535,6 +578,7 @@ local extension = {
stm = detect.html,
htt = 'httest',
htb = 'httest',
+ http = 'http',
hurl = 'hurl',
hw = detect.hw,
module = detect.hw,
@@ -572,6 +616,7 @@ local extension = {
jsx = 'javascriptreact',
clp = 'jess',
jgr = 'jgraph',
+ jinja = 'jinja',
jjdescription = 'jj',
j73 = 'jovial',
jov = 'jovial',
@@ -582,6 +627,7 @@ local extension = {
json = 'json',
jsonp = 'json',
geojson = 'json',
+ mcmeta = 'json',
webmanifest = 'json',
ipynb = 'json',
['jupyterlab-settings'] = 'json',
@@ -606,6 +652,9 @@ local extension = {
kts = 'kotlin',
kt = 'kotlin',
ktm = 'kotlin',
+ sub = 'krl',
+ Sub = 'krl',
+ SUB = 'krl',
ks = 'kscript',
k = 'kwt',
ACE = 'lace',
@@ -639,6 +688,9 @@ local extension = {
lt = 'lite',
lite = 'lite',
livemd = 'livebook',
+ log = detect.log,
+ Log = detect.log,
+ LOG = detect.log,
lgt = 'logtalk',
lotos = 'lotos',
lot = detect_line1('\\contentsline', 'tex', 'lotos'),
@@ -664,9 +716,8 @@ local extension = {
return not (path:find('html%.m4$') or path:find('fvwm2rc')) and 'm4' or nil
end,
eml = 'mail',
- mk = 'make',
- mak = 'make',
- dsp = 'make',
+ mk = detect.make,
+ mak = detect.make,
page = 'mallard',
map = 'map',
mws = 'maple',
@@ -679,7 +730,6 @@ local extension = {
markdown = detect.markdown,
mdown = detect.markdown,
mhtml = 'mason',
- comp = 'mason',
mason = 'mason',
master = 'master',
mas = 'master',
@@ -689,6 +739,8 @@ local extension = {
dm3 = 'maxima',
dmt = 'maxima',
wxm = 'maxima',
+ mw = 'mediawiki',
+ wiki = 'mediawiki',
mel = 'mel',
mmd = 'mermaid',
mmdc = 'mermaid',
@@ -702,9 +754,17 @@ local extension = {
mixal = 'mix',
mm = detect.mm,
nb = 'mma',
+ wl = 'mma',
mmp = 'mmp',
mms = detect.mms,
+ mod = detect.mod,
+ Mod = detect.mod,
+ MOD = detect.mod,
DEF = 'modula2',
+ m3 = 'modula3',
+ i3 = 'modula3',
+ mg = 'modula3',
+ ig = 'modula3',
lm3 = 'modula3',
mojo = 'mojo',
['🔥'] = 'mojo', -- 🙄
@@ -729,6 +789,14 @@ local extension = {
n1ql = 'n1ql',
nql = 'n1ql',
nanorc = 'nanorc',
+ NSA = 'natural',
+ NSC = 'natural',
+ NSG = 'natural',
+ NSL = 'natural',
+ NSM = 'natural',
+ NSN = 'natural',
+ NSP = 'natural',
+ NSS = 'natural',
ncf = 'ncf',
nginx = 'nginx',
nim = 'nim',
@@ -738,6 +806,15 @@ local extension = {
nix = 'nix',
norg = 'norg',
nqc = 'nqc',
+ ['1'] = detect.nroff,
+ ['2'] = detect.nroff,
+ ['3'] = detect.nroff,
+ ['4'] = detect.nroff,
+ ['5'] = detect.nroff,
+ ['6'] = detect.nroff,
+ ['7'] = detect.nroff,
+ ['8'] = detect.nroff,
+ ['9'] = detect.nroff,
roff = 'nroff',
tmac = 'nroff',
man = 'nroff',
@@ -769,6 +846,14 @@ local extension = {
['or'] = 'openroad',
scad = 'openscad',
ovpn = 'openvpn',
+ opl = 'opl',
+ opL = 'opl',
+ oPl = 'opl',
+ oPL = 'opl',
+ Opl = 'opl',
+ OpL = 'opl',
+ OPl = 'opl',
+ OPL = 'opl',
ora = 'ora',
org = 'org',
org_archive = 'org',
@@ -800,6 +885,16 @@ local extension = {
ctp = 'php',
php = 'php',
phpt = 'php',
+ php0 = 'php',
+ php1 = 'php',
+ php2 = 'php',
+ php3 = 'php',
+ php4 = 'php',
+ php5 = 'php',
+ php6 = 'php',
+ php7 = 'php',
+ php8 = 'php',
+ php9 = 'php',
phtml = 'php',
theme = 'php',
pike = 'pike',
@@ -832,6 +927,9 @@ local extension = {
it = 'ppwiz',
ih = 'ppwiz',
action = 'privoxy',
+ prg = detect.prg,
+ Prg = detect.prg,
+ PRG = detect.prg,
pc = 'proc',
pdb = 'prolog',
pml = 'promela',
@@ -873,6 +971,19 @@ local extension = {
t6 = 'raku',
p6 = 'raku',
raml = 'raml',
+ sysx = 'rapid',
+ sysX = 'rapid',
+ Sysx = 'rapid',
+ SysX = 'rapid',
+ SYSX = 'rapid',
+ SYSx = 'rapid',
+ modx = 'rapid',
+ modX = 'rapid',
+ Modx = 'rapid',
+ ModX = 'rapid',
+ MODX = 'rapid',
+ MODx = 'rapid',
+ rasi = 'rasi',
rbs = 'rbs',
rego = 'rego',
rem = 'remind',
@@ -929,6 +1040,7 @@ local extension = {
rake = 'ruby',
rs = 'rust',
sage = 'sage',
+ sls = 'salt',
sas = 'sas',
sass = 'sass',
sa = 'sather',
@@ -952,6 +1064,7 @@ local extension = {
ebuild = detect.bash,
eclass = detect.bash,
env = detect.sh,
+ envrc = detect.sh,
ksh = detect.ksh,
sh = detect.sh,
mdd = 'sh',
@@ -984,6 +1097,7 @@ local extension = {
smt = 'smith',
smithy = 'smithy',
sml = 'sml',
+ smk = 'snakemake',
spt = 'snobol4',
sno = 'snobol4',
sln = 'solution',
@@ -1006,6 +1120,9 @@ local extension = {
sqi = 'sqr',
sqr = 'sqr',
nut = 'squirrel',
+ src = detect.src,
+ Src = detect.src,
+ SRC = detect.src,
s28 = 'srec',
s37 = 'srec',
srec = 'srec',
@@ -1030,8 +1147,12 @@ local extension = {
svelte = 'svelte',
svg = 'svg',
swift = 'swift',
+ swiftinterface = 'swift',
swig = 'swig',
swg = 'swig',
+ sys = detect.sys,
+ Sys = detect.sys,
+ SYS = detect.sys,
svh = 'systemverilog',
sv = 'systemverilog',
cmm = 'trace32',
@@ -1060,6 +1181,7 @@ local extension = {
nls = 'tex',
thm = 'tex',
eps_tex = 'tex',
+ pdf_tex = 'tex',
pygtex = 'tex',
pygstyle = 'tex',
clo = 'tex',
@@ -1153,6 +1275,8 @@ local extension = {
wbt = 'winbatch',
wit = 'wit',
wml = 'wml',
+ wsf = 'wsh',
+ wsc = 'wsh',
wsml = 'wsml',
ad = 'xdefaults',
xhtml = 'xhtml',
@@ -1206,6 +1330,8 @@ local extension = {
z8a = 'z8a',
zig = 'zig',
zon = 'zig',
+ ziggy = 'ziggy',
+ ['ziggy-schema'] = 'ziggy_schema',
zu = 'zimbu',
zut = 'zimbutempl',
zs = 'zserio',
@@ -1261,8 +1387,7 @@ local extension = {
['dpkg-new'] = detect_noext,
['in'] = function(path, bufnr)
if vim.fs.basename(path) ~= 'configure.in' then
- local root = fn.fnamemodify(path, ':r')
- return M.match({ buf = bufnr, filename = root })
+ return detect_noext(path, bufnr)
end
end,
new = detect_noext,
@@ -1275,7 +1400,7 @@ local extension = {
-- END EXTENSION
}
---- @type vim.filetype.mapping
+---@type vim.filetype.mapping
local filename = {
-- BEGIN FILENAME
['a2psrc'] = 'a2ps',
@@ -1290,7 +1415,17 @@ local filename = {
['/.aptitude/config'] = 'aptconf',
['=tagging-method'] = 'arch',
['.arch-inventory'] = 'arch',
+ ['makefile.am'] = 'automake',
+ ['Makefile.am'] = 'automake',
['GNUmakefile.am'] = 'automake',
+ ['.bash_aliases'] = detect.bash,
+ ['.bash-aliases'] = detect.bash,
+ ['.bash_history'] = detect.bash,
+ ['.bash-history'] = detect.bash,
+ ['.bash_logout'] = detect.bash,
+ ['.bash-logout'] = detect.bash,
+ ['.bash_profile'] = detect.bash,
+ ['.bash-profile'] = detect.bash,
['named.root'] = 'bindzone',
WORKSPACE = 'bzl',
['WORKSPACE.bzlmod'] = 'bzl',
@@ -1373,6 +1508,7 @@ local filename = {
jbuild = 'dune',
['dune-workspace'] = 'dune',
['dune-project'] = 'dune',
+ ['dune-file'] = 'dune',
Earthfile = 'earthfile',
['.editorconfig'] = 'editorconfig',
['elinks.conf'] = 'elinks',
@@ -1409,6 +1545,7 @@ local filename = {
gnashpluginrc = 'gnash',
gnashrc = 'gnash',
['.gnuplot_history'] = 'gnuplot',
+ ['goaccess.conf'] = 'goaccess',
['go.sum'] = 'gosum',
['go.work.sum'] = 'gosum',
['go.work'] = 'gowork',
@@ -1445,11 +1582,15 @@ local filename = {
['ipf.conf'] = 'ipfilter',
['ipf6.conf'] = 'ipfilter',
['ipf.rules'] = 'ipfilter',
+ ['.bun_repl_history'] = 'javascript',
['.node_repl_history'] = 'javascript',
+ ['deno_history.txt'] = 'javascript',
['Pipfile.lock'] = 'json',
['.firebaserc'] = 'json',
['.prettierrc'] = 'json',
['.stylelintrc'] = 'json',
+ ['.lintstagedrc'] = 'json',
+ ['deno.lock'] = 'json',
['flake.lock'] = 'json',
['.babelrc'] = 'jsonc',
['.eslintrc'] = 'jsonc',
@@ -1461,9 +1602,14 @@ local filename = {
['.swrc'] = 'jsonc',
['.vsconfig'] = 'jsonc',
['.justfile'] = 'just',
+ ['justfile'] = 'just',
+ ['Justfile'] = 'just',
Kconfig = 'kconfig',
['Kconfig.debug'] = 'kconfig',
['Config.in'] = 'kconfig',
+ ['ldaprc'] = 'ldapconf',
+ ['.ldaprc'] = 'ldapconf',
+ ['ldap.conf'] = 'ldapconf',
['lftp.conf'] = 'lftp',
['.lftprc'] = 'lftp',
['/.libao'] = 'libao',
@@ -1509,6 +1655,8 @@ local filename = {
mrxvtrc = 'mrxvtrc',
['.mrxvtrc'] = 'mrxvtrc',
['.msmtprc'] = 'msmtp',
+ ['Muttngrc'] = 'muttrc',
+ ['Muttrc'] = 'muttrc',
['.mysql_history'] = 'mysql',
['/etc/nanorc'] = 'nanorc',
Neomuttrc = 'neomuttrc',
@@ -1520,6 +1668,7 @@ local filename = {
['octave.conf'] = 'octave',
['.ondirrc'] = 'ondir',
opam = 'opam',
+ ['opam.locked'] = 'opam',
['pacman.log'] = 'pacmanlog',
['/etc/pam.conf'] = 'pamconf',
['pam_env.conf'] = 'pamenv',
@@ -1532,6 +1681,9 @@ local filename = {
['/etc/shadow-'] = 'passwd',
['/etc/shadow'] = 'passwd',
['/etc/passwd.edit'] = 'passwd',
+ ['.gitolite.rc'] = 'perl',
+ ['gitolite.rc'] = 'perl',
+ ['example.gitolite.rc'] = 'perl',
['latexmkrc'] = 'perl',
['.latexmkrc'] = 'perl',
['pf.conf'] = 'pf',
@@ -1587,6 +1739,10 @@ local filename = {
irbrc = 'ruby',
['.irb_history'] = 'ruby',
irb_history = 'ruby',
+ ['rakefile'] = 'ruby',
+ ['Rakefile'] = 'ruby',
+ ['rantfile'] = 'ruby',
+ ['Rantfile'] = 'ruby',
Vagrantfile = 'ruby',
['smb.conf'] = 'samba',
screenrc = 'screen',
@@ -1597,6 +1753,8 @@ local filename = {
['/etc/serial.conf'] = 'setserial',
['/etc/udev/cdsymlinks.conf'] = 'sh',
['.ash_history'] = 'sh',
+ ['.devscripts'] = 'sh',
+ ['devscripts.conf'] = 'sh',
['makepkg.conf'] = 'sh',
['.makepkg.conf'] = 'sh',
['user-dirs.dirs'] = 'sh',
@@ -1618,6 +1776,7 @@ local filename = {
['/etc/slp.spi'] = 'slpspi',
['.slrnrc'] = 'slrnrc',
['sendmail.cf'] = 'sm',
+ Snakefile = 'snakemake',
['.sqlite_history'] = 'sql',
['squid.conf'] = 'squid',
['ssh_config'] = 'sshconfig',
@@ -1637,7 +1796,7 @@ local filename = {
['.xsdbcmdhistory'] = 'tcl',
['texmf.cnf'] = 'texmf',
COPYING = 'text',
- README = 'text',
+ README = detect_seq(detect.haredoc, 'text'),
LICENSE = 'text',
AUTHORS = 'text',
tfrc = 'tf',
@@ -1703,528 +1862,568 @@ local filename = {
}
-- Re-use closures as much as possible
-local detect_apache = starsetf('apache')
-local detect_muttrc = starsetf('muttrc')
-local detect_neomuttrc = starsetf('neomuttrc')
+local detect_apache_diretc = starsetf('apache', { parent = '/etc/' })
+local detect_apache_dotconf = starsetf('apache', { parent = '%.conf' })
+local detect_muttrc = starsetf('muttrc', { parent = 'utt' })
+local detect_neomuttrc = starsetf('neomuttrc', { parent = 'utt' })
+local detect_xkb = starsetf('xkb', { parent = '/usr/' })
---- @type vim.filetype.mapping
+---@type table<string,vim.filetype.mapping>
local pattern = {
-- BEGIN PATTERN
- ['.*/etc/a2ps/.*%.cfg'] = 'a2ps',
- ['.*/etc/a2ps%.cfg'] = 'a2ps',
- ['.*/usr/share/alsa/alsa%.conf'] = 'alsaconf',
- ['.*/etc/asound%.conf'] = 'alsaconf',
- ['.*/etc/apache2/sites%-.*/.*%.com'] = 'apache',
- ['.*/etc/httpd/.*%.conf'] = 'apache',
- ['.*/etc/apache2/.*%.conf.*'] = detect_apache,
- ['.*/etc/apache2/conf%..*/.*'] = detect_apache,
- ['.*/etc/apache2/mods%-.*/.*'] = detect_apache,
- ['.*/etc/apache2/sites%-.*/.*'] = detect_apache,
- ['access%.conf.*'] = detect_apache,
- ['apache%.conf.*'] = detect_apache,
- ['apache2%.conf.*'] = detect_apache,
- ['httpd%.conf.*'] = detect_apache,
- ['srm%.conf.*'] = detect_apache,
- ['.*/etc/httpd/conf%..*/.*'] = detect_apache,
- ['.*/etc/httpd/conf%.d/.*%.conf.*'] = detect_apache,
- ['.*/etc/httpd/mods%-.*/.*'] = detect_apache,
- ['.*/etc/httpd/sites%-.*/.*'] = detect_apache,
- ['.*/etc/proftpd/.*%.conf.*'] = starsetf('apachestyle'),
- ['.*/etc/proftpd/conf%..*/.*'] = starsetf('apachestyle'),
- ['proftpd%.conf.*'] = starsetf('apachestyle'),
- ['.*asterisk/.*%.conf.*'] = starsetf('asterisk'),
- ['.*asterisk.*/.*voicemail%.conf.*'] = starsetf('asteriskvm'),
- ['.*/%.aptitude/config'] = 'aptconf',
- ['.*%.[aA]'] = detect.asm,
- ['.*%.[sS]'] = detect.asm,
- ['[mM]akefile%.am'] = 'automake',
- ['.*/bind/db%..*'] = starsetf('bindzone'),
- ['.*/named/db%..*'] = starsetf('bindzone'),
- ['.*/build/conf/.*%.conf'] = 'bitbake',
- ['.*/meta/conf/.*%.conf'] = 'bitbake',
- ['.*/meta%-.*/conf/.*%.conf'] = 'bitbake',
- ['.*%.blade%.php'] = 'blade',
- ['bzr_log%..*'] = 'bzr',
- ['.*enlightenment/.*%.cfg'] = 'c',
- ['.*/%.cabal/config'] = 'cabalconfig',
- ['.*/cabal/config'] = 'cabalconfig',
- ['cabal%.project%..*'] = starsetf('cabalproject'),
- ['.*/%.calendar/.*'] = starsetf('calendar'),
- ['.*/share/calendar/.*/calendar%..*'] = starsetf('calendar'),
- ['.*/share/calendar/calendar%..*'] = starsetf('calendar'),
- ['sgml%.catalog.*'] = starsetf('catalog'),
- ['.*/etc/defaults/cdrdao'] = 'cdrdaoconf',
- ['.*/etc/cdrdao%.conf'] = 'cdrdaoconf',
- ['.*/etc/default/cdrdao'] = 'cdrdaoconf',
- ['.*hgrc'] = 'cfg',
- ['.*%.[Cc][Ff][Gg]'] = {
- detect.cfg,
- -- Decrease priority to avoid conflicts with more specific patterns
- -- such as '.*/etc/a2ps/.*%.cfg', '.*enlightenment/.*%.cfg', etc.
- { priority = -1 },
+ ['/debian/'] = {
+ ['/debian/changelog$'] = 'debchangelog',
+ ['/debian/control$'] = 'debcontrol',
+ ['/debian/copyright$'] = 'debcopyright',
+ ['/debian/patches/'] = detect.dep3patch,
+ },
+ ['/etc/'] = {
+ ['/etc/a2ps/.*%.cfg$'] = 'a2ps',
+ ['/etc/a2ps%.cfg$'] = 'a2ps',
+ ['/etc/asound%.conf$'] = 'alsaconf',
+ ['/etc/apache2/sites%-.*/.*%.com$'] = 'apache',
+ ['/etc/httpd/.*%.conf$'] = 'apache',
+ ['/etc/apache2/.*%.conf'] = detect_apache_diretc,
+ ['/etc/apache2/conf%..*/'] = detect_apache_diretc,
+ ['/etc/apache2/mods%-.*/'] = detect_apache_diretc,
+ ['/etc/apache2/sites%-.*/'] = detect_apache_diretc,
+ ['/etc/httpd/conf%..*/'] = detect_apache_diretc,
+ ['/etc/httpd/conf%.d/.*%.conf'] = detect_apache_diretc,
+ ['/etc/httpd/mods%-.*/'] = detect_apache_diretc,
+ ['/etc/httpd/sites%-.*/'] = detect_apache_diretc,
+ ['/etc/proftpd/.*%.conf'] = starsetf('apachestyle'),
+ ['/etc/proftpd/conf%..*/'] = starsetf('apachestyle'),
+ ['/etc/cdrdao%.conf$'] = 'cdrdaoconf',
+ ['/etc/default/cdrdao$'] = 'cdrdaoconf',
+ ['/etc/defaults/cdrdao$'] = 'cdrdaoconf',
+ ['/etc/translate%-shell$'] = 'clojure',
+ ['/etc/hostname%.'] = starsetf('config'),
+ ['/etc/cron%.d/'] = starsetf('crontab'),
+ ['/etc/apt/sources%.list%.d/.*%.sources$'] = 'deb822sources',
+ ['/etc/apt/sources%.list%.d/.*%.list$'] = 'debsources',
+ ['/etc/apt/sources%.list$'] = 'debsources',
+ ['/etc/DIR_COLORS$'] = 'dircolors',
+ ['/etc/dnsmasq%.conf$'] = 'dnsmasq',
+ ['/etc/dnsmasq%.d/'] = starsetf('dnsmasq'),
+ ['/etc/yum%.conf$'] = 'dosini',
+ ['/etc/yum%.repos%.d/'] = starsetf('dosini'),
+ ['/etc/gitconfig%.d/'] = starsetf('gitconfig'),
+ ['/etc/gitconfig$'] = 'gitconfig',
+ ['/etc/gitattributes$'] = 'gitattributes',
+ ['/etc/group$'] = 'group',
+ ['/etc/group%-$'] = 'group',
+ ['/etc/group%.edit$'] = 'group',
+ ['/etc/gshadow%-$'] = 'group',
+ ['/etc/gshadow%.edit$'] = 'group',
+ ['/etc/gshadow$'] = 'group',
+ ['/etc/grub%.conf$'] = 'grub',
+ ['/etc/host%.conf$'] = 'hostconf',
+ ['/etc/hosts%.allow$'] = 'hostsaccess',
+ ['/etc/hosts%.deny$'] = 'hostsaccess',
+ ['/etc/initng/.*/.*%.i$'] = 'initng',
+ ['/etc/libao%.conf$'] = 'libao',
+ ['/etc/.*limits%.conf$'] = 'limits',
+ ['/etc/.*limits%.d/.*%.conf$'] = 'limits',
+ ['/etc/limits$'] = 'limits',
+ ['/etc/logcheck/.*%.d.*/'] = starsetf('logcheck'),
+ ['/etc/login%.access$'] = 'loginaccess',
+ ['/etc/login%.defs$'] = 'logindefs',
+ ['/etc/aliases$'] = 'mailaliases',
+ ['/etc/mail/aliases$'] = 'mailaliases',
+ ['/etc/man%.conf$'] = 'manconf',
+ ['/etc/conf%.modules$'] = 'modconf',
+ ['/etc/modprobe%.'] = starsetf('modconf'),
+ ['/etc/modules%.conf$'] = 'modconf',
+ ['/etc/modules$'] = 'modconf',
+ ['/etc/modutils/'] = starsetf(function(path, bufnr)
+ if fn.executable(fn.expand(path)) ~= 1 then
+ return 'modconf'
+ end
+ end),
+ ['/etc/Muttrc%.d/'] = starsetf('muttrc'),
+ ['/etc/nanorc$'] = 'nanorc',
+ ['/etc/nginx/'] = 'nginx',
+ ['/etc/pam%.conf$'] = 'pamconf',
+ ['/etc/pam%.d/'] = starsetf('pamconf'),
+ ['/etc/passwd%-$'] = 'passwd',
+ ['/etc/shadow$'] = 'passwd',
+ ['/etc/shadow%.edit$'] = 'passwd',
+ ['/etc/passwd$'] = 'passwd',
+ ['/etc/passwd%.edit$'] = 'passwd',
+ ['/etc/shadow%-$'] = 'passwd',
+ ['/etc/pinforc$'] = 'pinfo',
+ ['/etc/protocols$'] = 'protocols',
+ ['/etc/sensors%.d/[^.]'] = starsetf('sensors'),
+ ['/etc/sensors%.conf$'] = 'sensors',
+ ['/etc/sensors3%.conf$'] = 'sensors',
+ ['/etc/services$'] = 'services',
+ ['/etc/serial%.conf$'] = 'setserial',
+ ['/etc/udev/cdsymlinks%.conf$'] = 'sh',
+ ['/etc/profile$'] = detect.sh,
+ ['/etc/slp%.conf$'] = 'slpconf',
+ ['/etc/slp%.reg$'] = 'slpreg',
+ ['/etc/slp%.spi$'] = 'slpspi',
+ ['/etc/sudoers%.d/'] = starsetf('sudoers'),
+ ['/etc/ssh/ssh_config%.d/.*%.conf$'] = 'sshconfig',
+ ['/etc/ssh/sshd_config%.d/.*%.conf$'] = 'sshdconfig',
+ ['/etc/sudoers$'] = 'sudoers',
+ ['/etc/sysctl%.conf$'] = 'sysctl',
+ ['/etc/sysctl%.d/.*%.conf$'] = 'sysctl',
+ ['/etc/systemd/.*%.conf%.d/.*%.conf$'] = 'systemd',
+ ['/etc/systemd/system/.*%.d/.*%.conf$'] = 'systemd',
+ ['/etc/systemd/system/.*%.d/%.#'] = 'systemd',
+ ['/etc/systemd/system/%.#'] = 'systemd',
+ ['/etc/config/'] = starsetf(detect.uci),
+ ['/etc/udev/udev%.conf$'] = 'udevconf',
+ ['/etc/udev/permissions%.d/.*%.permissions$'] = 'udevperm',
+ ['/etc/updatedb%.conf$'] = 'updatedb',
+ ['/etc/init/.*%.conf$'] = 'upstart',
+ ['/etc/init/.*%.override$'] = 'upstart',
+ ['/etc/xinetd%.conf$'] = 'xinetd',
+ ['/etc/xinetd%.d/'] = starsetf('xinetd'),
+ ['/etc/blkid%.tab%.old$'] = 'xml',
+ ['/etc/blkid%.tab$'] = 'xml',
+ ['/etc/xdg/menus/.*%.menu$'] = 'xml',
+ ['/etc/zprofile$'] = 'zsh',
},
- ['[cC]hange[lL]og.*'] = starsetf(detect.changelog),
- ['.*%.%.ch'] = 'chill',
- ['.*/etc/translate%-shell'] = 'clojure',
- ['.*%.cmake%.in'] = 'cmake',
- -- */cmus/rc and */.cmus/rc
- ['.*/%.?cmus/rc'] = 'cmusrc',
- -- */cmus/*.theme and */.cmus/*.theme
- ['.*/%.?cmus/.*%.theme'] = 'cmusrc',
- ['.*/%.cmus/autosave'] = 'cmusrc',
- ['.*/%.cmus/command%-history'] = 'cmusrc',
- ['.*/etc/hostname%..*'] = starsetf('config'),
- ['crontab%..*'] = starsetf('crontab'),
- ['.*/etc/cron%.d/.*'] = starsetf('crontab'),
- ['%.cshrc.*'] = detect.csh,
- ['%.login.*'] = detect.csh,
- ['cvs%d+'] = 'cvs',
- ['.*%.[Dd][Aa][Tt]'] = detect.dat,
- ['.*/debian/patches/.*'] = detect.dep3patch,
- ['.*/etc/dnsmasq%.d/.*'] = starsetf('dnsmasq'),
- ['Containerfile%..*'] = starsetf('dockerfile'),
- ['Dockerfile%..*'] = starsetf('dockerfile'),
- ['.*/etc/yum%.repos%.d/.*'] = starsetf('dosini'),
- ['drac%..*'] = starsetf('dracula'),
- ['.*/debian/changelog'] = 'debchangelog',
- ['.*/debian/control'] = 'debcontrol',
- ['.*/debian/copyright'] = 'debcopyright',
- ['.*/etc/apt/sources%.list%.d/.*%.list'] = 'debsources',
- ['.*/etc/apt/sources%.list'] = 'debsources',
- ['.*/etc/apt/sources%.list%.d/.*%.sources'] = 'deb822sources',
- ['.*%.directory'] = 'desktop',
- ['.*%.desktop'] = 'desktop',
- ['dictd.*%.conf'] = 'dictdconf',
- ['.*/etc/DIR_COLORS'] = 'dircolors',
- ['.*/etc/dnsmasq%.conf'] = 'dnsmasq',
- ['php%.ini%-.*'] = 'dosini',
- ['.*/%.aws/config'] = 'confini',
- ['.*/%.aws/credentials'] = 'confini',
- ['.*/etc/yum%.conf'] = 'dosini',
- ['.*/lxqt/.*%.conf'] = 'dosini',
- ['.*/screengrab/.*%.conf'] = 'dosini',
- ['.*/bpython/config'] = 'dosini',
- ['.*/mypy/config'] = 'dosini',
- ['.*/flatpak/repo/config'] = 'dosini',
- ['.*lvs'] = 'dracula',
- ['.*lpe'] = 'dracula',
- ['.*/dtrace/.*%.d'] = 'dtrace',
- ['.*esmtprc'] = 'esmtprc',
- ['.*Eterm/.*%.cfg'] = 'eterm',
- ['.*s6.*/up'] = 'execline',
- ['.*s6.*/down'] = 'execline',
- ['.*s6.*/run'] = 'execline',
- ['.*s6.*/finish'] = 'execline',
- ['s6%-.*'] = 'execline',
- ['[a-zA-Z0-9].*Dict'] = detect.foam,
- ['[a-zA-Z0-9].*Dict%..*'] = detect.foam,
- ['[a-zA-Z].*Properties'] = detect.foam,
- ['[a-zA-Z].*Properties%..*'] = detect.foam,
- ['.*Transport%..*'] = detect.foam,
- ['.*/constant/g'] = detect.foam,
- ['.*/0/.*'] = detect.foam,
- ['.*/0%.orig/.*'] = detect.foam,
- ['.*/%.fvwm/.*'] = starsetf('fvwm'),
- ['.*fvwmrc.*'] = starsetf(detect.fvwm_v1),
- ['.*fvwm95.*%.hook'] = starsetf(detect.fvwm_v1),
- ['.*fvwm2rc.*'] = starsetf(detect.fvwm_v2),
- ['.*/tmp/lltmp.*'] = starsetf('gedcom'),
- ['.*/etc/gitconfig%.d/.*'] = starsetf('gitconfig'),
- ['.*/gitolite%-admin/conf/.*'] = starsetf('gitolite'),
- ['tmac%..*'] = starsetf('nroff'),
- ['.*/%.gitconfig%.d/.*'] = starsetf('gitconfig'),
- ['.*%.git/.*'] = {
- detect.git,
- -- Decrease priority to run after simple pattern checks
- { priority = -1 },
+ ['/log/'] = {
+ ['/log/auth%.crit$'] = 'messages',
+ ['/log/auth%.err$'] = 'messages',
+ ['/log/auth%.info$'] = 'messages',
+ ['/log/auth%.log$'] = 'messages',
+ ['/log/auth%.notice$'] = 'messages',
+ ['/log/auth%.warn$'] = 'messages',
+ ['/log/auth$'] = 'messages',
+ ['/log/cron%.crit$'] = 'messages',
+ ['/log/cron%.err$'] = 'messages',
+ ['/log/cron%.info$'] = 'messages',
+ ['/log/cron%.log$'] = 'messages',
+ ['/log/cron%.notice$'] = 'messages',
+ ['/log/cron%.warn$'] = 'messages',
+ ['/log/cron$'] = 'messages',
+ ['/log/daemon%.crit$'] = 'messages',
+ ['/log/daemon%.err$'] = 'messages',
+ ['/log/daemon%.info$'] = 'messages',
+ ['/log/daemon%.log$'] = 'messages',
+ ['/log/daemon%.notice$'] = 'messages',
+ ['/log/daemon%.warn$'] = 'messages',
+ ['/log/daemon$'] = 'messages',
+ ['/log/debug%.crit$'] = 'messages',
+ ['/log/debug%.err$'] = 'messages',
+ ['/log/debug%.info$'] = 'messages',
+ ['/log/debug%.log$'] = 'messages',
+ ['/log/debug%.notice$'] = 'messages',
+ ['/log/debug%.warn$'] = 'messages',
+ ['/log/debug$'] = 'messages',
+ ['/log/kern%.crit$'] = 'messages',
+ ['/log/kern%.err$'] = 'messages',
+ ['/log/kern%.info$'] = 'messages',
+ ['/log/kern%.log$'] = 'messages',
+ ['/log/kern%.notice$'] = 'messages',
+ ['/log/kern%.warn$'] = 'messages',
+ ['/log/kern$'] = 'messages',
+ ['/log/lpr%.crit$'] = 'messages',
+ ['/log/lpr%.err$'] = 'messages',
+ ['/log/lpr%.info$'] = 'messages',
+ ['/log/lpr%.log$'] = 'messages',
+ ['/log/lpr%.notice$'] = 'messages',
+ ['/log/lpr%.warn$'] = 'messages',
+ ['/log/lpr$'] = 'messages',
+ ['/log/mail%.crit$'] = 'messages',
+ ['/log/mail%.err$'] = 'messages',
+ ['/log/mail%.info$'] = 'messages',
+ ['/log/mail%.log$'] = 'messages',
+ ['/log/mail%.notice$'] = 'messages',
+ ['/log/mail%.warn$'] = 'messages',
+ ['/log/mail$'] = 'messages',
+ ['/log/messages%.crit$'] = 'messages',
+ ['/log/messages%.err$'] = 'messages',
+ ['/log/messages%.info$'] = 'messages',
+ ['/log/messages%.log$'] = 'messages',
+ ['/log/messages%.notice$'] = 'messages',
+ ['/log/messages%.warn$'] = 'messages',
+ ['/log/messages$'] = 'messages',
+ ['/log/news/news%.crit$'] = 'messages',
+ ['/log/news/news%.err$'] = 'messages',
+ ['/log/news/news%.info$'] = 'messages',
+ ['/log/news/news%.log$'] = 'messages',
+ ['/log/news/news%.notice$'] = 'messages',
+ ['/log/news/news%.warn$'] = 'messages',
+ ['/log/news/news$'] = 'messages',
+ ['/log/syslog%.crit$'] = 'messages',
+ ['/log/syslog%.err$'] = 'messages',
+ ['/log/syslog%.info$'] = 'messages',
+ ['/log/syslog%.log$'] = 'messages',
+ ['/log/syslog%.notice$'] = 'messages',
+ ['/log/syslog%.warn$'] = 'messages',
+ ['/log/syslog$'] = 'messages',
+ ['/log/user%.crit$'] = 'messages',
+ ['/log/user%.err$'] = 'messages',
+ ['/log/user%.info$'] = 'messages',
+ ['/log/user%.log$'] = 'messages',
+ ['/log/user%.notice$'] = 'messages',
+ ['/log/user%.warn$'] = 'messages',
+ ['/log/user$'] = 'messages',
+ },
+ ['/systemd/'] = {
+ ['/%.config/systemd/user/%.#'] = 'systemd',
+ ['/%.config/systemd/user/.*%.d/%.#'] = 'systemd',
+ ['/%.config/systemd/user/.*%.d/.*%.conf$'] = 'systemd',
+ ['/systemd/.*%.automount$'] = 'systemd',
+ ['/systemd/.*%.dnssd$'] = 'systemd',
+ ['/systemd/.*%.link$'] = 'systemd',
+ ['/systemd/.*%.mount$'] = 'systemd',
+ ['/systemd/.*%.netdev$'] = 'systemd',
+ ['/systemd/.*%.network$'] = 'systemd',
+ ['/systemd/.*%.nspawn$'] = 'systemd',
+ ['/systemd/.*%.path$'] = 'systemd',
+ ['/systemd/.*%.service$'] = 'systemd',
+ ['/systemd/.*%.slice$'] = 'systemd',
+ ['/systemd/.*%.socket$'] = 'systemd',
+ ['/systemd/.*%.swap$'] = 'systemd',
+ ['/systemd/.*%.target$'] = 'systemd',
+ ['/systemd/.*%.timer$'] = 'systemd',
+ },
+ ['/usr/'] = {
+ ['/usr/share/alsa/alsa%.conf$'] = 'alsaconf',
+ ['/usr/.*/gnupg/options%.skel$'] = 'gpg',
+ ['/usr/share/upstart/.*%.conf$'] = 'upstart',
+ ['/usr/share/upstart/.*%.override$'] = 'upstart',
+ ['/usr/share/X11/xkb/compat/'] = detect_xkb,
+ ['/usr/share/X11/xkb/geometry/'] = detect_xkb,
+ ['/usr/share/X11/xkb/keycodes/'] = detect_xkb,
+ ['/usr/share/X11/xkb/symbols/'] = detect_xkb,
+ ['/usr/share/X11/xkb/types/'] = detect_xkb,
+ },
+ ['/var/'] = {
+ ['/var/backups/group%.bak$'] = 'group',
+ ['/var/backups/gshadow%.bak$'] = 'group',
+ ['/var/backups/passwd%.bak$'] = 'passwd',
+ ['/var/backups/shadow%.bak$'] = 'passwd',
+ },
+ ['/conf'] = {
+ ['/%.aptitude/config$'] = 'aptconf',
+ ['/build/conf/.*%.conf$'] = 'bitbake',
+ ['/meta%-.*/conf/.*%.conf$'] = 'bitbake',
+ ['/meta/conf/.*%.conf$'] = 'bitbake',
+ ['/%.cabal/config$'] = 'cabalconfig',
+ ['/cabal/config$'] = 'cabalconfig',
+ ['/%.aws/config$'] = 'confini',
+ ['/bpython/config$'] = 'dosini',
+ ['/flatpak/repo/config$'] = 'dosini',
+ ['/mypy/config$'] = 'dosini',
+ ['^${HOME}/%.config/notmuch/.*/config$'] = 'dosini',
+ ['^${XDG_CONFIG_HOME}/notmuch/.*/config$'] = 'dosini',
+ ['^${XDG_CONFIG_HOME}/git/config$'] = 'gitconfig',
+ ['%.git/config%.worktree$'] = 'gitconfig',
+ ['%.git/config$'] = 'gitconfig',
+ ['%.git/modules/.*/config$'] = 'gitconfig',
+ ['%.git/modules/config$'] = 'gitconfig',
+ ['%.git/worktrees/.*/config%.worktree$'] = 'gitconfig',
+ ['/%.config/git/config$'] = 'gitconfig',
+ ['/gitolite%-admin/conf/'] = starsetf('gitolite'),
+ ['/%.i3/config$'] = 'i3config',
+ ['/i3/config$'] = 'i3config',
+ ['/supertux2/config$'] = 'lisp',
+ ['/%.mplayer/config$'] = 'mplayerconf',
+ ['/neofetch/config%.conf$'] = 'sh',
+ ['/%.ssh/config$'] = 'sshconfig',
+ ['/%.sway/config$'] = 'swayconfig',
+ ['/sway/config$'] = 'swayconfig',
+ ['/%.cargo/config$'] = 'toml',
+ ['/%.bundle/config$'] = 'yaml',
+ },
+ ['/%.'] = {
+ ['/%.aws/credentials$'] = 'confini',
+ ['/%.gitconfig%.d/'] = starsetf('gitconfig'),
+ ['/%.gnupg/gpg%.conf$'] = 'gpg',
+ ['/%.gnupg/options$'] = 'gpg',
+ ['/%.icewm/menu$'] = 'icemenu',
+ ['/%.libao$'] = 'libao',
+ ['/%.pinforc$'] = 'pinfo',
+ ['/%.cargo/credentials$'] = 'toml',
+ ['/%.init/.*%.override$'] = 'upstart',
+ },
+ ['calendar/'] = {
+ ['/%.calendar/'] = starsetf('calendar'),
+ ['/share/calendar/.*/calendar%.'] = starsetf('calendar'),
+ ['/share/calendar/calendar%.'] = starsetf('calendar'),
+ },
+ ['cmus/'] = {
+ -- */cmus/*.theme and */.cmus/*.theme
+ ['/%.?cmus/.*%.theme$'] = 'cmusrc',
+ -- */cmus/rc and */.cmus/rc
+ ['/%.?cmus/rc$'] = 'cmusrc',
+ ['/%.cmus/autosave$'] = 'cmusrc',
+ ['/%.cmus/command%-history$'] = 'cmusrc',
+ },
+ ['git/'] = {
+ ['%.git/'] = {
+ detect.git,
+ -- Decrease priority to run after simple pattern checks
+ { priority = -1 },
+ },
+ ['^${XDG_CONFIG_HOME}/git/attributes$'] = 'gitattributes',
+ ['%.git/info/attributes$'] = 'gitattributes',
+ ['/%.config/git/attributes$'] = 'gitattributes',
+ ['^${XDG_CONFIG_HOME}/git/ignore$'] = 'gitignore',
+ ['%.git/info/exclude$'] = 'gitignore',
+ ['/%.config/git/ignore$'] = 'gitignore',
+ },
+ ['%.cfg'] = {
+ ['enlightenment/.*%.cfg$'] = 'c',
+ ['Eterm/.*%.cfg$'] = 'eterm',
+ ['baseq[2-3]/.*%.cfg$'] = 'quake',
+ ['id1/.*%.cfg$'] = 'quake',
+ ['quake[1-3]/.*%.cfg$'] = 'quake',
+ ['/tex/latex/.*%.cfg$'] = 'tex',
+ },
+ ['%.conf'] = {
+ ['^proftpd%.conf'] = starsetf('apachestyle'),
+ ['^access%.conf'] = detect_apache_dotconf,
+ ['^apache%.conf'] = detect_apache_dotconf,
+ ['^apache2%.conf'] = detect_apache_dotconf,
+ ['^httpd%.conf'] = detect_apache_dotconf,
+ ['^srm%.conf'] = detect_apache_dotconf,
+ ['asterisk/.*%.conf'] = starsetf('asterisk'),
+ ['asterisk.*/.*voicemail%.conf'] = starsetf('asteriskvm'),
+ ['^dictd.*%.conf$'] = 'dictdconf',
+ ['/lxqt/.*%.conf$'] = 'dosini',
+ ['/screengrab/.*%.conf$'] = 'dosini',
+ ['^${GNUPGHOME}/gpg%.conf$'] = 'gpg',
+ ['/boot/grub/grub%.conf$'] = 'grub',
+ ['^lilo%.conf'] = starsetf('lilo'),
+ ['^named.*%.conf$'] = 'named',
+ ['^rndc.*%.conf$'] = 'named',
+ ['/openvpn/.*/.*%.conf$'] = 'openvpn',
+ ['/%.ssh/.*%.conf$'] = 'sshconfig',
+ ['^%.?tmux.*%.conf$'] = 'tmux',
+ ['^%.?tmux.*%.conf'] = { 'tmux', { priority = -1 } },
+ ['/%.config/upstart/.*%.conf$'] = 'upstart',
+ ['/%.config/upstart/.*%.override$'] = 'upstart',
+ ['/%.init/.*%.conf$'] = 'upstart',
+ ['/xorg%.conf%.d/.*%.conf$'] = detect.xfree86_v4,
+ },
+ ['sst%.meta'] = {
+ ['%.%-sst%.meta$'] = 'sisu',
+ ['%._sst%.meta$'] = 'sisu',
+ ['%.sst%.meta$'] = 'sisu',
+ },
+ ['file'] = {
+ ['^Containerfile%.'] = starsetf('dockerfile'),
+ ['^Dockerfile%.'] = starsetf('dockerfile'),
+ ['[mM]akefile$'] = detect.make,
+ ['^[mM]akefile'] = starsetf('make'),
+ ['^[rR]akefile'] = starsetf('ruby'),
+ ['^%.profile'] = detect.sh,
+ },
+ ['fvwm'] = {
+ ['/%.fvwm/'] = starsetf('fvwm'),
+ ['fvwmrc'] = starsetf(detect.fvwm_v1),
+ ['fvwm95.*%.hook$'] = starsetf(detect.fvwm_v1),
+ ['fvwm2rc'] = starsetf(detect.fvwm_v2),
+ },
+ ['nginx'] = {
+ ['/nginx/.*%.conf$'] = 'nginx',
+ ['/usr/local/nginx/conf/'] = 'nginx',
+ ['nginx%.conf$'] = 'nginx',
+ ['^nginx.*%.conf$'] = 'nginx',
+ },
+ ['require'] = {
+ ['%-requirements%.txt$'] = 'requirements',
+ ['^requirements/.*%.txt$'] = 'requirements',
+ ['^requires/.*%.txt$'] = 'requirements',
+ },
+ ['s6'] = {
+ ['s6.*/down$'] = 'execline',
+ ['s6.*/finish$'] = 'execline',
+ ['s6.*/run$'] = 'execline',
+ ['s6.*/up$'] = 'execline',
+ ['^s6%-'] = 'execline',
+ },
+ ['utt'] = {
+ ['^mutt%-.*%-%w+$'] = 'mail',
+ ['^mutt' .. string.rep('[%w_-]', 6) .. '$'] = 'mail',
+ ['^muttng%-.*%-%w+$'] = 'mail',
+ ['^neomutt%-.*%-%w+$'] = 'mail',
+ ['^neomutt' .. string.rep('[%w_-]', 6) .. '$'] = 'mail',
+ -- muttngrc* and .muttngrc*
+ ['^%.?muttngrc'] = detect_muttrc,
+ -- muttrc* and .muttrc*
+ ['^%.?muttrc'] = detect_muttrc,
+ ['/%.mutt/muttrc'] = detect_muttrc,
+ ['/%.muttng/muttngrc'] = detect_muttrc,
+ ['/%.muttng/muttrc'] = detect_muttrc,
+ ['^Muttngrc'] = detect_muttrc,
+ ['^Muttrc'] = detect_muttrc,
+ -- neomuttrc* and .neomuttrc*
+ ['^%.?neomuttrc'] = detect_neomuttrc,
+ ['/%.neomutt/neomuttrc'] = detect_neomuttrc,
+ ['^Neomuttrc'] = detect_neomuttrc,
+ },
+ ['^%.'] = {
+ ['^%.cshrc'] = detect.csh,
+ ['^%.login'] = detect.csh,
+ ['^%.notmuch%-config%.'] = 'dosini',
+ ['^%.gitsendemail%.msg%.......$'] = 'gitsendemail',
+ ['^%.kshrc'] = detect.ksh,
+ ['^%.article%.%d+$'] = 'mail',
+ ['^%.letter%.%d+$'] = 'mail',
+ ['^%.reminders'] = starsetf('remind'),
+ ['^%.tcshrc'] = detect.tcsh,
+ ['^%.zcompdump'] = starsetf('zsh'),
+ },
+ ['proj%.user$'] = {
+ ['%.csproj%.user$'] = 'xml',
+ ['%.fsproj%.user$'] = 'xml',
+ ['%.vbproj%.user$'] = 'xml',
+ },
+ [''] = {
+ ['^bash%-fc[%-%.]'] = detect.bash,
+ ['/bind/db%.'] = starsetf('bindzone'),
+ ['/named/db%.'] = starsetf('bindzone'),
+ ['%.blade%.php$'] = 'blade',
+ ['^bzr_log%.'] = 'bzr',
+ ['^cabal%.project%.'] = starsetf('cabalproject'),
+ ['^sgml%.catalog'] = starsetf('catalog'),
+ ['hgrc$'] = 'cfg',
+ ['^[cC]hange[lL]og'] = starsetf(detect.changelog),
+ ['%.%.ch$'] = 'chill',
+ ['%.cmake%.in$'] = 'cmake',
+ ['^crontab%.'] = starsetf('crontab'),
+ ['^cvs%d+$'] = 'cvs',
+ ['^php%.ini%-'] = 'dosini',
+ ['^drac%.'] = starsetf('dracula'),
+ ['/dtrace/.*%.d$'] = 'dtrace',
+ ['esmtprc$'] = 'esmtprc',
+ ['/0%.orig/'] = detect.foam,
+ ['/0/'] = detect.foam,
+ ['/constant/g$'] = detect.foam,
+ ['Transport%.'] = detect.foam,
+ ['^[a-zA-Z0-9].*Dict%.'] = detect.foam,
+ ['^[a-zA-Z0-9].*Dict$'] = detect.foam,
+ ['^[a-zA-Z].*Properties%.'] = detect.foam,
+ ['^[a-zA-Z].*Properties$'] = detect.foam,
+ ['/tmp/lltmp'] = starsetf('gedcom'),
+ ['^gkrellmrc_.$'] = 'gkrellmrc',
+ ['^${GNUPGHOME}/options$'] = 'gpg',
+ ['/boot/grub/menu%.lst$'] = 'grub',
+ -- gtkrc* and .gtkrc*
+ ['^%.?gtkrc'] = starsetf('gtkrc'),
+ ['^${VIMRUNTIME}/doc/.*%.txt$'] = 'help',
+ ['^hg%-editor%-.*%.txt$'] = 'hgcommit',
+ ['%.html%.m4$'] = 'htmlm4',
+ ['^JAM.*%.'] = starsetf('jam'),
+ ['^Prl.*%.'] = starsetf('jam'),
+ ['%.properties_..$'] = 'jproperties',
+ ['%.properties_.._..$'] = 'jproperties',
+ ['%.properties_.._.._'] = starsetf('jproperties'),
+ ['^org%.eclipse%..*%.prefs$'] = 'jproperties',
+ ['^[jt]sconfig.*%.json$'] = 'jsonc',
+ ['^Config%.in%.'] = starsetf('kconfig'),
+ ['^Kconfig%.'] = starsetf('kconfig'),
+ ['/ldscripts/'] = 'ld',
+ ['lftp/rc$'] = 'lftp',
+ ['/LiteStep/.*/.*%.rc$'] = 'litestep',
+ ['^/tmp/SLRN[0-9A-Z.]+$'] = 'mail',
+ ['^ae%d+%.txt$'] = 'mail',
+ ['^pico%.%d+$'] = 'mail',
+ ['^reportbug%-'] = starsetf('mail'),
+ ['^snd%.%d+$'] = 'mail',
+ ['^rndc.*%.key$'] = 'named',
+ ['^tmac%.'] = starsetf('nroff'),
+ ['%.ml%.cppo$'] = 'ocaml',
+ ['%.mli%.cppo$'] = 'ocaml',
+ ['/octave/history$'] = 'octave',
+ ['%.opam%.locked$'] = 'opam',
+ ['%.opam%.template$'] = 'opam',
+ ['printcap'] = starsetf(function(path, bufnr)
+ return require('vim.filetype.detect').printcap('print')
+ end),
+ ['/queries/.*%.scm$'] = 'query', -- treesitter queries (Neovim only)
+ [',v$'] = 'rcs',
+ ['^svn%-commit.*%.tmp$'] = 'svn',
+ ['%.swift%.gyb$'] = 'swiftgyb',
+ ['termcap'] = starsetf(function(path, bufnr)
+ return require('vim.filetype.detect').printcap('term')
+ end),
+ ['%.t%.html$'] = 'tilde',
+ ['%.vhdl_[0-9]'] = starsetf('vhdl'),
+ ['vimrc'] = starsetf('vim'),
+ ['/Xresources/'] = starsetf('xdefaults'),
+ ['/app%-defaults/'] = starsetf('xdefaults'),
+ ['^Xresources'] = starsetf('xdefaults'),
+ -- Increase priority to run before the pattern below
+ ['^XF86Config%-4'] = starsetf(detect.xfree86_v4, { priority = -math.huge + 1 }),
+ ['^XF86Config'] = starsetf(detect.xfree86_v3),
+ ['Xmodmap$'] = 'xmodmap',
+ ['xmodmap'] = starsetf('xmodmap'),
+ -- .zlog* and zlog*
+ ['^%.?zlog'] = starsetf('zsh'),
+ -- .zsh* and zsh*
+ ['^%.?zsh'] = starsetf('zsh'),
+ -- Ignored extension
+ ['~$'] = function(path, bufnr)
+ local short = path:gsub('~+$', '', 1)
+ if path ~= short and short ~= '' then
+ return M.match({ buf = bufnr, filename = fn.fnameescape(short) })
+ end
+ end,
},
- ['.*%.git/modules/.*/config'] = 'gitconfig',
- ['.*%.git/modules/config'] = 'gitconfig',
- ['.*%.git/config'] = 'gitconfig',
- ['.*/etc/gitconfig'] = 'gitconfig',
- ['.*/%.config/git/config'] = 'gitconfig',
- ['.*%.git/config%.worktree'] = 'gitconfig',
- ['.*%.git/worktrees/.*/config%.worktree'] = 'gitconfig',
- ['${XDG_CONFIG_HOME}/git/config'] = 'gitconfig',
- ['.*%.git/info/attributes'] = 'gitattributes',
- ['.*/etc/gitattributes'] = 'gitattributes',
- ['.*/%.config/git/attributes'] = 'gitattributes',
- ['${XDG_CONFIG_HOME}/git/attributes'] = 'gitattributes',
- ['.*%.git/info/exclude'] = 'gitignore',
- ['.*/%.config/git/ignore'] = 'gitignore',
- ['${XDG_CONFIG_HOME}/git/ignore'] = 'gitignore',
- ['%.gitsendemail%.msg%.......'] = 'gitsendemail',
- ['gkrellmrc_.'] = 'gkrellmrc',
- ['.*/usr/.*/gnupg/options%.skel'] = 'gpg',
- ['.*/%.gnupg/options'] = 'gpg',
- ['.*/%.gnupg/gpg%.conf'] = 'gpg',
- ['${GNUPGHOME}/options'] = 'gpg',
- ['${GNUPGHOME}/gpg%.conf'] = 'gpg',
- ['.*/etc/group'] = 'group',
- ['.*/etc/gshadow'] = 'group',
- ['.*/etc/group%.edit'] = 'group',
- ['.*/var/backups/gshadow%.bak'] = 'group',
- ['.*/etc/group%-'] = 'group',
- ['.*/etc/gshadow%-'] = 'group',
- ['.*/var/backups/group%.bak'] = 'group',
- ['.*/etc/gshadow%.edit'] = 'group',
- ['.*/boot/grub/grub%.conf'] = 'grub',
- ['.*/boot/grub/menu%.lst'] = 'grub',
- ['.*/etc/grub%.conf'] = 'grub',
- -- gtkrc* and .gtkrc*
- ['%.?gtkrc.*'] = starsetf('gtkrc'),
- ['${VIMRUNTIME}/doc/.*%.txt'] = 'help',
- ['hg%-editor%-.*%.txt'] = 'hgcommit',
- ['.*/etc/host%.conf'] = 'hostconf',
- ['.*/etc/hosts%.deny'] = 'hostsaccess',
- ['.*/etc/hosts%.allow'] = 'hostsaccess',
- ['.*%.html%.m4'] = 'htmlm4',
- ['.*/%.i3/config'] = 'i3config',
- ['.*/i3/config'] = 'i3config',
- ['.*/%.icewm/menu'] = 'icemenu',
- ['.*/etc/initng/.*/.*%.i'] = 'initng',
- ['JAM.*%..*'] = starsetf('jam'),
- ['Prl.*%..*'] = starsetf('jam'),
- ['.*%.properties_..'] = 'jproperties',
- ['.*%.properties_.._..'] = 'jproperties',
- ['org%.eclipse%..*%.prefs'] = 'jproperties',
- ['.*%.properties_.._.._.*'] = starsetf('jproperties'),
- ['[jt]sconfig.*%.json'] = 'jsonc',
- ['[jJ]ustfile'] = 'just',
- ['Kconfig%..*'] = starsetf('kconfig'),
- ['Config%.in%..*'] = starsetf('kconfig'),
- ['.*%.[Ss][Uu][Bb]'] = 'krl',
- ['lilo%.conf.*'] = starsetf('lilo'),
- ['.*/etc/logcheck/.*%.d.*/.*'] = starsetf('logcheck'),
- ['.*/ldscripts/.*'] = 'ld',
- ['.*lftp/rc'] = 'lftp',
- ['.*/%.libao'] = 'libao',
- ['.*/etc/libao%.conf'] = 'libao',
- ['.*/etc/.*limits%.conf'] = 'limits',
- ['.*/etc/limits'] = 'limits',
- ['.*/etc/.*limits%.d/.*%.conf'] = 'limits',
- ['.*/supertux2/config'] = 'lisp',
- ['.*/LiteStep/.*/.*%.rc'] = 'litestep',
- ['.*/etc/login%.access'] = 'loginaccess',
- ['.*/etc/login%.defs'] = 'logindefs',
- ['%.letter%.%d+'] = 'mail',
- ['%.article%.%d+'] = 'mail',
- ['/tmp/SLRN[0-9A-Z.]+'] = 'mail',
- ['ae%d+%.txt'] = 'mail',
- ['pico%.%d+'] = 'mail',
- ['mutt%-.*%-%w+'] = 'mail',
- ['muttng%-.*%-%w+'] = 'mail',
- ['neomutt%-.*%-%w+'] = 'mail',
- ['mutt' .. string.rep('[%w_-]', 6)] = 'mail',
- ['neomutt' .. string.rep('[%w_-]', 6)] = 'mail',
- ['snd%.%d+'] = 'mail',
- ['reportbug%-.*'] = starsetf('mail'),
- ['.*/etc/mail/aliases'] = 'mailaliases',
- ['.*/etc/aliases'] = 'mailaliases',
- ['.*[mM]akefile'] = 'make',
- ['[mM]akefile.*'] = starsetf('make'),
- ['.*/etc/man%.conf'] = 'manconf',
- ['.*/log/auth'] = 'messages',
- ['.*/log/cron'] = 'messages',
- ['.*/log/daemon'] = 'messages',
- ['.*/log/debug'] = 'messages',
- ['.*/log/kern'] = 'messages',
- ['.*/log/lpr'] = 'messages',
- ['.*/log/mail'] = 'messages',
- ['.*/log/messages'] = 'messages',
- ['.*/log/news/news'] = 'messages',
- ['.*/log/syslog'] = 'messages',
- ['.*/log/user'] = 'messages',
- ['.*/log/auth%.log'] = 'messages',
- ['.*/log/cron%.log'] = 'messages',
- ['.*/log/daemon%.log'] = 'messages',
- ['.*/log/debug%.log'] = 'messages',
- ['.*/log/kern%.log'] = 'messages',
- ['.*/log/lpr%.log'] = 'messages',
- ['.*/log/mail%.log'] = 'messages',
- ['.*/log/messages%.log'] = 'messages',
- ['.*/log/news/news%.log'] = 'messages',
- ['.*/log/syslog%.log'] = 'messages',
- ['.*/log/user%.log'] = 'messages',
- ['.*/log/auth%.err'] = 'messages',
- ['.*/log/cron%.err'] = 'messages',
- ['.*/log/daemon%.err'] = 'messages',
- ['.*/log/debug%.err'] = 'messages',
- ['.*/log/kern%.err'] = 'messages',
- ['.*/log/lpr%.err'] = 'messages',
- ['.*/log/mail%.err'] = 'messages',
- ['.*/log/messages%.err'] = 'messages',
- ['.*/log/news/news%.err'] = 'messages',
- ['.*/log/syslog%.err'] = 'messages',
- ['.*/log/user%.err'] = 'messages',
- ['.*/log/auth%.info'] = 'messages',
- ['.*/log/cron%.info'] = 'messages',
- ['.*/log/daemon%.info'] = 'messages',
- ['.*/log/debug%.info'] = 'messages',
- ['.*/log/kern%.info'] = 'messages',
- ['.*/log/lpr%.info'] = 'messages',
- ['.*/log/mail%.info'] = 'messages',
- ['.*/log/messages%.info'] = 'messages',
- ['.*/log/news/news%.info'] = 'messages',
- ['.*/log/syslog%.info'] = 'messages',
- ['.*/log/user%.info'] = 'messages',
- ['.*/log/auth%.warn'] = 'messages',
- ['.*/log/cron%.warn'] = 'messages',
- ['.*/log/daemon%.warn'] = 'messages',
- ['.*/log/debug%.warn'] = 'messages',
- ['.*/log/kern%.warn'] = 'messages',
- ['.*/log/lpr%.warn'] = 'messages',
- ['.*/log/mail%.warn'] = 'messages',
- ['.*/log/messages%.warn'] = 'messages',
- ['.*/log/news/news%.warn'] = 'messages',
- ['.*/log/syslog%.warn'] = 'messages',
- ['.*/log/user%.warn'] = 'messages',
- ['.*/log/auth%.crit'] = 'messages',
- ['.*/log/cron%.crit'] = 'messages',
- ['.*/log/daemon%.crit'] = 'messages',
- ['.*/log/debug%.crit'] = 'messages',
- ['.*/log/kern%.crit'] = 'messages',
- ['.*/log/lpr%.crit'] = 'messages',
- ['.*/log/mail%.crit'] = 'messages',
- ['.*/log/messages%.crit'] = 'messages',
- ['.*/log/news/news%.crit'] = 'messages',
- ['.*/log/syslog%.crit'] = 'messages',
- ['.*/log/user%.crit'] = 'messages',
- ['.*/log/auth%.notice'] = 'messages',
- ['.*/log/cron%.notice'] = 'messages',
- ['.*/log/daemon%.notice'] = 'messages',
- ['.*/log/debug%.notice'] = 'messages',
- ['.*/log/kern%.notice'] = 'messages',
- ['.*/log/lpr%.notice'] = 'messages',
- ['.*/log/mail%.notice'] = 'messages',
- ['.*/log/messages%.notice'] = 'messages',
- ['.*/log/news/news%.notice'] = 'messages',
- ['.*/log/syslog%.notice'] = 'messages',
- ['.*/log/user%.notice'] = 'messages',
- ['.*%.[Mm][Oo][Dd]'] = detect.mod,
- ['.*/etc/modules%.conf'] = 'modconf',
- ['.*/etc/conf%.modules'] = 'modconf',
- ['.*/etc/modules'] = 'modconf',
- ['.*/etc/modprobe%..*'] = starsetf('modconf'),
- ['.*/etc/modutils/.*'] = starsetf(function(path, bufnr)
- if fn.executable(fn.expand(path)) ~= 1 then
- return 'modconf'
- end
- end),
- ['.*%.[mi][3g]'] = 'modula3',
- ['Muttrc'] = 'muttrc',
- ['Muttngrc'] = 'muttrc',
- ['.*/etc/Muttrc%.d/.*'] = starsetf('muttrc'),
- ['.*/%.mplayer/config'] = 'mplayerconf',
- ['Muttrc.*'] = detect_muttrc,
- ['Muttngrc.*'] = detect_muttrc,
- -- muttrc* and .muttrc*
- ['%.?muttrc.*'] = detect_muttrc,
- -- muttngrc* and .muttngrc*
- ['%.?muttngrc.*'] = detect_muttrc,
- ['.*/%.mutt/muttrc.*'] = detect_muttrc,
- ['.*/%.muttng/muttrc.*'] = detect_muttrc,
- ['.*/%.muttng/muttngrc.*'] = detect_muttrc,
- ['rndc.*%.conf'] = 'named',
- ['rndc.*%.key'] = 'named',
- ['named.*%.conf'] = 'named',
- ['.*/etc/nanorc'] = 'nanorc',
- ['.*%.NS[ACGLMNPS]'] = 'natural',
- ['Neomuttrc.*'] = detect_neomuttrc,
- -- neomuttrc* and .neomuttrc*
- ['%.?neomuttrc.*'] = detect_neomuttrc,
- ['.*/%.neomutt/neomuttrc.*'] = detect_neomuttrc,
- ['nginx.*%.conf'] = 'nginx',
- ['.*/etc/nginx/.*'] = 'nginx',
- ['.*nginx%.conf'] = 'nginx',
- ['.*/nginx/.*%.conf'] = 'nginx',
- ['.*/usr/local/nginx/conf/.*'] = 'nginx',
- ['.*%.[1-9]'] = detect.nroff,
- ['.*%.ml%.cppo'] = 'ocaml',
- ['.*%.mli%.cppo'] = 'ocaml',
- ['.*/octave/history'] = 'octave',
- ['.*%.opam%.template'] = 'opam',
- ['.*/openvpn/.*/.*%.conf'] = 'openvpn',
- ['.*%.[Oo][Pp][Ll]'] = 'opl',
- ['.*/etc/pam%.conf'] = 'pamconf',
- ['.*/etc/pam%.d/.*'] = starsetf('pamconf'),
- ['.*/etc/passwd%-'] = 'passwd',
- ['.*/etc/shadow'] = 'passwd',
- ['.*/etc/shadow%.edit'] = 'passwd',
- ['.*/var/backups/shadow%.bak'] = 'passwd',
- ['.*/var/backups/passwd%.bak'] = 'passwd',
- ['.*/etc/passwd'] = 'passwd',
- ['.*/etc/passwd%.edit'] = 'passwd',
- ['.*/etc/shadow%-'] = 'passwd',
- ['%.?gitolite%.rc'] = 'perl',
- ['example%.gitolite%.rc'] = 'perl',
- ['.*%.php%d'] = 'php',
- ['.*/%.pinforc'] = 'pinfo',
- ['.*/etc/pinforc'] = 'pinfo',
- ['.*%.[Pp][Rr][Gg]'] = detect.prg,
- ['.*/etc/protocols'] = 'protocols',
- ['.*printcap.*'] = starsetf(function(path, bufnr)
- return require('vim.filetype.detect').printcap('print')
- end),
- ['.*baseq[2-3]/.*%.cfg'] = 'quake',
- ['.*quake[1-3]/.*%.cfg'] = 'quake',
- ['.*id1/.*%.cfg'] = 'quake',
- ['.*/queries/.*%.scm'] = 'query', -- treesitter queries (Neovim only)
- ['.*,v'] = 'rcs',
- ['%.reminders.*'] = starsetf('remind'),
- ['.*%-requirements%.txt'] = 'requirements',
- ['requirements/.*%.txt'] = 'requirements',
- ['requires/.*%.txt'] = 'requirements',
- ['[rR]akefile.*'] = starsetf('ruby'),
- ['[rR]antfile'] = 'ruby',
- ['[rR]akefile'] = 'ruby',
- ['.*/etc/sensors%.d/[^.].*'] = starsetf('sensors'),
- ['.*/etc/sensors%.conf'] = 'sensors',
- ['.*/etc/sensors3%.conf'] = 'sensors',
- ['.*/etc/services'] = 'services',
- ['.*/etc/serial%.conf'] = 'setserial',
- ['.*/etc/udev/cdsymlinks%.conf'] = 'sh',
- ['.*/neofetch/config%.conf'] = 'sh',
- ['%.bash[_%-]aliases'] = detect.bash,
- ['%.bash[_%-]history'] = detect.bash,
- ['%.bash[_%-]logout'] = detect.bash,
- ['%.bash[_%-]profile'] = detect.bash,
- ['%.kshrc.*'] = detect.ksh,
- ['%.profile.*'] = detect.sh,
- ['.*/etc/profile'] = detect.sh,
- ['bash%-fc[%-%.].*'] = detect.bash,
- ['%.tcshrc.*'] = detect.tcsh,
- ['.*/etc/sudoers%.d/.*'] = starsetf('sudoers'),
- ['.*%._sst%.meta'] = 'sisu',
- ['.*%.%-sst%.meta'] = 'sisu',
- ['.*%.sst%.meta'] = 'sisu',
- ['.*/etc/slp%.conf'] = 'slpconf',
- ['.*/etc/slp%.reg'] = 'slpreg',
- ['.*/etc/slp%.spi'] = 'slpspi',
- ['.*/etc/ssh/ssh_config%.d/.*%.conf'] = 'sshconfig',
- ['.*/%.ssh/config'] = 'sshconfig',
- ['.*/%.ssh/.*%.conf'] = 'sshconfig',
- ['.*/etc/ssh/sshd_config%.d/.*%.conf'] = 'sshdconfig',
- ['.*%.[Ss][Rr][Cc]'] = detect.src,
- ['.*/etc/sudoers'] = 'sudoers',
- ['svn%-commit.*%.tmp'] = 'svn',
- ['.*/sway/config'] = 'swayconfig',
- ['.*/%.sway/config'] = 'swayconfig',
- ['.*%.swift%.gyb'] = 'swiftgyb',
- ['.*%.[Ss][Yy][Ss]'] = detect.sys,
- ['.*/etc/sysctl%.conf'] = 'sysctl',
- ['.*/etc/sysctl%.d/.*%.conf'] = 'sysctl',
- ['.*/systemd/.*%.automount'] = 'systemd',
- ['.*/systemd/.*%.dnssd'] = 'systemd',
- ['.*/systemd/.*%.link'] = 'systemd',
- ['.*/systemd/.*%.mount'] = 'systemd',
- ['.*/systemd/.*%.netdev'] = 'systemd',
- ['.*/systemd/.*%.network'] = 'systemd',
- ['.*/systemd/.*%.nspawn'] = 'systemd',
- ['.*/systemd/.*%.path'] = 'systemd',
- ['.*/systemd/.*%.service'] = 'systemd',
- ['.*/systemd/.*%.slice'] = 'systemd',
- ['.*/systemd/.*%.socket'] = 'systemd',
- ['.*/systemd/.*%.swap'] = 'systemd',
- ['.*/systemd/.*%.target'] = 'systemd',
- ['.*/systemd/.*%.timer'] = 'systemd',
- ['.*/etc/systemd/.*%.conf%.d/.*%.conf'] = 'systemd',
- ['.*/%.config/systemd/user/.*%.d/.*%.conf'] = 'systemd',
- ['.*/etc/systemd/system/.*%.d/.*%.conf'] = 'systemd',
- ['.*/etc/systemd/system/.*%.d/%.#.*'] = 'systemd',
- ['.*/etc/systemd/system/%.#.*'] = 'systemd',
- ['.*/%.config/systemd/user/.*%.d/%.#.*'] = 'systemd',
- ['.*/%.config/systemd/user/%.#.*'] = 'systemd',
- ['.*termcap.*'] = starsetf(function(path, bufnr)
- return require('vim.filetype.detect').printcap('term')
- end),
- ['.*/tex/latex/.*%.cfg'] = 'tex',
- ['.*%.t%.html'] = 'tilde',
- ['%.?tmux.*%.conf'] = 'tmux',
- ['%.?tmux.*%.conf.*'] = { 'tmux', { priority = -1 } },
- ['.*/%.cargo/config'] = 'toml',
- ['.*/%.cargo/credentials'] = 'toml',
- ['.*/etc/udev/udev%.conf'] = 'udevconf',
- ['.*/etc/udev/permissions%.d/.*%.permissions'] = 'udevperm',
- ['.*/etc/updatedb%.conf'] = 'updatedb',
- ['.*/%.init/.*%.override'] = 'upstart',
- ['.*/usr/share/upstart/.*%.conf'] = 'upstart',
- ['.*/%.config/upstart/.*%.override'] = 'upstart',
- ['.*/etc/init/.*%.conf'] = 'upstart',
- ['.*/etc/init/.*%.override'] = 'upstart',
- ['.*/%.config/upstart/.*%.conf'] = 'upstart',
- ['.*/%.init/.*%.conf'] = 'upstart',
- ['.*/usr/share/upstart/.*%.override'] = 'upstart',
- ['.*%.[Ll][Oo][Gg]'] = detect.log,
- ['.*/etc/config/.*'] = starsetf(detect.uci),
- ['.*%.vhdl_[0-9].*'] = starsetf('vhdl'),
- ['.*%.ws[fc]'] = 'wsh',
- ['.*/Xresources/.*'] = starsetf('xdefaults'),
- ['.*/app%-defaults/.*'] = starsetf('xdefaults'),
- ['.*/etc/xinetd%.conf'] = 'xinetd',
- ['.*/usr/share/X11/xkb/compat/.*'] = starsetf('xkb'),
- ['.*/usr/share/X11/xkb/geometry/.*'] = starsetf('xkb'),
- ['.*/usr/share/X11/xkb/keycodes/.*'] = starsetf('xkb'),
- ['.*/usr/share/X11/xkb/symbols/.*'] = starsetf('xkb'),
- ['.*/usr/share/X11/xkb/types/.*'] = starsetf('xkb'),
- ['.*/etc/blkid%.tab'] = 'xml',
- ['.*/etc/blkid%.tab%.old'] = 'xml',
- ['.*%.vbproj%.user'] = 'xml',
- ['.*%.fsproj%.user'] = 'xml',
- ['.*%.csproj%.user'] = 'xml',
- ['.*/etc/xdg/menus/.*%.menu'] = 'xml',
- ['.*Xmodmap'] = 'xmodmap',
- ['.*/etc/zprofile'] = 'zsh',
- ['.*vimrc.*'] = starsetf('vim'),
- ['Xresources.*'] = starsetf('xdefaults'),
- ['.*/etc/xinetd%.d/.*'] = starsetf('xinetd'),
- ['.*xmodmap.*'] = starsetf('xmodmap'),
- ['.*/xorg%.conf%.d/.*%.conf'] = detect.xfree86_v4,
- -- Increase priority to run before the pattern below
- ['XF86Config%-4.*'] = starsetf(detect.xfree86_v4, { priority = -math.huge + 1 }),
- ['XF86Config.*'] = starsetf(detect.xfree86_v3),
- ['.*/%.bundle/config'] = 'yaml',
- ['%.zcompdump.*'] = starsetf('zsh'),
- -- .zlog* and zlog*
- ['%.?zlog.*'] = starsetf('zsh'),
- -- .zsh* and zsh*
- ['%.?zsh.*'] = starsetf('zsh'),
- -- Ignored extension
- ['.*~'] = function(path, bufnr)
- local short = path:gsub('~+$', '', 1)
- if path ~= short and short ~= '' then
- return M.match({ buf = bufnr, filename = fn.fnameescape(short) })
- end
- end,
-- END PATTERN
}
-- luacheck: pop
-- luacheck: pop
---- @param t vim.filetype.mapping
+--- Lookup table/cache for patterns
+--- @alias vim.filetype.pattern_cache { has_env: boolean, has_slash: boolean }
+--- @type table<string,vim.filetype.pattern_cache>
+local pattern_lookup = {}
+
+local function compare_by_priority(a, b)
+ return a[next(a)][2].priority > b[next(b)][2].priority
+end
+
+--- @param pat string
+--- @return { has_env: boolean, has_slash: boolean }
+local function parse_pattern(pat)
+ return { has_env = pat:find('%$%b{}') ~= nil, has_slash = pat:find('/') ~= nil }
+end
+
+--- @param t table<string,vim.filetype.mapping>
+--- @return vim.filetype.mapping[]
--- @return vim.filetype.mapping[]
local function sort_by_priority(t)
- local sorted = {} --- @type vim.filetype.mapping[]
- for k, v in pairs(t) do
- local ft = type(v) == 'table' and v[1] or v
- assert(
- type(ft) == 'string' or type(ft) == 'function',
- 'Expected string or function for filetype'
- )
-
- local opts = (type(v) == 'table' and type(v[2]) == 'table') and v[2] or {}
- if not opts.priority then
- opts.priority = 0
+ -- Separate patterns with non-negative and negative priority because they
+ -- will be processed separately
+ local pos = {} --- @type vim.filetype.mapping[]
+ local neg = {} --- @type vim.filetype.mapping[]
+ for parent, ft_map in pairs(t) do
+ pattern_lookup[parent] = pattern_lookup[parent] or parse_pattern(parent)
+ for pat, maptbl in pairs(ft_map) do
+ local ft = type(maptbl) == 'table' and maptbl[1] or maptbl
+ assert(
+ type(ft) == 'string' or type(ft) == 'function',
+ 'Expected string or function for filetype'
+ )
+
+ -- Parse pattern for common data and cache it once
+ pattern_lookup[pat] = pattern_lookup[pat] or parse_pattern(pat)
+
+ local opts = (type(maptbl) == 'table' and type(maptbl[2]) == 'table') and maptbl[2] or {}
+ opts.parent = opts.parent or parent
+ opts.priority = opts.priority or 0
+
+ table.insert(opts.priority >= 0 and pos or neg, { [pat] = { ft, opts } })
end
- table.insert(sorted, { [k] = { ft, opts } })
end
- table.sort(sorted, function(a, b)
- return a[next(a)][2].priority > b[next(b)][2].priority
- end)
- return sorted
+
+ table.sort(pos, compare_by_priority)
+ table.sort(neg, compare_by_priority)
+ return pos, neg
end
-local pattern_sorted = sort_by_priority(pattern)
+local pattern_sorted_pos, pattern_sorted_neg = sort_by_priority(pattern)
--- @param path string
--- @param as_pattern? true
@@ -2302,7 +2501,7 @@ end
--- ['.*/etc/foo/.*%.conf'] = { 'dosini', { priority = 10 } },
--- -- A pattern containing an environment variable
--- ['${XDG_CONFIG_HOME}/foo/git'] = 'git',
---- ['README.(%a+)$'] = function(path, bufnr, ext)
+--- ['.*README.(%a+)'] = function(path, bufnr, ext)
--- if ext == 'md' then
--- return 'markdown'
--- elseif ext == 'rst' then
@@ -2344,11 +2543,16 @@ function M.add(filetypes)
end
for k, v in pairs(filetypes.pattern or {}) do
- pattern[normalize_path(k, true)] = v
+ -- Add to "match all" parent pattern (might be better to optimize later or document
+ -- supplying `opts.parent` directly)
+ -- User patterns are assumed to be implicitly anchored (as in Vim)
+ pattern['']['^' .. normalize_path(k, true) .. '$'] = v
end
if filetypes.pattern then
- pattern_sorted = sort_by_priority(pattern)
+ -- TODO: full resorting might be expensive with a lot of separate `vim.filetype.add()` calls.
+ -- Consider inserting new patterns precisely into already sorted lists of built-in patterns.
+ pattern_sorted_pos, pattern_sorted_neg = sort_by_priority(pattern)
end
end
@@ -2392,46 +2596,81 @@ local function dispatch(ft, path, bufnr, ...)
return ft0, on_detect
end
---- Lookup table/cache for patterns that contain an environment variable pattern, e.g. ${SOME_VAR}.
---- @type table<string,boolean>
-local expand_env_lookup = {}
+--- @param pat string
+--- @return boolean
+--- @return string
+local function expand_envvar_pattern(pat)
+ local some_env_missing = false
+ local expanded = pat:gsub('%${(%S-)}', function(env)
+ local val = vim.env[env] --- @type string?
+ some_env_missing = some_env_missing or val == nil
+ return vim.pesc(val or '')
+ end)
+ return some_env_missing, expanded
+end
--- @param name string
--- @param path string
--- @param tail string
--- @param pat string
---- @return string|false?
-local function match_pattern(name, path, tail, pat)
- if expand_env_lookup[pat] == nil then
- expand_env_lookup[pat] = pat:find('%${') ~= nil
- end
- if expand_env_lookup[pat] then
- local return_early --- @type true?
- --- @type string
- pat = pat:gsub('%${(%S-)}', function(env)
- -- If an environment variable is present in the pattern but not set, there is no match
- if not vim.env[env] then
- return_early = true
- return nil
- end
- return vim.pesc(vim.env[env])
- end)
- if return_early then
- return false
+--- @param try_all_candidates boolean
+--- @return string?
+local function match_pattern(name, path, tail, pat, try_all_candidates)
+ local pat_cache = pattern_lookup[pat]
+ local has_slash = pat_cache.has_slash
+
+ if pat_cache.has_env then
+ local some_env_missing, expanded = expand_envvar_pattern(pat)
+ -- If any environment variable is present in the pattern but not set, there is no match
+ if some_env_missing then
+ return nil
end
+ pat, has_slash = expanded, expanded:find('/') ~= nil
end
- -- If the pattern contains a / match against the full path, otherwise just the tail
- local fullpat = '^' .. pat .. '$'
+ -- Try all possible candidates to make parent patterns not depend on slash presence
+ if try_all_candidates then
+ return (path:match(pat) or name:match(pat) or tail:match(pat))
+ end
- if pat:find('/') then
+ -- If the pattern contains a / match against the full path, otherwise just the tail
+ if has_slash then
-- Similar to |autocmd-pattern|, if the pattern contains a '/' then check for a match against
-- both the short file name (as typed) and the full file name (after expanding to full path
-- and resolving symlinks)
- return (name:match(fullpat) or path:match(fullpat))
+ return (name:match(pat) or path:match(pat))
end
- return (tail:match(fullpat))
+ return (tail:match(pat))
+end
+
+--- @param name string
+--- @param path string
+--- @param tail string
+--- @param pattern_sorted vim.filetype.mapping[]
+--- @param parent_matches table<string,boolean>
+--- @param bufnr integer?
+local function match_pattern_sorted(name, path, tail, pattern_sorted, parent_matches, bufnr)
+ for i = 1, #pattern_sorted do
+ local pat, ft_data = next(pattern_sorted[i])
+
+ local parent = ft_data[2].parent
+ local parent_is_matched = parent_matches[parent]
+ if parent_is_matched == nil then
+ parent_matches[parent] = match_pattern(name, path, tail, parent, true) ~= nil
+ parent_is_matched = parent_matches[parent]
+ end
+
+ if parent_is_matched then
+ local matches = match_pattern(name, path, tail, pat, false)
+ if matches then
+ local ft, on_detect = dispatch(ft_data[1], path, bufnr, matches)
+ if ft then
+ return ft, on_detect
+ end
+ end
+ end
+ end
end
--- @class vim.filetype.match.args
@@ -2527,23 +2766,12 @@ function M.match(args)
end
-- Next, check the file path against available patterns with non-negative priority
- local j = 1
- for i, v in ipairs(pattern_sorted) do
- local k = next(v)
- local opts = v[k][2]
- if opts.priority < 0 then
- j = i
- break
- end
-
- local filetype = v[k][1]
- local matches = match_pattern(name, path, tail, k)
- if matches then
- ft, on_detect = dispatch(filetype, path, bufnr, matches)
- if ft then
- return ft, on_detect
- end
- end
+ -- Cache match results of all parent patterns to improve performance
+ local parent_matches = {}
+ ft, on_detect =
+ match_pattern_sorted(name, path, tail, pattern_sorted_pos, parent_matches, bufnr)
+ if ft then
+ return ft, on_detect
end
-- Next, check file extension
@@ -2556,18 +2784,10 @@ function M.match(args)
end
-- Next, check patterns with negative priority
- for i = j, #pattern_sorted do
- local v = pattern_sorted[i]
- local k = next(v)
-
- local filetype = v[k][1]
- local matches = match_pattern(name, path, tail, k)
- if matches then
- ft, on_detect = dispatch(filetype, path, bufnr, matches)
- if ft then
- return ft, on_detect
- end
- end
+ ft, on_detect =
+ match_pattern_sorted(name, path, tail, pattern_sorted_neg, parent_matches, bufnr)
+ if ft then
+ return ft, on_detect
end
end
@@ -2583,20 +2803,24 @@ function M.match(args)
contents = M._getlines(bufnr)
end
end
- -- If name is nil, catch any errors from the contents filetype detection function.
- -- If the function tries to use the filename that is nil then it will fail,
- -- but this enables checks which do not need a filename to still work.
- local ok
- ok, ft, on_detect = pcall(
- require('vim.filetype.detect').match_contents,
- contents,
- name,
- function(ext)
- return dispatch(extension[ext], name, bufnr)
+
+ -- Match based solely on content only if there is any content (for performance)
+ if not (#contents == 1 and contents[1] == '') then
+ -- If name is nil, catch any errors from the contents filetype detection function.
+ -- If the function tries to use the filename that is nil then it will fail,
+ -- but this enables checks which do not need a filename to still work.
+ local ok
+ ok, ft, on_detect = pcall(
+ require('vim.filetype.detect').match_contents,
+ contents,
+ name,
+ function(ext)
+ return dispatch(extension[ext], name, bufnr)
+ end
+ )
+ if ok then
+ return ft, on_detect
end
- )
- if ok then
- return ft, on_detect
end
end
end
@@ -2615,6 +2839,7 @@ end
--- Note: this uses |nvim_get_option_value()| but caches the result.
--- This means |ftplugin| and |FileType| autocommands are only
--- triggered once and may not reflect later changes.
+--- @since 11
--- @param filetype string Filetype
--- @param option string Option name
--- @return string|boolean|integer: Option value
diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua
index ba86d8de5a..1cc81b177f 100644
--- a/runtime/lua/vim/filetype/detect.lua
+++ b/runtime/lua/vim/filetype/detect.lua
@@ -450,7 +450,7 @@ local function modula2(bufnr)
return 'modula2',
function(b)
- vim.api.nvim_buf_call(b, function()
+ vim._with({ buf = b }, function()
fn['modula2#SetDialect'](dialect, extension)
end)
end
@@ -472,6 +472,41 @@ function M.def(_, bufnr)
end
--- @type vim.filetype.mapfn
+function M.dsp(path, bufnr)
+ if vim.g.filetype_dsp then
+ return vim.g.filetype_dsp
+ end
+
+ -- Test the filename
+ local file_name = fn.fnamemodify(path, ':t')
+ if file_name:find('^[mM]akefile.*$') then
+ return 'make'
+ end
+
+ -- Test the file contents
+ for _, line in ipairs(getlines(bufnr, 1, 200)) do
+ if
+ findany(line, {
+ -- Check for comment style
+ [[#.*]],
+ -- Check for common lines
+ [[^.*Microsoft Developer Studio Project File.*$]],
+ [[^!MESSAGE This is not a valid makefile\..+$]],
+ -- Check for keywords
+ [[^!(IF,ELSEIF,ENDIF).*$]],
+ -- Check for common assignments
+ [[^SOURCE=.*$]],
+ })
+ then
+ return 'make'
+ end
+ end
+
+ -- Otherwise, assume we have a Faust file
+ return 'faust'
+end
+
+--- @type vim.filetype.mapfn
function M.e(_, bufnr)
if vim.g.filetype_euphoria then
return vim.g.filetype_euphoria
@@ -594,7 +629,7 @@ function M.frm(_, bufnr)
end
--- @type vim.filetype.mapfn
-function M.fvwm_1(_, _)
+function M.fvwm_v1(_, _)
return 'fvwm', function(bufnr)
vim.b[bufnr].fvwm_version = 1
end
@@ -650,13 +685,58 @@ function M.header(_, bufnr)
end
end
+--- Recursively search for Hare source files in a directory and any
+--- subdirectories, up to a given depth.
+--- @param dir string
+--- @param depth number
+--- @return boolean
+local function is_hare_module(dir, depth)
+ depth = math.max(depth, 0)
+ for name, _ in vim.fs.dir(dir, { depth = depth + 1 }) do
+ if name:find('%.ha$') then
+ return true
+ end
+ end
+ return false
+end
+
+--- @type vim.filetype.mapfn
+function M.haredoc(path, _)
+ if vim.g.filetype_haredoc then
+ if is_hare_module(vim.fs.dirname(path), vim.g.haredoc_search_depth or 1) then
+ return 'haredoc'
+ end
+ end
+end
+
--- @type vim.filetype.mapfn
function M.html(_, bufnr)
- for _, line in ipairs(getlines(bufnr, 1, 10)) do
- if matchregex(line, [[\<DTD\s\+XHTML\s]]) then
+ -- Disabled for the reasons mentioned here:
+ -- https://github.com/vim/vim/pull/13594#issuecomment-1834465890
+ -- local filename = fn.fnamemodify(path, ':t')
+ -- if filename:find('%.component%.html$') then
+ -- return 'htmlangular'
+ -- end
+
+ for _, line in ipairs(getlines(bufnr, 1, 40)) do
+ if
+ matchregex(
+ line,
+ [[@\(if\|for\|defer\|switch\)\|\*\(ngIf\|ngFor\|ngSwitch\|ngTemplateOutlet\)\|ng-template\|ng-content\|{{.*}}]]
+ )
+ then
+ return 'htmlangular'
+ elseif matchregex(line, [[\<DTD\s\+XHTML\s]]) then
return 'xhtml'
- elseif matchregex(line, [[\c{%\s*\(extends\|block\|load\)\>\|{#\s\+]]) then
+ elseif
+ matchregex(
+ line,
+ [[\c{%\s*\(autoescape\|block\|comment\|csrf_token\|cycle\|debug\|extends\|filter\|firstof\|for\|if\|ifchanged\|include\|load\|lorem\|now\|query_string\|regroup\|resetcycle\|spaceless\|templatetag\|url\|verbatim\|widthratio\|with\)\>\|{#\s\+]]
+ )
+ then
return 'htmldjango'
+ elseif findany(line, { '<extend', '<super>' }) then
+ return 'superhtml'
end
end
return 'html'
@@ -894,6 +974,24 @@ local function m4(contents)
end
end
+--- Check if it is a Microsoft Makefile
+--- @type vim.filetype.mapfn
+function M.make(_, bufnr)
+ vim.b.make_microsoft = nil
+ for _, line in ipairs(getlines(bufnr, 1, 1000)) do
+ if matchregex(line, [[\c^\s*!\s*\(ifn\=\(def\)\=\|include\|message\|error\)\>]]) then
+ vim.b.make_microsoft = 1
+ break
+ elseif
+ matchregex(line, [[^ *ifn\=\(eq\|def\)\>]])
+ or findany(line, { '^ *[-s]?%s', '^ *%w+%s*[!?:+]=' })
+ then
+ break
+ end
+ end
+ return 'make'
+end
+
--- @type vim.filetype.mapfn
function M.markdown(_, _)
return vim.g.filetype_md or 'markdown'
@@ -1038,6 +1136,8 @@ function M.perl(path, bufnr)
end
end
+local prolog_patterns = { '^%s*:%-', '^%s*%%+%s', '^%s*%%+$', '^%s*/%*', '%.%s*$' }
+
--- @type vim.filetype.mapfn
function M.pl(_, bufnr)
if vim.g.filetype_pl then
@@ -1046,11 +1146,7 @@ function M.pl(_, bufnr)
-- Recognize Prolog by specific text in the first non-empty line;
-- require a blank after the '%' because Perl uses "%list" and "%translate"
local line = nextnonblank(bufnr, 1)
- if
- line and line:find(':%-')
- or matchregex(line, [[\c\<prolog\>]])
- or findany(line, { '^%s*%%+%s', '^%s*%%+$', '^%s*/%*' })
- then
+ if line and matchregex(line, [[\c\<prolog\>]]) or findany(line, prolog_patterns) then
return 'prolog'
else
return 'perl'
@@ -1154,11 +1250,7 @@ function M.proto(_, bufnr)
-- Recognize Prolog by specific text in the first non-empty line;
-- require a blank after the '%' because Perl uses "%list" and "%translate"
local line = nextnonblank(bufnr, 1)
- if
- line and line:find(':%-')
- or matchregex(line, [[\c\<prolog\>]])
- or findany(line, { '^%s*%%+%s', '^%s*%%+$', '^%s*/%*' })
- then
+ if line and matchregex(line, [[\c\<prolog\>]]) or findany(line, prolog_patterns) then
return 'prolog'
end
end
@@ -1331,7 +1423,7 @@ end
function M.sgml(_, bufnr)
local lines = table.concat(getlines(bufnr, 1, 5))
if lines:find('linuxdoc') then
- return 'smgllnx'
+ return 'sgmllnx'
elseif lines:find('<!DOCTYPE.*DocBook') then
return 'docbk',
function(b)
@@ -1533,7 +1625,7 @@ function M.tex(path, bufnr)
end
end
--- Determine if a *.tf file is TF mud client or terraform
+-- Determine if a *.tf file is TF (TinyFugue) mud client or terraform
--- @type vim.filetype.mapfn
function M.tf(_, bufnr)
for _, line in ipairs(getlines(bufnr)) do
@@ -1759,6 +1851,7 @@ local patterns_hashbang = {
['^janet\\>'] = { 'janet', { vim_regex = true } },
['^dart\\>'] = { 'dart', { vim_regex = true } },
['^execlineb\\>'] = { 'execline', { vim_regex = true } },
+ ['^vim\\>'] = { 'vim', { vim_regex = true } },
}
---@private
diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua
index b05220ee2c..ccddf826f7 100644
--- a/runtime/lua/vim/fs.lua
+++ b/runtime/lua/vim/fs.lua
@@ -1,6 +1,10 @@
+local uv = vim.uv
+
local M = {}
-local iswin = vim.uv.os_uname().sysname == 'Windows_NT'
+-- Can't use `has('win32')` because the `nvim -ll` test runner doesn't support `vim.fn` yet.
+local sysname = uv.os_uname().sysname:lower()
+local iswin = not not (sysname:find('windows') or sysname:find('mingw'))
local os_sep = iswin and '\\' or '/'
--- Iterate over all the parents of the given path.
@@ -21,6 +25,7 @@ local os_sep = iswin and '\\' or '/'
--- end
--- ```
---
+---@since 10
---@param start (string) Initial path.
---@return fun(_, dir: string): string? # Iterator
---@return nil
@@ -40,6 +45,7 @@ end
--- Return the parent directory of the given path
---
+---@since 10
---@generic T : string|nil
---@param file T Path
---@return T Parent directory of {file}
@@ -69,6 +75,7 @@ end
--- Return the basename of the given path
---
+---@since 10
---@generic T : string|nil
---@param file T Path
---@return T Basename of {file}
@@ -89,6 +96,7 @@ end
--- Concatenate directories and/or file paths into a single path with normalization
--- (e.g., `"foo/"` and `"bar"` get joined to `"foo/bar"`)
---
+---@since 12
---@param ... string
---@return string
function M.joinpath(...)
@@ -99,6 +107,7 @@ end
--- Return an iterator over the items located in {path}
---
+---@since 10
---@param path (string) An absolute or relative path to the directory to iterate
--- over. The path is first normalized |vim.fs.normalize()|.
--- @param opts table|nil Optional keyword arguments:
@@ -122,12 +131,12 @@ function M.dir(path, opts)
path = M.normalize(path)
if not opts.depth or opts.depth == 1 then
- local fs = vim.uv.fs_scandir(path)
+ local fs = uv.fs_scandir(path)
return function()
if not fs then
return
end
- return vim.uv.fs_scandir_next(fs)
+ return uv.fs_scandir_next(fs)
end
end
@@ -138,9 +147,9 @@ function M.dir(path, opts)
--- @type string, integer
local dir0, level = unpack(table.remove(dirs, 1))
local dir = level == 1 and dir0 or M.joinpath(path, dir0)
- local fs = vim.uv.fs_scandir(dir)
+ local fs = uv.fs_scandir(dir)
while fs do
- local name, t = vim.uv.fs_scandir_next(fs)
+ local name, t = uv.fs_scandir_next(fs)
if not name then
break
end
@@ -210,6 +219,7 @@ end
--- end, {limit = math.huge, type = 'file'})
--- ```
---
+---@since 10
---@param names (string|string[]|fun(name: string, path: string): boolean) Names of the items to find.
--- Must be base names, paths and globs are not supported when {names} is a string or a table.
--- If {names} is a function, it is called for each traversed item with args:
@@ -234,7 +244,7 @@ function M.find(names, opts)
names = { names }
end
- local path = opts.path or assert(vim.uv.cwd())
+ local path = opts.path or assert(uv.cwd())
local stop = opts.stop
local limit = opts.limit or 1
@@ -265,7 +275,7 @@ function M.find(names, opts)
local t = {} --- @type string[]
for _, name in ipairs(names) do
local f = M.joinpath(p, name)
- local stat = vim.uv.fs_stat(f)
+ local stat = uv.fs_stat(f)
if stat and (not opts.type or opts.type == stat.type) then
t[#t + 1] = f
end
@@ -349,6 +359,7 @@ end
--- end)
--- ```
---
+--- @since 12
--- @param source integer|string Buffer number (0 for current buffer) or file path (absolute or
--- relative to the |current-directory|) to begin the search from.
--- @param marker (string|string[]|fun(name: string, path: string): boolean) A marker, or list
@@ -365,7 +376,7 @@ function M.root(source, marker)
path = source
elseif type(source) == 'number' then
if vim.bo[source].buftype ~= '' then
- path = assert(vim.uv.cwd())
+ path = assert(uv.cwd())
else
path = vim.api.nvim_buf_get_name(source)
end
@@ -528,6 +539,7 @@ end
--- [[\\?\UNC\server\share\foo\..\..\..\bar]] => "//?/UNC/server/share/bar"
--- ```
---
+---@since 10
---@param path (string) Path to normalize
---@param opts? vim.fs.normalize.Opts
---@return (string) : Normalized path
@@ -552,7 +564,7 @@ function M.normalize(path, opts)
-- Expand ~ to users home directory
if vim.startswith(path, '~') then
- local home = vim.uv.os_homedir() or '~'
+ local home = uv.os_homedir() or '~'
if home:sub(-1) == os_sep_local then
home = home:sub(1, -2)
end
@@ -561,7 +573,7 @@ function M.normalize(path, opts)
-- Expand environment variables if `opts.expand_env` isn't `false`
if opts.expand_env == nil or opts.expand_env then
- path = path:gsub('%$([%w_]+)', vim.uv.os_getenv)
+ path = path:gsub('%$([%w_]+)', uv.os_getenv)
end
if win then
@@ -609,4 +621,56 @@ function M.normalize(path, opts)
return path
end
+--- @param path string Path to remove
+--- @param ty string type of path
+--- @param recursive? boolean
+--- @param force? boolean
+local function rm(path, ty, recursive, force)
+ --- @diagnostic disable-next-line:no-unknown
+ local rm_fn
+
+ if ty == 'directory' then
+ if recursive then
+ for file, fty in vim.fs.dir(path) do
+ rm(M.joinpath(path, file), fty, true, force)
+ end
+ elseif not force then
+ error(string.format('%s is a directory', path))
+ end
+
+ rm_fn = uv.fs_rmdir
+ else
+ rm_fn = uv.fs_unlink
+ end
+
+ local ret, err, errnm = rm_fn(path)
+ if ret == nil and (not force or errnm ~= 'ENOENT') then
+ error(err)
+ end
+end
+
+--- @class vim.fs.rm.Opts
+--- @inlinedoc
+---
+--- Remove directories and their contents recursively
+--- @field recursive? boolean
+---
+--- Ignore nonexistent files and arguments
+--- @field force? boolean
+
+--- Remove files or directories
+--- @since 13
+--- @param path string Path to remove
+--- @param opts? vim.fs.rm.Opts
+function M.rm(path, opts)
+ opts = opts or {}
+
+ local stat, err, errnm = uv.fs_stat(path)
+ if stat then
+ rm(path, stat.type, opts.recursive, opts.force)
+ elseif not opts.force or errnm ~= 'ENOENT' then
+ error(err)
+ end
+end
+
return M
diff --git a/runtime/lua/vim/glob.lua b/runtime/lua/vim/glob.lua
index ad4a915a94..22073b15c8 100644
--- a/runtime/lua/vim/glob.lua
+++ b/runtime/lua/vim/glob.lua
@@ -1,6 +1,6 @@
local lpeg = vim.lpeg
local P, S, V, R, B = lpeg.P, lpeg.S, lpeg.V, lpeg.R, lpeg.B
-local C, Cc, Ct, Cf = lpeg.C, lpeg.Cc, lpeg.Ct, lpeg.Cf
+local C, Cc, Ct, Cf, Cmt = lpeg.C, lpeg.Cc, lpeg.Ct, lpeg.Cf, lpeg.Cmt
local M = {}
@@ -29,8 +29,10 @@ function M.to_lpeg(pattern)
return patt
end
- local function add(acc, a)
- return acc + a
+ local function condlist(conds, after)
+ return vim.iter(conds):fold(P(false), function(acc, cond)
+ return acc + cond * after
+ end)
end
local function mul(acc, m)
@@ -45,13 +47,22 @@ function M.to_lpeg(pattern)
return (-after * P(1)) ^ 0 * after
end
+ -- luacheck: push ignore s
+ local function cut(s, idx, match)
+ return idx, match
+ end
+ -- luacheck: pop
+
local p = P({
'Pattern',
Pattern = V('Elem') ^ -1 * V('End'),
- Elem = Cf(
- (V('DStar') + V('Star') + V('Ques') + V('Class') + V('CondList') + V('Literal'))
- * (V('Elem') + V('End')),
- mul
+ Elem = Cmt(
+ Cf(
+ (V('DStar') + V('Star') + V('Ques') + V('Class') + V('CondList') + V('Literal'))
+ * (V('Elem') + V('End')),
+ mul
+ ),
+ cut
),
DStar = (B(pathsep) + -B(P(1)))
* P('**')
@@ -63,15 +74,14 @@ function M.to_lpeg(pattern)
* C(P('!') ^ -1)
* Ct(Ct(C(P(1)) * P('-') * C(P(1) - P(']'))) ^ 1 * P(']'))
/ class,
- CondList = P('{') * Cf(V('Cond') * (P(',') * V('Cond')) ^ 0, add) * P('}'),
+ CondList = P('{') * Ct(V('Cond') * (P(',') * V('Cond')) ^ 0) * P('}') * V('Pattern') / condlist,
-- TODO: '*' inside a {} condition is interpreted literally but should probably have the same
-- wildcard semantics it usually has.
-- Fixing this is non-trivial because '*' should match non-greedily up to "the rest of the
-- pattern" which in all other cases is the entire succeeding part of the pattern, but at the end of a {}
-- condition means "everything after the {}" where several other options separated by ',' may
-- exist in between that should not be matched by '*'.
- Cond = Cf((V('Ques') + V('Class') + V('CondList') + (V('Literal') - S(',}'))) ^ 1, mul)
- + Cc(P(0)),
+ Cond = Cmt(Cf((V('Ques') + V('Class') + V('Literal') - S(',}')) ^ 1, mul), cut) + Cc(P(0)),
Literal = P(1) / P,
End = P(-1) * Cc(P(-1)),
})
diff --git a/runtime/lua/vim/health.lua b/runtime/lua/vim/health.lua
index f40f04a064..52a7a13966 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
@@ -104,10 +104,10 @@ local function filepath_to_healthcheck(path)
local subpath = path:gsub('.*lua/', '')
if vim.fs.basename(subpath) == 'health.lua' then
-- */health.lua
- name = assert(vim.fs.dirname(subpath))
+ name = vim.fs.dirname(subpath)
else
-- */health/init.lua
- name = assert(vim.fs.dirname(assert(vim.fs.dirname(subpath))))
+ name = vim.fs.dirname(vim.fs.dirname(subpath))
end
name = name:gsub('/', '.')
@@ -275,114 +275,6 @@ function M.error(msg, ...)
collect_output(input)
end
-function M._provider_disabled(provider)
- local loaded_var = 'loaded_' .. provider .. '_provider'
- local v = vim.g[loaded_var]
- if v == 0 then
- M.info('Disabled (' .. loaded_var .. '=' .. v .. ').')
- return true
- end
- return false
-end
-
--- Handler for s:system() function.
-local function system_handler(self, _, data, event)
- if event == 'stderr' then
- if self.add_stderr_to_output then
- self.output = self.output .. table.concat(data, '')
- else
- self.stderr = self.stderr .. table.concat(data, '')
- end
- elseif event == 'stdout' then
- self.output = self.output .. table.concat(data, '')
- end
-end
-
--- Attempts to construct a shell command from an args list.
--- Only for display, to help users debug a failed command.
-local function shellify(cmd)
- if type(cmd) ~= 'table' then
- return cmd
- end
- local escaped = {}
- for i, v in ipairs(cmd) do
- if v:match('[^A-Za-z_/.-]') then
- escaped[i] = vim.fn.shellescape(v)
- else
- escaped[i] = v
- end
- end
- return table.concat(escaped, ' ')
-end
-
-function M._cmd_ok(cmd)
- local out = vim.fn.system(cmd)
- return vim.v.shell_error == 0, out
-end
-
---- Run a system command and timeout after 30 seconds.
----
---- @param cmd table List of command arguments to execute
---- @param args? table Optional arguments:
---- - stdin (string): Data to write to the job's stdin
---- - stderr (boolean): Append stderr to stdout
---- - ignore_error (boolean): If true, ignore error output
---- - timeout (number): Number of seconds to wait before timing out (default 30)
-function M._system(cmd, args)
- args = args or {}
- local stdin = args.stdin or ''
- local stderr = vim.F.if_nil(args.stderr, false)
- local ignore_error = vim.F.if_nil(args.ignore_error, false)
-
- local shell_error_code = 0
- local opts = {
- add_stderr_to_output = stderr,
- output = '',
- stderr = '',
- on_stdout = system_handler,
- on_stderr = system_handler,
- on_exit = function(_, data)
- shell_error_code = data
- end,
- }
- local jobid = vim.fn.jobstart(cmd, opts)
-
- if jobid < 1 then
- local message =
- string.format('Command error (job=%d): %s (in %s)', jobid, shellify(cmd), vim.uv.cwd())
- error(message)
- return opts.output, 1
- end
-
- if stdin:find('^%s$') then
- vim.fn.chansend(jobid, stdin)
- end
-
- local res = vim.fn.jobwait({ jobid }, vim.F.if_nil(args.timeout, 30) * 1000)
- if res[1] == -1 then
- error('Command timed out: ' .. shellify(cmd))
- vim.fn.jobstop(jobid)
- elseif shell_error_code ~= 0 and not ignore_error then
- local emsg = string.format(
- 'Command error (job=%d, exit code %d): %s (in %s)',
- jobid,
- shell_error_code,
- shellify(cmd),
- vim.uv.cwd()
- )
- if opts.output:find('%S') then
- emsg = string.format('%s\noutput: %s', emsg, opts.output)
- end
- if opts.stderr:find('%S') then
- emsg = string.format('%s\nstderr: %s', emsg, opts.stderr)
- end
- error(emsg)
- end
-
- -- return opts.output
- return vim.trim(vim.fn.system(cmd)), shell_error_code
-end
-
local path2name = function(path)
if path:match('%.lua$') then
-- Lua: transform "../lua/vim/lsp/health.lua" into "vim.lsp"
@@ -393,8 +285,8 @@ local path2name = function(path)
-- Remove everything up to the last /lua/ folder
path = path:gsub('^.*/lua/', '')
- -- Remove the filename (health.lua)
- path = vim.fs.dirname(path)
+ -- Remove the filename (health.lua) or (health/init.lua)
+ path = vim.fs.dirname(path:gsub('/init%.lua$', ''))
-- Change slashes to dots
path = path:gsub('/', '.')
@@ -409,11 +301,13 @@ end
local PATTERNS = { '/autoload/health/*.vim', '/lua/**/**/health.lua', '/lua/**/**/health/init.lua' }
--- :checkhealth completion function used by cmdexpand.c get_healthcheck_names()
M._complete = function()
- local unique = vim
+ local unique = vim ---@type table<string,boolean>
+ ---@param pattern string
.iter(vim.tbl_map(function(pattern)
return vim.tbl_map(path2name, vim.api.nvim_get_runtime_file(pattern, true))
end, PATTERNS))
:flatten()
+ ---@param t table<string,boolean>
:fold({}, function(t, name)
t[name] = true -- Remove duplicates
return t
@@ -472,7 +366,7 @@ function M._check(mods, plugin_names)
vim.fn.call(func, {})
else
local f = assert(loadstring(func))
- local ok, output = pcall(f)
+ local ok, output = pcall(f) ---@type boolean, string
if not ok then
M.error(
string.format('Failed to run healthcheck for "%s" plugin. Exception:\n%s\n', name, output)
@@ -485,7 +379,14 @@ function M._check(mods, plugin_names)
s_output = {}
M.error('The healthcheck report for "' .. name .. '" plugin is empty.')
end
- local header = { string.rep('=', 78), name .. ': ' .. func, '' }
+
+ local header = {
+ string.rep('=', 78),
+ -- Example: `foo.health: [ …] require("foo.health").check()`
+ ('%s: %s%s'):format(name, (' '):rep(76 - name:len() - func:len()), func),
+ '',
+ }
+
-- remove empty line after header from report_start
if s_output[1] == '' then
local tmp = {} ---@type string[]
@@ -499,7 +400,7 @@ function M._check(mods, plugin_names)
end
s_output[#s_output + 1] = ''
s_output = vim.list_extend(header, s_output)
- vim.fn.append('$', s_output)
+ vim.fn.append(vim.fn.line('$'), s_output)
vim.cmd.redraw()
end
diff --git a/runtime/lua/vim/health/health.lua b/runtime/lua/vim/health/health.lua
index 5bc03199ee..d226f35f9a 100644
--- a/runtime/lua/vim/health/health.lua
+++ b/runtime/lua/vim/health/health.lua
@@ -11,10 +11,14 @@ local function check_runtime()
health.start('Runtime')
-- Files from an old installation.
local bad_files = {
- ['plugin/health.vim'] = false,
['autoload/health/nvim.vim'] = false,
['autoload/health/provider.vim'] = false,
['autoload/man.vim'] = false,
+ ['lua/provider/node/health.lua'] = false,
+ ['lua/provider/perl/health.lua'] = false,
+ ['lua/provider/python/health.lua'] = false,
+ ['lua/provider/ruby/health.lua'] = false,
+ ['plugin/health.vim'] = false,
['plugin/man.vim'] = false,
['queries/help/highlights.scm'] = false,
['queries/help/injections.scm'] = false,
@@ -39,7 +43,7 @@ local function check_runtime()
'Found old files in $VIMRUNTIME (this can cause weird behavior):\n%s',
bad_files_msg
),
- { 'Delete the $VIMRUNTIME directory (or uninstall Nvim), then reinstall Nvim.' }
+ { 'Delete the $VIMRUNTIME directory, then reinstall Nvim.' }
)
end
end
@@ -50,11 +54,11 @@ local function check_config()
local init_lua = vim.fn.stdpath('config') .. '/init.lua'
local init_vim = vim.fn.stdpath('config') .. '/init.vim'
- local vimrc = vim.env.MYVIMRC and vim.fn.expand(vim.env.MYVIMRC) or init_lua
+ local vimrc = vim.env.MYVIMRC and vim.fs.normalize(vim.env.MYVIMRC) or init_lua
if vim.fn.filereadable(vimrc) == 0 and vim.fn.filereadable(init_vim) == 0 then
ok = false
- local has_vim = vim.fn.filereadable(vim.fn.expand('~/.vimrc')) == 1
+ local has_vim = vim.fn.filereadable(vim.fs.normalize('~/.vimrc')) == 1
health.warn(
('%s user config file: %s'):format(
-1 == vim.fn.getfsize(vimrc) and 'Missing' or 'Unreadable',
@@ -114,7 +118,7 @@ local function check_config()
)
shadafile = (
vim.o.shadafile == ''
- and (shadafile == '' and vim.fn.stdpath('state') .. '/shada/main.shada' or vim.fn.expand(
+ and (shadafile == '' and vim.fn.stdpath('state') .. '/shada/main.shada' or vim.fs.normalize(
shadafile
))
or (vim.o.shadafile == 'NONE' and '' or vim.o.shadafile)
@@ -239,6 +243,7 @@ local function check_tmux()
return
end
+ ---@param option string
local get_tmux_option = function(option)
local cmd = 'tmux show-option -qvg ' .. option -- try global scope
local out = vim.fn.system(vim.fn.split(cmd))
@@ -378,7 +383,7 @@ local function check_terminal()
'SSH_TTY',
}) do
if vim.env[env_var] then
- health.info(vim.fn.printf('$%s="%s"', env_var, vim.env[env_var]))
+ health.info(string.format('$%s="%s"', env_var, vim.env[env_var]))
end
end
end
diff --git a/runtime/lua/vim/highlight.lua b/runtime/lua/vim/highlight.lua
index f278bd357f..a8d88db372 100644
--- a/runtime/lua/vim/highlight.lua
+++ b/runtime/lua/vim/highlight.lua
@@ -20,8 +20,8 @@ M.priorities = {
--- @class vim.highlight.range.Opts
--- @inlinedoc
---
---- Type of range. See [setreg()]
---- (default: `'charwise'`)
+--- Type of range. See [getregtype()]
+--- (default: `'v'` i.e. charwise)
--- @field regtype? string
---
--- Indicates whether the range is end-inclusive
@@ -31,8 +31,6 @@ M.priorities = {
--- Indicates priority of highlight
--- (default: `vim.highlight.priorities.user`)
--- @field priority? integer
----
---- @field package _scoped? boolean
--- Apply highlight group to range of text.
---
@@ -47,25 +45,72 @@ function M.range(bufnr, ns, higroup, start, finish, opts)
local regtype = opts.regtype or 'v'
local inclusive = opts.inclusive or false
local priority = opts.priority or M.priorities.user
- local scoped = opts._scoped or false
-
- -- TODO: in case of 'v', 'V' (not block), this should calculate equivalent
- -- bounds (row, col, end_row, end_col) as multiline regions are natively
- -- supported now
- local region = vim.region(bufnr, start, finish, regtype, inclusive)
- for linenr, cols in pairs(region) do
- local end_row
- if cols[2] == -1 then
- end_row = linenr + 1
- cols[2] = 0
+
+ local v_maxcol = vim.v.maxcol
+
+ local pos1 = type(start) == 'string' and vim.fn.getpos(start)
+ or {
+ bufnr,
+ start[1] + 1,
+ start[2] ~= -1 and start[2] ~= v_maxcol and start[2] + 1 or v_maxcol,
+ 0,
+ }
+ local pos2 = type(finish) == 'string' and vim.fn.getpos(finish)
+ or {
+ bufnr,
+ finish[1] + 1,
+ finish[2] ~= -1 and start[2] ~= v_maxcol and finish[2] + 1 or v_maxcol,
+ 0,
+ }
+
+ local buf_line_count = vim.api.nvim_buf_line_count(bufnr)
+ pos1[2] = math.min(pos1[2], buf_line_count)
+ pos2[2] = math.min(pos2[2], buf_line_count)
+
+ if pos1[2] <= 0 or pos1[3] <= 0 or pos2[2] <= 0 or pos2[3] <= 0 then
+ return
+ end
+
+ vim._with({ buf = bufnr }, function()
+ if pos1[3] ~= v_maxcol then
+ local max_col1 = vim.fn.col({ pos1[2], '$' })
+ pos1[3] = math.min(pos1[3], max_col1)
+ end
+ if pos2[3] ~= v_maxcol then
+ local max_col2 = vim.fn.col({ pos2[2], '$' })
+ pos2[3] = math.min(pos2[3], max_col2)
end
- api.nvim_buf_set_extmark(bufnr, ns, linenr, cols[1], {
+ end)
+
+ local region = vim.fn.getregionpos(pos1, pos2, {
+ type = regtype,
+ exclusive = not inclusive,
+ eol = true,
+ })
+ -- For non-blockwise selection, use a single extmark.
+ if regtype == 'v' or regtype == 'V' then
+ region = { { region[1][1], region[#region][2] } }
+ if
+ regtype == 'V'
+ or region[1][2][2] == pos1[2] and pos1[3] == v_maxcol
+ or region[1][2][2] == pos2[2] and pos2[3] == v_maxcol
+ then
+ region[1][2][2] = region[1][2][2] + 1
+ region[1][2][3] = 0
+ end
+ end
+
+ for _, res in ipairs(region) do
+ local start_row = res[1][2] - 1
+ local start_col = res[1][3] - 1
+ local end_row = res[2][2] - 1
+ local end_col = res[2][3]
+ api.nvim_buf_set_extmark(bufnr, ns, start_row, start_col, {
hl_group = higroup,
end_row = end_row,
- end_col = cols[2],
+ end_col = end_col,
priority = priority,
strict = false,
- scoped = scoped,
})
end
end
@@ -129,19 +174,18 @@ function M.on_yank(opts)
yank_cancel()
end
- vim.api.nvim__win_add_ns(winid, yank_ns)
+ vim.api.nvim__ns_set(yank_ns, { wins = { winid } })
M.range(bufnr, yank_ns, higroup, "'[", "']", {
regtype = event.regtype,
inclusive = event.inclusive,
priority = opts.priority or M.priorities.user,
- _scoped = true,
})
yank_cancel = function()
yank_timer = nil
yank_cancel = nil
pcall(vim.api.nvim_buf_clear_namespace, bufnr, yank_ns, 0, -1)
- pcall(vim.api.nvim__win_del_ns, winid, yank_ns)
+ pcall(vim.api.nvim__ns_set, { wins = {} })
end
yank_timer = vim.defer_fn(yank_cancel, timeout)
diff --git a/runtime/lua/vim/iter.lua b/runtime/lua/vim/iter.lua
index 1093759efe..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.
@@ -276,7 +278,7 @@ end
--- -- { 6, 12 }
--- ```
---
----@param f fun(...):any Mapping function. Takes all values returned from
+---@param f fun(...):...:any Mapping function. Takes all values returned from
--- the previous stage in the pipeline as arguments
--- and returns one or more new values, which are used
--- in the next pipeline stage. Nil return values
@@ -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/keymap.lua b/runtime/lua/vim/keymap.lua
index ec00c56c7a..50ca0d2d0e 100644
--- a/runtime/lua/vim/keymap.lua
+++ b/runtime/lua/vim/keymap.lua
@@ -15,30 +15,28 @@ local keymap = {}
--- (Default: `false`)
--- @field remap? boolean
---- Adds a new |mapping|.
+--- Defines a |mapping| of |keycodes| to a function or keycodes.
+---
--- Examples:
---
--- ```lua
---- -- Map to a Lua function:
---- vim.keymap.set('n', 'lhs', function() print("real lua function") end)
---- -- Map to multiple modes:
---- vim.keymap.set({'n', 'v'}, '<leader>lr', vim.lsp.buf.references, { buffer = true })
---- -- Buffer-local mapping:
---- vim.keymap.set('n', '<leader>w', "<cmd>w<cr>", { silent = true, buffer = 5 })
---- -- Expr mapping:
+--- -- Map "x" to a Lua function:
+--- vim.keymap.set('n', 'x', function() print("real lua function") end)
+--- -- Map "<leader>x" to multiple modes for the current buffer:
+--- vim.keymap.set({'n', 'v'}, '<leader>x', vim.lsp.buf.references, { buffer = true })
+--- -- Map <Tab> to an expression (|:map-<expr>|):
--- vim.keymap.set('i', '<Tab>', function()
--- return vim.fn.pumvisible() == 1 and "<C-n>" or "<Tab>"
--- end, { expr = true })
---- -- <Plug> mapping:
+--- -- Map "[%%" to a <Plug> mapping:
--- vim.keymap.set('n', '[%%', '<Plug>(MatchitNormalMultiBackward)')
--- ```
---
----@param mode string|string[] Mode short-name, see |nvim_set_keymap()|.
---- Can also be list of modes to create mapping on multiple modes.
+---@param mode string|string[] Mode "short-name" (see |nvim_set_keymap()|), or a list thereof.
---@param lhs string Left-hand side |{lhs}| of the mapping.
---@param rhs string|function Right-hand side |{rhs}| of the mapping, can be a Lua function.
----
---@param opts? vim.keymap.set.Opts
+---
---@see |nvim_set_keymap()|
---@see |maparg()|
---@see |mapcheck()|
diff --git a/runtime/lua/vim/loader.lua b/runtime/lua/vim/loader.lua
index ea77a22416..e86d33bf53 100644
--- a/runtime/lua/vim/loader.lua
+++ b/runtime/lua/vim/loader.lua
@@ -200,7 +200,7 @@ function Loader.loader(modname)
return chunk or error(err)
end
Loader._hashes = nil
- return '\ncache_loader: module ' .. modname .. ' not found'
+ return ("\n\tcache_loader: module '%s' not found"):format(modname)
end
--- The `package.loaders` loader for libs
@@ -208,8 +208,7 @@ end
---@return string|function
---@private
function Loader.loader_lib(modname)
- local sysname = uv.os_uname().sysname:lower() or ''
- local is_win = sysname:find('win', 1, true) and not sysname:find('darwin', 1, true)
+ local is_win = vim.fn.has('win32') == 1
local ret = M.find(modname, { patterns = is_win and { '.dll' } or { '.so' } })[1]
if ret then
-- Making function name in Lua 5.1 (see src/loadlib.c:mkfuncname) is
@@ -222,7 +221,7 @@ function Loader.loader_lib(modname)
local chunk, err = package.loadlib(ret.modpath, 'luaopen_' .. funcname:gsub('%.', '_'))
return chunk or error(err)
end
- return '\ncache_loader_lib: module ' .. modname .. ' not found'
+ return ("\n\tcache_loader_lib: module '%s' not found"):format(modname)
end
--- `loadfile` using the cache
@@ -290,6 +289,9 @@ function Loader.load(modpath, opts)
end
--- Finds Lua modules for the given module name.
+---
+--- @since 0
+---
---@param modname string Module name, or `"*"` to find the top-level modules instead
---@param opts? vim.loader.find.Opts Options for finding a module:
---@return vim.loader.ModuleInfo[]
@@ -378,8 +380,10 @@ function M.find(modname, opts)
return results
end
---- Resets the cache for the path, or all the paths
---- if path is nil.
+--- Resets the cache for the path, or all the paths if path is nil.
+---
+--- @since 0
+---
---@param path string? path to reset
function M.reset(path)
if path then
@@ -399,6 +403,8 @@ end
--- * adds the Lua loader using the byte-compilation cache
--- * adds the libs loader
--- * removes the default Nvim loader
+---
+--- @since 0
function M.enable()
if M.enabled then
return
@@ -422,6 +428,8 @@ end
--- Disables the experimental Lua module loader:
--- * removes the loaders
--- * adds the default Nvim loader
+---
+--- @since 0
function M.disable()
if not M.enabled then
return
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 1592fd3151..60677554ce 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -3,7 +3,6 @@ local validate = vim.validate
local lsp = vim._defer_require('vim.lsp', {
_changetracking = ..., --- @module 'vim.lsp._changetracking'
- _completion = ..., --- @module 'vim.lsp._completion'
_dynamic = ..., --- @module 'vim.lsp._dynamic'
_snippet_grammar = ..., --- @module 'vim.lsp._snippet_grammar'
_tagfunc = ..., --- @module 'vim.lsp._tagfunc'
@@ -11,6 +10,7 @@ local lsp = vim._defer_require('vim.lsp', {
buf = ..., --- @module 'vim.lsp.buf'
client = ..., --- @module 'vim.lsp.client'
codelens = ..., --- @module 'vim.lsp.codelens'
+ completion = ..., --- @module 'vim.lsp.completion'
diagnostic = ..., --- @module 'vim.lsp.diagnostic'
handlers = ..., --- @module 'vim.lsp.handlers'
inlay_hint = ..., --- @module 'vim.lsp.inlay_hint'
@@ -33,43 +33,50 @@ lsp.rpc_response_error = lsp.rpc.rpc_response_error
-- maps request name to the required server_capability in the client.
lsp._request_name_to_capability = {
- [ms.textDocument_hover] = { 'hoverProvider' },
- [ms.textDocument_signatureHelp] = { 'signatureHelpProvider' },
- [ms.textDocument_definition] = { 'definitionProvider' },
- [ms.textDocument_implementation] = { 'implementationProvider' },
- [ms.textDocument_declaration] = { 'declarationProvider' },
- [ms.textDocument_typeDefinition] = { 'typeDefinitionProvider' },
- [ms.textDocument_documentSymbol] = { 'documentSymbolProvider' },
- [ms.textDocument_prepareCallHierarchy] = { 'callHierarchyProvider' },
[ms.callHierarchy_incomingCalls] = { 'callHierarchyProvider' },
[ms.callHierarchy_outgoingCalls] = { 'callHierarchyProvider' },
- [ms.textDocument_prepareTypeHierarchy] = { 'typeHierarchyProvider' },
- [ms.typeHierarchy_subtypes] = { 'typeHierarchyProvider' },
- [ms.typeHierarchy_supertypes] = { 'typeHierarchyProvider' },
- [ms.textDocument_rename] = { 'renameProvider' },
- [ms.textDocument_prepareRename] = { 'renameProvider', 'prepareProvider' },
+ [ms.codeAction_resolve] = { 'codeActionProvider', 'resolveProvider' },
+ [ms.codeLens_resolve] = { 'codeLensProvider', 'resolveProvider' },
+ [ms.documentLink_resolve] = { 'documentLinkProvider', 'resolveProvider' },
+ [ms.inlayHint_resolve] = { 'inlayHintProvider', 'resolveProvider' },
[ms.textDocument_codeAction] = { 'codeActionProvider' },
[ms.textDocument_codeLens] = { 'codeLensProvider' },
- [ms.codeLens_resolve] = { 'codeLensProvider', 'resolveProvider' },
- [ms.codeAction_resolve] = { 'codeActionProvider', 'resolveProvider' },
- [ms.workspace_executeCommand] = { 'executeCommandProvider' },
- [ms.workspace_symbol] = { 'workspaceSymbolProvider' },
- [ms.textDocument_references] = { 'referencesProvider' },
- [ms.textDocument_rangeFormatting] = { 'documentRangeFormattingProvider' },
- [ms.textDocument_formatting] = { 'documentFormattingProvider' },
[ms.textDocument_completion] = { 'completionProvider' },
- [ms.textDocument_documentHighlight] = { 'documentHighlightProvider' },
- [ms.textDocument_semanticTokens_full] = { 'semanticTokensProvider' },
- [ms.textDocument_semanticTokens_full_delta] = { 'semanticTokensProvider' },
- [ms.textDocument_inlayHint] = { 'inlayHintProvider' },
+ [ms.textDocument_declaration] = { 'declarationProvider' },
+ [ms.textDocument_definition] = { 'definitionProvider' },
[ms.textDocument_diagnostic] = { 'diagnosticProvider' },
- [ms.inlayHint_resolve] = { 'inlayHintProvider', 'resolveProvider' },
- [ms.textDocument_documentLink] = { 'documentLinkProvider' },
- [ms.documentLink_resolve] = { 'documentLinkProvider', 'resolveProvider' },
[ms.textDocument_didClose] = { 'textDocumentSync', 'openClose' },
[ms.textDocument_didOpen] = { 'textDocumentSync', 'openClose' },
- [ms.textDocument_willSave] = { 'textDocumentSync', 'willSave' },
+ [ms.textDocument_documentColor] = { 'colorProvider' },
+ [ms.textDocument_documentHighlight] = { 'documentHighlightProvider' },
+ [ms.textDocument_documentLink] = { 'documentLinkProvider' },
+ [ms.textDocument_documentSymbol] = { 'documentSymbolProvider' },
+ [ms.textDocument_formatting] = { 'documentFormattingProvider' },
+ [ms.textDocument_hover] = { 'hoverProvider' },
+ [ms.textDocument_implementation] = { 'implementationProvider' },
+ [ms.textDocument_inlayHint] = { 'inlayHintProvider' },
+ [ms.textDocument_inlineValue] = { 'inlineValueProvider' },
+ [ms.textDocument_linkedEditingRange] = { 'linkedEditingRangeProvider' },
+ [ms.textDocument_moniker] = { 'monikerProvider' },
+ [ms.textDocument_onTypeFormatting] = { 'documentOnTypeFormattingProvider' },
+ [ms.textDocument_prepareCallHierarchy] = { 'callHierarchyProvider' },
+ [ms.textDocument_prepareRename] = { 'renameProvider', 'prepareProvider' },
+ [ms.textDocument_prepareTypeHierarchy] = { 'typeHierarchyProvider' },
+ [ms.textDocument_rangeFormatting] = { 'documentRangeFormattingProvider' },
+ [ms.textDocument_rangesFormatting] = { 'documentRangeFormattingProvider', 'rangesSupport' },
+ [ms.textDocument_references] = { 'referencesProvider' },
+ [ms.textDocument_rename] = { 'renameProvider' },
+ [ms.textDocument_selectionRange] = { 'selectionRangeProvider' },
+ [ms.textDocument_semanticTokens_full] = { 'semanticTokensProvider' },
+ [ms.textDocument_semanticTokens_full_delta] = { 'semanticTokensProvider' },
+ [ms.textDocument_signatureHelp] = { 'signatureHelpProvider' },
+ [ms.textDocument_typeDefinition] = { 'typeDefinitionProvider' },
[ms.textDocument_willSaveWaitUntil] = { 'textDocumentSync', 'willSaveWaitUntil' },
+ [ms.textDocument_willSave] = { 'textDocumentSync', 'willSave' },
+ [ms.typeHierarchy_subtypes] = { 'typeHierarchyProvider' },
+ [ms.typeHierarchy_supertypes] = { 'typeHierarchyProvider' },
+ [ms.workspace_executeCommand] = { 'executeCommandProvider' },
+ [ms.workspace_symbol] = { 'workspaceSymbolProvider' },
}
-- TODO improve handling of scratch buffers with LSP attached.
@@ -201,10 +208,10 @@ end
--- 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
+--- @field bufnr? integer
---
--- Suppress error reporting if the LSP server fails to start (default false).
--- @field silent? boolean
@@ -351,7 +358,7 @@ function lsp._set_defaults(client, bufnr)
then
vim.bo[bufnr].formatexpr = 'v:lua.vim.lsp.formatexpr()'
end
- api.nvim_buf_call(bufnr, function()
+ vim._with({ buf = bufnr }, function()
if
client.supports_method(ms.textDocument_hover)
and is_empty_or_default(bufnr, 'keywordprg')
@@ -377,9 +384,9 @@ local function reset_defaults(bufnr)
if vim.bo[bufnr].formatexpr == 'v:lua.vim.lsp.formatexpr()' then
vim.bo[bufnr].formatexpr = nil
end
- api.nvim_buf_call(bufnr, function()
+ vim._with({ buf = bufnr }, function()
local keymap = vim.fn.maparg('K', 'n', false, true)
- if keymap and keymap.callback == vim.lsp.buf.hover then
+ if keymap and keymap.callback == vim.lsp.buf.hover and keymap.buffer == 1 then
vim.keymap.del('n', 'K', { buffer = bufnr })
end
end)
@@ -391,9 +398,9 @@ end
local function on_client_exit(code, signal, client_id)
local client = all_clients[client_id]
- for bufnr in pairs(client.attached_buffers) do
- vim.schedule(function()
- if client and client.attached_buffers[bufnr] then
+ 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,
@@ -401,15 +408,16 @@ local function on_client_exit(code, signal, client_id)
})
end
- local namespace = vim.lsp.diagnostic.get_namespace(client_id)
- vim.diagnostic.reset(namespace, bufnr)
client.attached_buffers[bufnr] = nil
if #lsp.get_clients({ bufnr = bufnr, _uninitialized = true }) == 0 then
reset_defaults(bufnr)
end
- end)
- end
+ end
+
+ local namespace = vim.lsp.diagnostic.get_namespace(client_id)
+ vim.diagnostic.reset(namespace)
+ end)
local name = client.name or 'unknown'
@@ -519,7 +527,6 @@ local function buf_detach_client(bufnr, client)
end
client.attached_buffers[bufnr] = nil
- util.buf_versions[bufnr] = nil
local namespace = lsp.diagnostic.get_namespace(client.id)
vim.diagnostic.reset(namespace, bufnr)
@@ -577,7 +584,8 @@ local function buf_attach(bufnr)
api.nvim_buf_attach(bufnr, false, {
on_lines = function(_, _, changedtick, firstline, lastline, new_lastline)
if #lsp.get_clients({ bufnr = bufnr }) == 0 then
- return true -- detach
+ -- detach if there are no clients
+ return #lsp.get_clients({ bufnr = bufnr, _uninitialized = true }) == 0
end
util.buf_versions[bufnr] = changedtick
changetracking.send_changes(bufnr, firstline, lastline, new_lastline)
@@ -603,6 +611,7 @@ local function buf_attach(bufnr)
buf_detach_client(bufnr, client)
end
attached_buffers[bufnr] = nil
+ util.buf_versions[bufnr] = nil
end,
-- TODO if we know all of the potential clients ahead of time, then we
@@ -852,17 +861,20 @@ api.nvim_create_autocmd('VimLeavePre', {
---@param params table|nil Parameters to send to the server
---@param handler? lsp.Handler See |lsp-handler|
--- If nil, follows resolution strategy defined in |lsp-handler-configuration|
----
+---@param on_unsupported? fun()
+--- The function to call when the buffer has no clients that support the given method.
+--- Defaults to an `ERROR` level notification.
---@return table<integer, integer> client_request_ids Map of client-id:request-id pairs
---for all successful requests.
---@return function _cancel_all_requests Function which can be used to
---cancel all the requests. You could instead
---iterate all clients and call their `cancel_request()` methods.
-function lsp.buf_request(bufnr, method, params, handler)
+function lsp.buf_request(bufnr, method, params, handler, on_unsupported)
validate({
bufnr = { bufnr, 'n', true },
method = { method, 's' },
handler = { handler, 'f', true },
+ on_unsupported = { on_unsupported, 'f', true },
})
bufnr = resolve_bufnr(bufnr)
@@ -884,7 +896,11 @@ function lsp.buf_request(bufnr, method, params, handler)
-- if has client but no clients support the given method, notify the user
if next(clients) and not method_supported then
- vim.notify(lsp._unsupported_method(method), vim.log.levels.ERROR)
+ if on_unsupported == nil then
+ vim.notify(lsp._unsupported_method(method), vim.log.levels.ERROR)
+ else
+ on_unsupported()
+ end
vim.cmd.redraw()
return {}, function() end
end
@@ -1002,8 +1018,7 @@ end
--- - findstart=0: column where the completion starts, or -2 or -3
--- - findstart=1: list of matches (actually just calls |complete()|)
function lsp.omnifunc(findstart, base)
- log.debug('omnifunc.findstart', { findstart = findstart, base = base })
- return vim.lsp._completion.omnifunc(findstart, base)
+ return vim.lsp.completion._omnifunc(findstart, base)
end
--- @class vim.lsp.formatexpr.Opts
@@ -1016,7 +1031,7 @@ end
--- Provides an interface between the built-in client and a `formatexpr` function.
---
--- Currently only supports a single client. This can be set via
---- `setlocal formatexpr=v:lua.vim.lsp.formatexpr()` but will typically or in `on_attach`
+--- `setlocal formatexpr=v:lua.vim.lsp.formatexpr()` or (more typically) in `on_attach`
--- via `vim.bo[bufnr].formatexpr = 'v:lua.vim.lsp.formatexpr(#{timeout_ms:250})'`.
---
---@param opts? vim.lsp.formatexpr.Opts
diff --git a/runtime/lua/vim/lsp/_completion.lua b/runtime/lua/vim/lsp/_completion.lua
deleted file mode 100644
index a169f96565..0000000000
--- a/runtime/lua/vim/lsp/_completion.lua
+++ /dev/null
@@ -1,276 +0,0 @@
-local M = {}
-local api = vim.api
-local lsp = vim.lsp
-local protocol = lsp.protocol
-local ms = protocol.Methods
-
---- @alias vim.lsp.CompletionResult lsp.CompletionList | lsp.CompletionItem[]
-
--- TODO(mariasolos): Remove this declaration once we figure out a better way to handle
--- literal/anonymous types (see https://github.com/neovim/neovim/pull/27542/files#r1495259331).
---- @class lsp.ItemDefaults
---- @field editRange lsp.Range | { insert: lsp.Range, replace: lsp.Range } | nil
---- @field insertTextFormat lsp.InsertTextFormat?
---- @field insertTextMode lsp.InsertTextMode?
---- @field data any
-
----@param input string unparsed snippet
----@return string parsed snippet
-local function parse_snippet(input)
- local ok, parsed = pcall(function()
- return vim.lsp._snippet_grammar.parse(input)
- end)
- return ok and tostring(parsed) or input
-end
-
---- Returns text that should be inserted when selecting completion item. The
---- precedence is as follows: textEdit.newText > insertText > label
----
---- See https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion
----
----@param item lsp.CompletionItem
----@return string
-local function get_completion_word(item)
- if item.textEdit ~= nil and item.textEdit.newText ~= nil and item.textEdit.newText ~= '' then
- if item.insertTextFormat == protocol.InsertTextFormat.PlainText then
- return item.textEdit.newText
- else
- return parse_snippet(item.textEdit.newText)
- end
- elseif item.insertText ~= nil and item.insertText ~= '' then
- if item.insertTextFormat == protocol.InsertTextFormat.PlainText then
- return item.insertText
- else
- return parse_snippet(item.insertText)
- end
- end
- return item.label
-end
-
---- Applies the given defaults to the completion item, modifying it in place.
----
---- @param item lsp.CompletionItem
---- @param defaults lsp.ItemDefaults?
-local function apply_defaults(item, defaults)
- if not defaults then
- return
- end
-
- item.insertTextFormat = item.insertTextFormat or defaults.insertTextFormat
- item.insertTextMode = item.insertTextMode or defaults.insertTextMode
- item.data = item.data or defaults.data
- if defaults.editRange then
- local textEdit = item.textEdit or {}
- item.textEdit = textEdit
- textEdit.newText = textEdit.newText or item.textEditText or item.insertText
- if defaults.editRange.start then
- textEdit.range = textEdit.range or defaults.editRange
- elseif defaults.editRange.insert then
- textEdit.insert = defaults.editRange.insert
- textEdit.replace = defaults.editRange.replace
- end
- end
-end
-
----@param result vim.lsp.CompletionResult
----@return lsp.CompletionItem[]
-local function get_items(result)
- if result.items then
- for _, item in ipairs(result.items) do
- ---@diagnostic disable-next-line: param-type-mismatch
- apply_defaults(item, result.itemDefaults)
- end
- return result.items
- else
- return result
- end
-end
-
---- Turns the result of a `textDocument/completion` request into vim-compatible
---- |complete-items|.
----
----@param result vim.lsp.CompletionResult Result of `textDocument/completion`
----@param prefix string prefix to filter the completion items
----@return table[]
----@see complete-items
-function M._lsp_to_complete_items(result, prefix)
- local items = get_items(result)
- if vim.tbl_isempty(items) then
- return {}
- end
-
- local function matches_prefix(item)
- return vim.startswith(get_completion_word(item), prefix)
- end
-
- items = vim.tbl_filter(matches_prefix, items) --[[@as lsp.CompletionItem[]|]]
- table.sort(items, function(a, b)
- return (a.sortText or a.label) < (b.sortText or b.label)
- end)
-
- local matches = {}
- for _, item in ipairs(items) do
- local info = ''
- local documentation = item.documentation
- if documentation then
- if type(documentation) == 'string' and documentation ~= '' then
- info = documentation
- elseif type(documentation) == 'table' and type(documentation.value) == 'string' then
- info = documentation.value
- else
- vim.notify(
- ('invalid documentation value %s'):format(vim.inspect(documentation)),
- vim.log.levels.WARN
- )
- end
- end
- local word = get_completion_word(item)
- table.insert(matches, {
- word = word,
- abbr = item.label,
- kind = protocol.CompletionItemKind[item.kind] or 'Unknown',
- menu = item.detail or '',
- info = #info > 0 and info or nil,
- icase = 1,
- dup = 1,
- empty = 1,
- user_data = {
- nvim = {
- lsp = {
- completion_item = item,
- },
- },
- },
- })
- end
- return matches
-end
-
----@param lnum integer 0-indexed
----@param items lsp.CompletionItem[]
-local function adjust_start_col(lnum, line, items, encoding)
- local min_start_char = nil
- for _, item in pairs(items) do
- if item.textEdit and item.textEdit.range.start.line == lnum then
- if min_start_char and min_start_char ~= item.textEdit.range.start.character then
- return nil
- end
- min_start_char = item.textEdit.range.start.character
- end
- end
- if min_start_char then
- return vim.lsp.util._str_byteindex_enc(line, min_start_char, encoding)
- else
- return nil
- end
-end
-
----@private
----@param line string line content
----@param lnum integer 0-indexed line number
----@param client_start_boundary integer 0-indexed word boundary
----@param server_start_boundary? integer 0-indexed word boundary, based on textEdit.range.start.character
----@param result vim.lsp.CompletionResult
----@param encoding string
----@return table[] matches
----@return integer? server_start_boundary
-function M._convert_results(
- line,
- lnum,
- cursor_col,
- client_start_boundary,
- server_start_boundary,
- result,
- encoding
-)
- -- Completion response items may be relative to a position different than `client_start_boundary`.
- -- Concrete example, with lua-language-server:
- --
- -- require('plenary.asy|
- -- ▲ ▲ ▲
- -- │ │ └── cursor_pos: 20
- -- │ └────── client_start_boundary: 17
- -- └────────────── textEdit.range.start.character: 9
- -- .newText = 'plenary.async'
- -- ^^^
- -- prefix (We'd remove everything not starting with `asy`,
- -- so we'd eliminate the `plenary.async` result
- --
- -- `adjust_start_col` is used to prefer the language server boundary.
- --
- local candidates = get_items(result)
- local curstartbyte = adjust_start_col(lnum, line, candidates, encoding)
- if server_start_boundary == nil then
- server_start_boundary = curstartbyte
- elseif curstartbyte ~= nil and curstartbyte ~= server_start_boundary then
- server_start_boundary = client_start_boundary
- end
- local prefix = line:sub((server_start_boundary or client_start_boundary) + 1, cursor_col)
- local matches = M._lsp_to_complete_items(result, prefix)
- return matches, server_start_boundary
-end
-
----@param findstart integer 0 or 1, decides behavior
----@param base integer findstart=0, text to match against
----@return integer|table Decided by {findstart}:
---- - findstart=0: column where the completion starts, or -2 or -3
---- - findstart=1: list of matches (actually just calls |complete()|)
-function M.omnifunc(findstart, base)
- assert(base) -- silence luals
- local bufnr = api.nvim_get_current_buf()
- local clients = lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_completion })
- local remaining = #clients
- if remaining == 0 then
- return findstart == 1 and -1 or {}
- end
-
- local win = api.nvim_get_current_win()
- local cursor = api.nvim_win_get_cursor(win)
- local lnum = cursor[1] - 1
- local cursor_col = cursor[2]
- local line = api.nvim_get_current_line()
- local line_to_cursor = line:sub(1, cursor_col)
- local client_start_boundary = vim.fn.match(line_to_cursor, '\\k*$') --[[@as integer]]
- local server_start_boundary = nil
- local items = {}
-
- local function on_done()
- local mode = api.nvim_get_mode()['mode']
- if mode == 'i' or mode == 'ic' then
- vim.fn.complete((server_start_boundary or client_start_boundary) + 1, items)
- end
- end
-
- local util = vim.lsp.util
- for _, client in ipairs(clients) do
- local params = util.make_position_params(win, client.offset_encoding)
- client.request(ms.textDocument_completion, params, function(err, result)
- if err then
- vim.lsp.log.warn(err.message)
- end
- if result and vim.fn.mode() == 'i' then
- local matches
- matches, server_start_boundary = M._convert_results(
- line,
- lnum,
- cursor_col,
- client_start_boundary,
- server_start_boundary,
- result,
- client.offset_encoding
- )
- vim.list_extend(items, matches)
- end
- remaining = remaining - 1
- if remaining == 0 then
- vim.schedule(on_done)
- end
- end, bufnr)
- end
-
- -- Return -2 to signal that we should continue completion so that we can
- -- async complete.
- return -2
-end
-
-return M
diff --git a/runtime/lua/vim/lsp/_dynamic.lua b/runtime/lua/vim/lsp/_dynamic.lua
index 819b03a63a..27113c0e74 100644
--- a/runtime/lua/vim/lsp/_dynamic.lua
+++ b/runtime/lua/vim/lsp/_dynamic.lua
@@ -24,7 +24,6 @@ function M:supports_registration(method)
end
--- @param registrations lsp.Registration[]
---- @package
function M:register(registrations)
-- remove duplicates
self:unregister(registrations)
@@ -38,7 +37,6 @@ function M:register(registrations)
end
--- @param unregisterations lsp.Unregistration[]
---- @package
function M:unregister(unregisterations)
for _, unreg in ipairs(unregisterations) do
local method = unreg.method
@@ -58,7 +56,6 @@ end
--- @param method string
--- @param opts? {bufnr: integer?}
--- @return lsp.Registration? (table|nil) the registration if found
---- @package
function M:get(method, opts)
opts = opts or {}
opts.bufnr = opts.bufnr or vim.api.nvim_get_current_buf()
@@ -78,7 +75,6 @@ end
--- @param method string
--- @param opts? {bufnr: integer?}
---- @package
function M:supports(method, opts)
return self:get(method, opts) ~= nil
end
diff --git a/runtime/lua/vim/lsp/_meta/protocol.lua b/runtime/lua/vim/lsp/_meta/protocol.lua
index 9a11972007..d83c40a09f 100644
--- a/runtime/lua/vim/lsp/_meta/protocol.lua
+++ b/runtime/lua/vim/lsp/_meta/protocol.lua
@@ -134,7 +134,7 @@ error('Cannot require a meta file')
---The zero-based character offset before the folded range ends. If not defined, defaults to the length of the end line.
---@field endCharacter? uinteger
---
----Describes the kind of the folding range such as `comment' or 'region'. The kind
+---Describes the kind of the folding range such as 'comment' or 'region'. The kind
---is used to categorize folding ranges and used by commands like 'Fold all comments'.
---See {@link FoldingRangeKind} for an enumeration of standardized kinds.
---@field kind? lsp.FoldingRangeKind
@@ -681,6 +681,11 @@ error('Cannot require a meta file')
---of a notebook cell.
---@field cellTextDocuments lsp.TextDocumentItem[]
+---Registration options specific to a notebook.
+---
+---@since 3.17.0
+---@class lsp.NotebookDocumentSyncRegistrationOptions: lsp.NotebookDocumentSyncOptions, lsp.StaticRegistrationOptions
+
---The params sent in a change notebook document notification.
---
---@since 3.17.0
@@ -789,7 +794,7 @@ error('Cannot require a meta file')
---Information about the server.
---
---@since 3.15.0
----@field serverInfo? lsp._anonym1.serverInfo
+---@field serverInfo? lsp.ServerInfo
---The data type of the ResponseError if the
---initialize request fails.
@@ -1115,7 +1120,7 @@ error('Cannot require a meta file')
---capability.
---
---@since 3.17.0
----@field itemDefaults? lsp._anonym2.itemDefaults
+---@field itemDefaults? lsp.CompletionItemDefaults
---
---The completion items.
---@field items lsp.CompletionItem[]
@@ -1171,7 +1176,7 @@ error('Cannot require a meta file')
---
---If `null`, no parameter of the signature is active (for example a named
---argument that does not match any declared parameters). This is only valid
----since 3.18.0 and if the client specifies the client capability
+---if the client specifies the client capability
---`textDocument.signatureHelp.noActiveParameterSupport === true`
---
---If omitted or the value lies outside the range of
@@ -1307,6 +1312,12 @@ error('Cannot require a meta file')
---Title of the command, like `save`.
---@field title string
---
+---An optional tooltip.
+---
+---@since 3.18.0
+---@proposed
+---@field tooltip? string
+---
---The identifier of the actual command handler.
---@field command string
---
@@ -1355,7 +1366,7 @@ error('Cannot require a meta file')
--- error message with `reason` in the editor.
---
---@since 3.16.0
----@field disabled? lsp._anonym4.disabled
+---@field disabled? lsp.CodeActionDisabled
---
---The workspace edit this code action performs.
---@field edit? lsp.WorkspaceEdit
@@ -1379,6 +1390,12 @@ error('Cannot require a meta file')
---
---A query string to filter symbols by. Clients may send an empty
---string here to request all symbols.
+---
+---The `query`-parameter should be interpreted in a *relaxed way* as editors
+---will apply their own highlighting and scoring on the results. A good rule
+---of thumb is to match case-insensitive and to simply check that the
+---characters of *query* appear in their order in a candidate symbol.
+---Servers shouldn't use prefix, substring, or similar strict matching.
---@field query string
---A special workspace symbol that supports locations without a range.
@@ -1393,7 +1410,7 @@ error('Cannot require a meta file')
---capability `workspace.symbol.resolveSupport`.
---
---See SymbolInformation#location for more details.
----@field location lsp.Location|lsp._anonym5.location
+---@field location lsp.Location|lsp.LocationUriOnly
---
---A data entry field that is preserved on a workspace symbol between a
---workspace symbol request and a workspace symbol resolve request.
@@ -1566,6 +1583,12 @@ error('Cannot require a meta file')
---
---The edits to apply.
---@field edit lsp.WorkspaceEdit
+---
+---Additional data about the edit.
+---
+---@since 3.18.0
+---@proposed
+---@field metadata? lsp.WorkspaceEditMetadata
---The result returned from the apply workspace edit request.
---
@@ -1650,7 +1673,7 @@ error('Cannot require a meta file')
---@class lsp.SetTraceParams
---
----@field value lsp.TraceValues
+---@field value lsp.TraceValue
---@class lsp.LogTraceParams
---
@@ -1848,10 +1871,10 @@ error('Cannot require a meta file')
---
---Server supports providing semantic tokens for a specific range
---of a document.
----@field range? boolean|lsp._anonym6.range
+---@field range? boolean|lsp._anonym1.range
---
---Server supports providing semantic tokens for a full document.
----@field full? boolean|lsp._anonym7.full
+---@field full? boolean|lsp.SemanticTokensFullDelta
---@since 3.16.0
---@class lsp.SemanticTokensEdit
@@ -1888,7 +1911,10 @@ error('Cannot require a meta file')
---
---@since 3.16.0 - support for AnnotatedTextEdit. This is guarded using a
---client capability.
----@field edits (lsp.TextEdit|lsp.AnnotatedTextEdit)[]
+---
+---@since 3.18.0 - support for SnippetTextEdit. This is guarded using a
+---client capability.
+---@field edits (lsp.TextEdit|lsp.AnnotatedTextEdit|lsp.SnippetTextEdit)[]
---Create file operation.
---@class lsp.CreateFile: lsp.ResourceOperation
@@ -2235,7 +2261,7 @@ error('Cannot require a meta file')
---@field uri lsp.DocumentUri
---
---The text document's language identifier.
----@field languageId string
+---@field languageId lsp.LanguageKind
---
---The version number of this document (it will increase after each
---change, including undo/redo).
@@ -2244,6 +2270,28 @@ error('Cannot require a meta file')
---The content of the opened text document.
---@field text string
+---Options specific to a notebook plus its cells
+---to be synced to the server.
+---
+---If a selector provides a notebook document
+---filter but no cell selector all cells of a
+---matching notebook document will be synced.
+---
+---If a selector provides no notebook document
+---filter but only a cell selector all notebook
+---document that contain at least one matching
+---cell will be synced.
+---
+---@since 3.17.0
+---@class lsp.NotebookDocumentSyncOptions
+---
+---The notebooks to be synced
+---@field notebookSelector (lsp.NotebookDocumentFilterWithNotebook|lsp.NotebookDocumentFilterWithCells)[]
+---
+---Whether save notification should be forwarded to
+---the server. Will only be honored if mode === `notebook`.
+---@field save? boolean
+
---A versioned notebook document identifier.
---
---@since 3.17.0
@@ -2266,7 +2314,7 @@ error('Cannot require a meta file')
---@field metadata? lsp.LSPObject
---
---Changes to cells
----@field cells? lsp._anonym8.cells
+---@field cells? lsp.NotebookDocumentCellChanges
---A literal to identify a notebook document in the client.
---
@@ -2348,7 +2396,7 @@ error('Cannot require a meta file')
---Information about the client
---
---@since 3.15.0
----@field clientInfo? lsp._anonym11.clientInfo
+---@field clientInfo? lsp.ClientInfo
---
---The locale the client is currently showing the user interface
---in. This must not necessarily be the locale of the operating
@@ -2380,7 +2428,7 @@ error('Cannot require a meta file')
---@field initializationOptions? lsp.LSPAny
---
---The initial trace setting. If omitted trace is disabled ('off').
----@field trace? lsp.TraceValues
+---@field trace? lsp.TraceValue
---@class lsp.WorkspaceFoldersInitializeParams
---
@@ -2534,18 +2582,24 @@ error('Cannot require a meta file')
---@proposed
---@field inlineCompletionProvider? boolean|lsp.InlineCompletionOptions
---
----Text document specific server capabilities.
----
----@since 3.18.0
----@proposed
----@field textDocument? lsp._anonym12.textDocument
----
---Workspace specific server capabilities.
----@field workspace? lsp._anonym14.workspace
+---@field workspace? lsp.WorkspaceOptions
---
---Experimental server capabilities.
---@field experimental? lsp.LSPAny
+---Information about the server
+---
+---@since 3.15.0
+---@since 3.18.0 ServerInfo type name added.
+---@class lsp.ServerInfo
+---
+---The name of the server as defined by the server.
+---@field name string
+---
+---The server's version as defined by the server.
+---@field version? string
+
---A text document identifier to denote a specific version of a text document.
---@class lsp.VersionedTextDocumentIdentifier: lsp.TextDocumentIdentifier
---
@@ -2586,8 +2640,9 @@ error('Cannot require a meta file')
---The range at which the message applies
---@field range lsp.Range
---
----The diagnostic's severity. Can be omitted. If omitted it is up to the
----client to interpret diagnostics as error, warning, info or hint.
+---The diagnostic's severity. To avoid interpretation mismatches when a
+---server is used with different clients it is highly recommended that servers
+---always provide a severity value.
---@field severity? lsp.DiagnosticSeverity
---
---The diagnostic's code, which usually appear in the user interface.
@@ -2604,10 +2659,8 @@ error('Cannot require a meta file')
---appears in the user interface.
---@field source? string
---
----The diagnostic's message. It usually appears in the user interface.
----
----@since 3.18.0 - support for `MarkupContent`. This is guarded by the client capability `textDocument.diagnostic.markupMessageSupport`.
----@field message string|lsp.MarkupContent
+---The diagnostic's message. It usually appears in the user interface
+---@field message string
---
---Additional metadata about the diagnostic.
---
@@ -2661,6 +2714,46 @@ error('Cannot require a meta file')
---The range if the replace is requested.
---@field replace lsp.Range
+---In many cases the items of an actual completion result share the same
+---value for properties like `commitCharacters` or the range of a text
+---edit. A completion list can therefore define item defaults which will
+---be used if a completion item itself doesn't specify the value.
+---
+---If a completion list specifies a default value and a completion item
+---also specifies a corresponding value the one from the item is used.
+---
+---Servers are only allowed to return default values if the client
+---signals support for this via the `completionList.itemDefaults`
+---capability.
+---
+---@since 3.17.0
+---@class lsp.CompletionItemDefaults
+---
+---A default commit character set.
+---
+---@since 3.17.0
+---@field commitCharacters? string[]
+---
+---A default edit range.
+---
+---@since 3.17.0
+---@field editRange? lsp.Range|lsp.EditRangeWithInsertReplace
+---
+---A default insert text format.
+---
+---@since 3.17.0
+---@field insertTextFormat? lsp.InsertTextFormat
+---
+---A default insert text mode.
+---
+---@since 3.17.0
+---@field insertTextMode? lsp.InsertTextMode
+---
+---A default data value.
+---
+---@since 3.17.0
+---@field data? lsp.LSPAny
+
---Completion options.
---@class lsp.CompletionOptions: lsp.WorkDoneProgressOptions
---
@@ -2692,7 +2785,7 @@ error('Cannot require a meta file')
---capabilities.
---
---@since 3.17.0
----@field completionItem? lsp._anonym15.completionItem
+---@field completionItem? lsp.ServerCompletionItemOptions
---Hover options.
---@class lsp.HoverOptions: lsp.WorkDoneProgressOptions
@@ -2742,7 +2835,7 @@ error('Cannot require a meta file')
---
---If `null`, no parameter of the signature is active (for example a named
---argument that does not match any declared parameters). This is only valid
----since 3.18.0 and if the client specifies the client capability
+---if the client specifies the client capability
---`textDocument.signatureHelp.noActiveParameterSupport === true`
---
---If provided (or `null`), this is used in place of
@@ -2819,8 +2912,6 @@ error('Cannot require a meta file')
---errors are currently presented to the user for the given range. There is no guarantee
---that these accurately reflect the error state of the resource. The primary parameter
---to compute code actions is the provided range.
----
----Note that the client should check the `textDocument.diagnostic.markupMessageSupport` server capability before sending diagnostics with markup messages to a server.
---@field diagnostics lsp.Diagnostic[]
---
---Requested kind of actions to return.
@@ -2834,6 +2925,16 @@ error('Cannot require a meta file')
---@since 3.17.0
---@field triggerKind? lsp.CodeActionTriggerKind
+---Captures why the code action is currently disabled.
+---
+---@since 3.18.0
+---@class lsp.CodeActionDisabled
+---
+---Human readable description of why the code action is currently disabled.
+---
+---This is displayed in the code actions UI.
+---@field reason string
+
---Provider options for a {@link CodeActionRequest}.
---@class lsp.CodeActionOptions: lsp.WorkDoneProgressOptions
---
@@ -2843,12 +2944,36 @@ error('Cannot require a meta file')
---may list out every specific kind they provide.
---@field codeActionKinds? lsp.CodeActionKind[]
---
+---Static documentation for a class of code actions.
+---
+---Documentation from the provider should be shown in the code actions menu if either:
+---
+---- Code actions of `kind` are requested by the editor. In this case, the editor will show the documentation that
+--- most closely matches the requested code action kind. For example, if a provider has documentation for
+--- both `Refactor` and `RefactorExtract`, when the user requests code actions for `RefactorExtract`,
+--- the editor will use the documentation for `RefactorExtract` instead of the documentation for `Refactor`.
+---
+---- Any code actions of `kind` are returned by the provider.
+---
+---At most one documentation entry should be shown per provider.
+---
+---@since 3.18.0
+---@proposed
+---@field documentation? lsp.CodeActionKindDocumentation[]
+---
---The server provides support to resolve additional
---information for a code action.
---
---@since 3.16.0
---@field resolveProvider? boolean
+---Location with only uri and does not include range.
+---
+---@since 3.18.0
+---@class lsp.LocationUriOnly
+---
+---@field uri lsp.DocumentUri
+
---Server capabilities for a {@link WorkspaceSymbolRequest}.
---@class lsp.WorkspaceSymbolOptions: lsp.WorkDoneProgressOptions
---
@@ -2923,12 +3048,33 @@ error('Cannot require a meta file')
---@since version 3.12.0
---@field prepareProvider? boolean
+---@since 3.18.0
+---@class lsp.PrepareRenamePlaceholder
+---
+---@field range lsp.Range
+---
+---@field placeholder string
+
+---@since 3.18.0
+---@class lsp.PrepareRenameDefaultBehavior
+---
+---@field defaultBehavior boolean
+
---The server capabilities of a {@link ExecuteCommandRequest}.
---@class lsp.ExecuteCommandOptions: lsp.WorkDoneProgressOptions
---
---The commands to be executed on the server
---@field commands string[]
+---Additional data about a workspace edit.
+---
+---@since 3.18.0
+---@proposed
+---@class lsp.WorkspaceEditMetadata
+---
+---Signal to the editor that this edit is a refactoring.
+---@field isRefactoring? boolean
+
---@since 3.16.0
---@class lsp.SemanticTokensLegend
---
@@ -2938,6 +3084,14 @@ error('Cannot require a meta file')
---The token modifiers a server uses.
---@field tokenModifiers string[]
+---Semantic tokens options to support deltas for full documents
+---
+---@since 3.18.0
+---@class lsp.SemanticTokensFullDelta
+---
+---The server supports deltas for full documents.
+---@field delta? boolean
+
---A text document identifier to optionally denote a specific version of a text document.
---@class lsp.OptionalVersionedTextDocumentIdentifier: lsp.TextDocumentIdentifier
---
@@ -2956,6 +3110,21 @@ error('Cannot require a meta file')
---The actual identifier of the change annotation
---@field annotationId lsp.ChangeAnnotationIdentifier
+---An interactive text edit.
+---
+---@since 3.18.0
+---@proposed
+---@class lsp.SnippetTextEdit
+---
+---The range of the text document to be manipulated.
+---@field range lsp.Range
+---
+---The snippet to be inserted.
+---@field snippet lsp.StringValue
+---
+---The actual identifier of the snippet edit.
+---@field annotationId? lsp.ChangeAnnotationIdentifier
+
---A generic resource operation.
---@class lsp.ResourceOperation
---
@@ -3066,20 +3235,43 @@ error('Cannot require a meta file')
---if supported by the client.
---@field executionSummary? lsp.ExecutionSummary
----A change describing how to move a `NotebookCell`
----array from state S to S'.
+---@since 3.18.0
+---@class lsp.NotebookDocumentFilterWithNotebook
---
----@since 3.17.0
----@class lsp.NotebookCellArrayChange
+---The notebook to be synced If a string
+---value is provided it matches against the
+---notebook type. '*' matches every notebook.
+---@field notebook string|lsp.NotebookDocumentFilter
---
----The start oftest of the cell that changed.
----@field start uinteger
+---The cells of the matching notebook to be synced.
+---@field cells? lsp.NotebookCellLanguage[]
+
+---@since 3.18.0
+---@class lsp.NotebookDocumentFilterWithCells
---
----The deleted cells
----@field deleteCount uinteger
+---The notebook to be synced If a string
+---value is provided it matches against the
+---notebook type. '*' matches every notebook.
+---@field notebook? string|lsp.NotebookDocumentFilter
---
----The new cells, if any
----@field cells? lsp.NotebookCell[]
+---The cells of the matching notebook to be synced.
+---@field cells lsp.NotebookCellLanguage[]
+
+---Cell changes to a notebook document.
+---
+---@since 3.18.0
+---@class lsp.NotebookDocumentCellChanges
+---
+---Changes to the cell structure to add or
+---remove cells.
+---@field structure? lsp.NotebookDocumentCellChangeStructure
+---
+---Changes to notebook cells properties like its
+---kind, execution summary or metadata.
+---@field data? lsp.NotebookCell[]
+---
+---Changes to the text content of notebook cells.
+---@field textContent? lsp.NotebookDocumentCellContentChanges[]
---Describes the currently selected completion item.
---
@@ -3093,6 +3285,18 @@ error('Cannot require a meta file')
---The text the range will be replaced with if this completion is accepted.
---@field text string
+---Information about the client
+---
+---@since 3.15.0
+---@since 3.18.0 ClientInfo type name added.
+---@class lsp.ClientInfo
+---
+---The name of the client as defined by the client.
+---@field name string
+---
+---The client's version as defined by the client.
+---@field version? string
+
---Defines the capabilities provided by the client.
---@class lsp.ClientCapabilities
---
@@ -3140,69 +3344,40 @@ error('Cannot require a meta file')
---sent.
---@field save? boolean|lsp.SaveOptions
----Options specific to a notebook plus its cells
----to be synced to the server.
+---Defines workspace specific capabilities of the server.
---
----If a selector provides a notebook document
----filter but no cell selector all cells of a
----matching notebook document will be synced.
+---@since 3.18.0
+---@class lsp.WorkspaceOptions
---
----If a selector provides no notebook document
----filter but only a cell selector all notebook
----document that contain at least one matching
----cell will be synced.
+---The server supports workspace folder.
---
----@since 3.17.0
----@class lsp.NotebookDocumentSyncOptions
+---@since 3.6.0
+---@field workspaceFolders? lsp.WorkspaceFoldersServerCapabilities
---
----The notebooks to be synced
----@field notebookSelector (lsp._anonym16.notebookSelector|lsp._anonym18.notebookSelector)[]
+---The server is interested in notifications/requests for operations on files.
---
----Whether save notification should be forwarded to
----the server. Will only be honored if mode === `notebook`.
----@field save? boolean
+---@since 3.16.0
+---@field fileOperations? lsp.FileOperationOptions
----Registration options specific to a notebook.
+---@since 3.18.0
+---@class lsp.TextDocumentContentChangePartial
---
----@since 3.17.0
----@class lsp.NotebookDocumentSyncRegistrationOptions: lsp.NotebookDocumentSyncOptions, lsp.StaticRegistrationOptions
-
----@class lsp.WorkspaceFoldersServerCapabilities
+---The range of the document that changed.
+---@field range lsp.Range
---
----The server has support for workspace folders
----@field supported? boolean
+---The optional length of the range that got replaced.
---
----Whether the server wants to receive workspace folder
----change notifications.
+---@deprecated use range instead.
+---@field rangeLength? uinteger
---
----If a string is provided the string is treated as an ID
----under which the notification is registered on the client
----side. The ID can be used to unregister for these events
----using the `client/unregisterCapability` request.
----@field changeNotifications? string|boolean
+---The new text for the provided range.
+---@field text string
----Options for notifications/requests for user operations on files.
----
----@since 3.16.0
----@class lsp.FileOperationOptions
----
----The server is interested in receiving didCreateFiles notifications.
----@field didCreate? lsp.FileOperationRegistrationOptions
----
----The server is interested in receiving willCreateFiles requests.
----@field willCreate? lsp.FileOperationRegistrationOptions
----
----The server is interested in receiving didRenameFiles notifications.
----@field didRename? lsp.FileOperationRegistrationOptions
----
----The server is interested in receiving willRenameFiles requests.
----@field willRename? lsp.FileOperationRegistrationOptions
----
----The server is interested in receiving didDeleteFiles file notifications.
----@field didDelete? lsp.FileOperationRegistrationOptions
+---@since 3.18.0
+---@class lsp.TextDocumentContentChangeWholeDocument
---
----The server is interested in receiving willDeleteFiles file requests.
----@field willDelete? lsp.FileOperationRegistrationOptions
+---The new text of the whole document.
+---@field text string
---Structure to capture a description for an error code.
---
@@ -3223,6 +3398,33 @@ error('Cannot require a meta file')
---The message of this related diagnostic information.
---@field message string
+---Edit range variant that includes ranges for insert and replace operations.
+---
+---@since 3.18.0
+---@class lsp.EditRangeWithInsertReplace
+---
+---@field insert lsp.Range
+---
+---@field replace lsp.Range
+
+---@since 3.18.0
+---@class lsp.ServerCompletionItemOptions
+---
+---The server has support for completion item label
+---details (see also `CompletionItemLabelDetails`) when
+---receiving a completion item in a resolve call.
+---
+---@since 3.17.0
+---@field labelDetailsSupport? boolean
+
+---@since 3.18.0
+---@deprecated use MarkupContent instead.
+---@class lsp.MarkedStringWithLanguage
+---
+---@field language string
+---
+---@field value string
+
---Represents a parameter of a callable-signature. A parameter can
---have a label and a doc-comment.
---@class lsp.ParameterInformation
@@ -3233,14 +3435,36 @@ error('Cannot require a meta file')
---signature label. (see SignatureInformation.label). The offsets are based on a UTF-16
---string representation as `Position` and `Range` does.
---
+---To avoid ambiguities a server should use the [start, end] offset value instead of using
+---a substring. Whether a client support this is controlled via `labelOffsetSupport` client
+---capability.
+---
---*Note*: a label of type string should be a substring of its containing signature label.
---Its intended use case is to highlight the parameter label part in the `SignatureInformation.label`.
----@field label string|{ [1]: uinteger, [2]: uinteger }
+---@field label string|[uinteger, uinteger]
---
---The human-readable doc-comment of this parameter. Will be shown
---in the UI but can be omitted.
---@field documentation? string|lsp.MarkupContent
+---Documentation for a class of code actions.
+---
+---@since 3.18.0
+---@proposed
+---@class lsp.CodeActionKindDocumentation
+---
+---The kind of the code action being documented.
+---
+---If the kind is generic, such as `CodeActionKind.Refactor`, the documentation will be shown whenever any
+---refactorings are returned. If the kind if more specific, such as `CodeActionKind.RefactorExtract`, the
+---documentation will only be shown when extract refactoring code actions are returned.
+---@field kind lsp.CodeActionKind
+---
+---Command that is ued to display the documentation to the user.
+---
+---The title of this documentation code action is taken from {@linkcode Command.title}
+---@field command lsp.Command
+
---A notebook cell text document filter denotes a cell text
---document by different properties.
---
@@ -3278,6 +3502,34 @@ error('Cannot require a meta file')
---not if known by the client.
---@field success? boolean
+---@since 3.18.0
+---@class lsp.NotebookCellLanguage
+---
+---@field language string
+
+---Structural changes to cells in a notebook document.
+---
+---@since 3.18.0
+---@class lsp.NotebookDocumentCellChangeStructure
+---
+---The change to the cell array.
+---@field array lsp.NotebookCellArrayChange
+---
+---Additional opened cell text documents.
+---@field didOpen? lsp.TextDocumentItem[]
+---
+---Additional closed cell text documents.
+---@field didClose? lsp.TextDocumentIdentifier[]
+
+---Content changes to a cell in a notebook document.
+---
+---@since 3.18.0
+---@class lsp.NotebookDocumentCellContentChanges
+---
+---@field document lsp.VersionedTextDocumentIdentifier
+---
+---@field changes lsp.TextDocumentContentChangeEvent[]
+
---Workspace specific client capabilities.
---@class lsp.WorkspaceClientCapabilities
---
@@ -3524,7 +3776,7 @@ error('Cannot require a meta file')
---anymore since the information is outdated).
---
---@since 3.17.0
----@field staleRequestSupport? lsp._anonym20.staleRequestSupport
+---@field staleRequestSupport? lsp.StaleRequestSupportOptions
---
---Client capabilities specific to regular expressions.
---
@@ -3556,6 +3808,43 @@ error('Cannot require a meta file')
---@since 3.17.0
---@field positionEncodings? lsp.PositionEncodingKind[]
+---@class lsp.WorkspaceFoldersServerCapabilities
+---
+---The server has support for workspace folders
+---@field supported? boolean
+---
+---Whether the server wants to receive workspace folder
+---change notifications.
+---
+---If a string is provided the string is treated as an ID
+---under which the notification is registered on the client
+---side. The ID can be used to unregister for these events
+---using the `client/unregisterCapability` request.
+---@field changeNotifications? string|boolean
+
+---Options for notifications/requests for user operations on files.
+---
+---@since 3.16.0
+---@class lsp.FileOperationOptions
+---
+---The server is interested in receiving didCreateFiles notifications.
+---@field didCreate? lsp.FileOperationRegistrationOptions
+---
+---The server is interested in receiving willCreateFiles requests.
+---@field willCreate? lsp.FileOperationRegistrationOptions
+---
+---The server is interested in receiving didRenameFiles notifications.
+---@field didRename? lsp.FileOperationRegistrationOptions
+---
+---The server is interested in receiving willRenameFiles requests.
+---@field willRename? lsp.FileOperationRegistrationOptions
+---
+---The server is interested in receiving didDeleteFiles file notifications.
+---@field didDelete? lsp.FileOperationRegistrationOptions
+---
+---The server is interested in receiving willDeleteFiles file requests.
+---@field willDelete? lsp.FileOperationRegistrationOptions
+
---A relative pattern is a helper to construct glob patterns that are matched
---relatively to a base URI. The common value for a `baseUri` is a workspace
---folder root, but it can be another absolute URI as well.
@@ -3570,6 +3859,111 @@ error('Cannot require a meta file')
---The actual glob pattern;
---@field pattern lsp.Pattern
+---A document filter where `language` is required field.
+---
+---@since 3.18.0
+---@class lsp.TextDocumentFilterLanguage
+---
+---A language id, like `typescript`.
+---@field language string
+---
+---A Uri {@link Uri.scheme scheme}, like `file` or `untitled`.
+---@field scheme? string
+---
+---A glob pattern, like **/*.{ts,js}. See TextDocumentFilter for examples.
+---
+---@since 3.18.0 - support for relative patterns.
+---@field pattern? lsp.GlobPattern
+
+---A document filter where `scheme` is required field.
+---
+---@since 3.18.0
+---@class lsp.TextDocumentFilterScheme
+---
+---A language id, like `typescript`.
+---@field language? string
+---
+---A Uri {@link Uri.scheme scheme}, like `file` or `untitled`.
+---@field scheme string
+---
+---A glob pattern, like **/*.{ts,js}. See TextDocumentFilter for examples.
+---
+---@since 3.18.0 - support for relative patterns.
+---@field pattern? lsp.GlobPattern
+
+---A document filter where `pattern` is required field.
+---
+---@since 3.18.0
+---@class lsp.TextDocumentFilterPattern
+---
+---A language id, like `typescript`.
+---@field language? string
+---
+---A Uri {@link Uri.scheme scheme}, like `file` or `untitled`.
+---@field scheme? string
+---
+---A glob pattern, like **/*.{ts,js}. See TextDocumentFilter for examples.
+---
+---@since 3.18.0 - support for relative patterns.
+---@field pattern lsp.GlobPattern
+
+---A notebook document filter where `notebookType` is required field.
+---
+---@since 3.18.0
+---@class lsp.NotebookDocumentFilterNotebookType
+---
+---The type of the enclosing notebook.
+---@field notebookType string
+---
+---A Uri {@link Uri.scheme scheme}, like `file` or `untitled`.
+---@field scheme? string
+---
+---A glob pattern.
+---@field pattern? lsp.GlobPattern
+
+---A notebook document filter where `scheme` is required field.
+---
+---@since 3.18.0
+---@class lsp.NotebookDocumentFilterScheme
+---
+---The type of the enclosing notebook.
+---@field notebookType? string
+---
+---A Uri {@link Uri.scheme scheme}, like `file` or `untitled`.
+---@field scheme string
+---
+---A glob pattern.
+---@field pattern? lsp.GlobPattern
+
+---A notebook document filter where `pattern` is required field.
+---
+---@since 3.18.0
+---@class lsp.NotebookDocumentFilterPattern
+---
+---The type of the enclosing notebook.
+---@field notebookType? string
+---
+---A Uri {@link Uri.scheme scheme}, like `file` or `untitled`.
+---@field scheme? string
+---
+---A glob pattern.
+---@field pattern lsp.GlobPattern
+
+---A change describing how to move a `NotebookCell`
+---array from state S to S'.
+---
+---@since 3.17.0
+---@class lsp.NotebookCellArrayChange
+---
+---The start oftest of the cell that changed.
+---@field start uinteger
+---
+---The deleted cells
+---@field deleteCount uinteger
+---
+---The new cells, if any
+---@field cells? lsp.NotebookCell[]
+
---@class lsp.WorkspaceEditClientCapabilities
---
---The client supports versioned document changes in `WorkspaceEdit`s
@@ -3600,7 +3994,19 @@ error('Cannot require a meta file')
---create file, rename file and delete file changes.
---
---@since 3.16.0
----@field changeAnnotationSupport? lsp._anonym21.changeAnnotationSupport
+---@field changeAnnotationSupport? lsp.ChangeAnnotationsSupportOptions
+---
+---Whether the client supports `WorkspaceEditMetadata` in `WorkspaceEdit`s.
+---
+---@since 3.18.0
+---@proposed
+---@field metadataSupport? boolean
+---
+---Whether the client supports snippets as text edits.
+---
+---@since 3.18.0
+---@proposed
+---@field snippetEditSupport? boolean
---@class lsp.DidChangeConfigurationClientCapabilities
---
@@ -3627,20 +4033,20 @@ error('Cannot require a meta file')
---@field dynamicRegistration? boolean
---
---Specific capabilities for the `SymbolKind` in the `workspace/symbol` request.
----@field symbolKind? lsp._anonym22.symbolKind
+---@field symbolKind? lsp.ClientSymbolKindOptions
---
---The client supports tags on `SymbolInformation`.
---Clients supporting tags have to handle unknown tags gracefully.
---
---@since 3.16.0
----@field tagSupport? lsp._anonym23.tagSupport
+---@field tagSupport? lsp.ClientSymbolTagOptions
---
---The client support partial workspace symbols. The client will send the
---request `workspaceSymbol/resolve` to the server to resolve additional
---properties.
---
---@since 3.17.0
----@field resolveSupport? lsp._anonym24.resolveSupport
+---@field resolveSupport? lsp.ClientSymbolResolveOptions
---The client capabilities of a {@link ExecuteCommandRequest}.
---@class lsp.ExecuteCommandClientCapabilities
@@ -3785,9 +4191,9 @@ error('Cannot require a meta file')
---
---The client supports the following `CompletionItem` specific
---capabilities.
----@field completionItem? lsp._anonym25.completionItem
+---@field completionItem? lsp.ClientCompletionItemOptions
---
----@field completionItemKind? lsp._anonym29.completionItemKind
+---@field completionItemKind? lsp.ClientCompletionItemOptionsKind
---
---Defines how the client handles whitespace and indentation
---when accepting a completion item that uses multi line
@@ -3804,7 +4210,7 @@ error('Cannot require a meta file')
---capabilities.
---
---@since 3.17.0
----@field completionList? lsp._anonym30.completionList
+---@field completionList? lsp.CompletionListCapabilities
---@class lsp.HoverClientCapabilities
---
@@ -3823,7 +4229,7 @@ error('Cannot require a meta file')
---
---The client supports the following `SignatureInformation`
---specific properties.
----@field signatureInformation? lsp._anonym31.signatureInformation
+---@field signatureInformation? lsp.ClientSignatureInformationOptions
---
---The client supports to send additional context information for a
---`textDocument/signatureHelp` request. A client that opts into
@@ -3901,7 +4307,7 @@ error('Cannot require a meta file')
---
---Specific capabilities for the `SymbolKind` in the
---`textDocument/documentSymbol` request.
----@field symbolKind? lsp._anonym33.symbolKind
+---@field symbolKind? lsp.ClientSymbolKindOptions
---
---The client supports hierarchical document symbols.
---@field hierarchicalDocumentSymbolSupport? boolean
@@ -3911,7 +4317,7 @@ error('Cannot require a meta file')
---Clients supporting tags have to handle unknown tags gracefully.
---
---@since 3.16.0
----@field tagSupport? lsp._anonym34.tagSupport
+---@field tagSupport? lsp.ClientSymbolTagOptions
---
---The client supports an additional label presented in the UI when
---registering a document symbol provider.
@@ -3930,7 +4336,7 @@ error('Cannot require a meta file')
---set the request can only return `Command` literals.
---
---@since 3.8.0
----@field codeActionLiteralSupport? lsp._anonym35.codeActionLiteralSupport
+---@field codeActionLiteralSupport? lsp.ClientCodeActionLiteralOptions
---
---Whether code action supports the `isPreferred` property.
---
@@ -3953,7 +4359,7 @@ error('Cannot require a meta file')
---properties via a separate `codeAction/resolve` request.
---
---@since 3.16.0
----@field resolveSupport? lsp._anonym37.resolveSupport
+---@field resolveSupport? lsp.ClientCodeActionResolveOptions
---
---Whether the client honors the change annotations in
---text edits and resource operations returned via the
@@ -3963,12 +4369,25 @@ error('Cannot require a meta file')
---
---@since 3.16.0
---@field honorsChangeAnnotations? boolean
+---
+---Whether the client supports documentation for a class of
+---code actions.
+---
+---@since 3.18.0
+---@proposed
+---@field documentationSupport? boolean
---The client capabilities of a {@link CodeLensRequest}.
---@class lsp.CodeLensClientCapabilities
---
---Whether code lens supports dynamic registration.
---@field dynamicRegistration? boolean
+---
+---Whether the client supports resolving additional code lens
+---properties via a separate `codeLens/resolve` request.
+---
+---@since 3.18.0
+---@field resolveSupport? lsp.ClientCodeLensResolveOptions
---The client capabilities of a {@link DocumentLinkRequest}.
---@class lsp.DocumentLinkClientCapabilities
@@ -4061,12 +4480,12 @@ error('Cannot require a meta file')
---Specific options for the folding range kind.
---
---@since 3.17.0
----@field foldingRangeKind? lsp._anonym38.foldingRangeKind
+---@field foldingRangeKind? lsp.ClientFoldingRangeKindOptions
---
---Specific options for the folding range.
---
---@since 3.17.0
----@field foldingRange? lsp._anonym39.foldingRange
+---@field foldingRange? lsp.ClientFoldingRangeOptions
---@class lsp.SelectionRangeClientCapabilities
---
@@ -4076,34 +4495,13 @@ error('Cannot require a meta file')
---@field dynamicRegistration? boolean
---The publish diagnostic client capabilities.
----@class lsp.PublishDiagnosticsClientCapabilities
----
----Whether the clients accepts diagnostics with related information.
----@field relatedInformation? boolean
----
----Client supports the tag property to provide meta data about a diagnostic.
----Clients supporting tags have to handle unknown tags gracefully.
----
----@since 3.15.0
----@field tagSupport? lsp._anonym40.tagSupport
+---@class lsp.PublishDiagnosticsClientCapabilities: lsp.DiagnosticsCapabilities
---
---Whether the client interprets the version property of the
---`textDocument/publishDiagnostics` notification's parameter.
---
---@since 3.15.0
---@field versionSupport? boolean
----
----Client supports a codeDescription property
----
----@since 3.16.0
----@field codeDescriptionSupport? boolean
----
----Whether code action supports the `data` property which is
----preserved between a `textDocument/publishDiagnostics` and
----`textDocument/codeAction` request.
----
----@since 3.16.0
----@field dataSupport? boolean
---@since 3.16.0
---@class lsp.CallHierarchyClientCapabilities
@@ -4129,7 +4527,7 @@ error('Cannot require a meta file')
---`request.range` are both set to true but the server only provides a
---range provider the client might not render a minimap correctly or might
---even decide to not show any semantic tokens at all.
----@field requests lsp._anonym41.requests
+---@field requests lsp.ClientSemanticTokensRequestOptions
---
---The token types that the client supports.
---@field tokenTypes string[]
@@ -4212,12 +4610,12 @@ error('Cannot require a meta file')
---
---Indicates which properties a client can resolve lazily on an inlay
---hint.
----@field resolveSupport? lsp._anonym44.resolveSupport
+---@field resolveSupport? lsp.ClientInlayHintResolveOptions
---Client capabilities specific to diagnostic pull requests.
---
---@since 3.17.0
----@class lsp.DiagnosticClientCapabilities
+---@class lsp.DiagnosticClientCapabilities: lsp.DiagnosticsCapabilities
---
---Whether implementation supports dynamic registration. If this is set to `true`
---the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)`
@@ -4226,9 +4624,6 @@ error('Cannot require a meta file')
---
---Whether the clients supports related documents for document diagnostic pulls.
---@field relatedDocumentSupport? boolean
----
----Whether the client supports `MarkupContent` in diagnostic messages.
----@field markupMessageSupport? boolean
---Client capabilities specific to inline completions.
---
@@ -4257,7 +4652,7 @@ error('Cannot require a meta file')
---@class lsp.ShowMessageRequestClientCapabilities
---
---Capabilities specific to the `MessageActionItem` type.
----@field messageActionItem? lsp._anonym45.messageActionItem
+---@field messageActionItem? lsp.ClientShowMessageActionItemOptions
---Client capabilities for the showDocument request.
---
@@ -4268,13 +4663,24 @@ error('Cannot require a meta file')
---request.
---@field support boolean
+---@since 3.18.0
+---@class lsp.StaleRequestSupportOptions
+---
+---The client will actively cancel the request.
+---@field cancel boolean
+---
+---The list of requests for which the client
+---will retry the request if it receives a
+---response with error code `ContentModified`
+---@field retryOnContentModified string[]
+
---Client capabilities specific to regular expressions.
---
---@since 3.16.0
---@class lsp.RegularExpressionsClientCapabilities
---
---The engine's name.
----@field engine string
+---@field engine lsp.RegularExpressionEngineKind
---
---The engine's version.
---@field version? string
@@ -4296,6 +4702,285 @@ error('Cannot require a meta file')
---@since 3.17.0
---@field allowedTags? string[]
+---@since 3.18.0
+---@class lsp.ChangeAnnotationsSupportOptions
+---
+---Whether the client groups edits with equal labels into tree nodes,
+---for instance all edits labelled with "Changes in Strings" would
+---be a tree node.
+---@field groupsOnLabel? boolean
+
+---@since 3.18.0
+---@class lsp.ClientSymbolKindOptions
+---
+---The symbol kind values the client supports. When this
+---property exists the client also guarantees that it will
+---handle values outside its set gracefully and falls back
+---to a default value when unknown.
+---
+---If this property is not present the client only supports
+---the symbol kinds from `File` to `Array` as defined in
+---the initial version of the protocol.
+---@field valueSet? lsp.SymbolKind[]
+
+---@since 3.18.0
+---@class lsp.ClientSymbolTagOptions
+---
+---The tags supported by the client.
+---@field valueSet lsp.SymbolTag[]
+
+---@since 3.18.0
+---@class lsp.ClientSymbolResolveOptions
+---
+---The properties that a client can resolve lazily. Usually
+---`location.range`
+---@field properties string[]
+
+---@since 3.18.0
+---@class lsp.ClientCompletionItemOptions
+---
+---Client supports snippets as insert text.
+---
+---A snippet can define tab stops and placeholders with `$1`, `$2`
+---and `${3:foo}`. `$0` defines the final tab stop, it defaults to
+---the end of the snippet. Placeholders with equal identifiers are linked,
+---that is typing in one will update others too.
+---@field snippetSupport? boolean
+---
+---Client supports commit characters on a completion item.
+---@field commitCharactersSupport? boolean
+---
+---Client supports the following content formats for the documentation
+---property. The order describes the preferred format of the client.
+---@field documentationFormat? lsp.MarkupKind[]
+---
+---Client supports the deprecated property on a completion item.
+---@field deprecatedSupport? boolean
+---
+---Client supports the preselect property on a completion item.
+---@field preselectSupport? boolean
+---
+---Client supports the tag property on a completion item. Clients supporting
+---tags have to handle unknown tags gracefully. Clients especially need to
+---preserve unknown tags when sending a completion item back to the server in
+---a resolve call.
+---
+---@since 3.15.0
+---@field tagSupport? lsp.CompletionItemTagOptions
+---
+---Client support insert replace edit to control different behavior if a
+---completion item is inserted in the text or should replace text.
+---
+---@since 3.16.0
+---@field insertReplaceSupport? boolean
+---
+---Indicates which properties a client can resolve lazily on a completion
+---item. Before version 3.16.0 only the predefined properties `documentation`
+---and `details` could be resolved lazily.
+---
+---@since 3.16.0
+---@field resolveSupport? lsp.ClientCompletionItemResolveOptions
+---
+---The client supports the `insertTextMode` property on
+---a completion item to override the whitespace handling mode
+---as defined by the client (see `insertTextMode`).
+---
+---@since 3.16.0
+---@field insertTextModeSupport? lsp.ClientCompletionItemInsertTextModeOptions
+---
+---The client has support for completion item label
+---details (see also `CompletionItemLabelDetails`).
+---
+---@since 3.17.0
+---@field labelDetailsSupport? boolean
+
+---@since 3.18.0
+---@class lsp.ClientCompletionItemOptionsKind
+---
+---The completion item kind values the client supports. When this
+---property exists the client also guarantees that it will
+---handle values outside its set gracefully and falls back
+---to a default value when unknown.
+---
+---If this property is not present the client only supports
+---the completion items kinds from `Text` to `Reference` as defined in
+---the initial version of the protocol.
+---@field valueSet? lsp.CompletionItemKind[]
+
+---The client supports the following `CompletionList` specific
+---capabilities.
+---
+---@since 3.17.0
+---@class lsp.CompletionListCapabilities
+---
+---The client supports the following itemDefaults on
+---a completion list.
+---
+---The value lists the supported property names of the
+---`CompletionList.itemDefaults` object. If omitted
+---no properties are supported.
+---
+---@since 3.17.0
+---@field itemDefaults? string[]
+
+---@since 3.18.0
+---@class lsp.ClientSignatureInformationOptions
+---
+---Client supports the following content formats for the documentation
+---property. The order describes the preferred format of the client.
+---@field documentationFormat? lsp.MarkupKind[]
+---
+---Client capabilities specific to parameter information.
+---@field parameterInformation? lsp.ClientSignatureParameterInformationOptions
+---
+---The client supports the `activeParameter` property on `SignatureInformation`
+---literal.
+---
+---@since 3.16.0
+---@field activeParameterSupport? boolean
+---
+---The client supports the `activeParameter` property on
+---`SignatureHelp`/`SignatureInformation` being set to `null` to
+---indicate that no parameter should be active.
+---
+---@since 3.18.0
+---@proposed
+---@field noActiveParameterSupport? boolean
+
+---@since 3.18.0
+---@class lsp.ClientCodeActionLiteralOptions
+---
+---The code action kind is support with the following value
+---set.
+---@field codeActionKind lsp.ClientCodeActionKindOptions
+
+---@since 3.18.0
+---@class lsp.ClientCodeActionResolveOptions
+---
+---The properties that a client can resolve lazily.
+---@field properties string[]
+
+---@since 3.18.0
+---@class lsp.ClientCodeLensResolveOptions
+---
+---The properties that a client can resolve lazily.
+---@field properties string[]
+
+---@since 3.18.0
+---@class lsp.ClientFoldingRangeKindOptions
+---
+---The folding range kind values the client supports. When this
+---property exists the client also guarantees that it will
+---handle values outside its set gracefully and falls back
+---to a default value when unknown.
+---@field valueSet? lsp.FoldingRangeKind[]
+
+---@since 3.18.0
+---@class lsp.ClientFoldingRangeOptions
+---
+---If set, the client signals that it supports setting collapsedText on
+---folding ranges to display custom labels instead of the default text.
+---
+---@since 3.17.0
+---@field collapsedText? boolean
+
+---General diagnostics capabilities for pull and push model.
+---@class lsp.DiagnosticsCapabilities
+---
+---Whether the clients accepts diagnostics with related information.
+---@field relatedInformation? boolean
+---
+---Client supports the tag property to provide meta data about a diagnostic.
+---Clients supporting tags have to handle unknown tags gracefully.
+---
+---@since 3.15.0
+---@field tagSupport? lsp.ClientDiagnosticsTagOptions
+---
+---Client supports a codeDescription property
+---
+---@since 3.16.0
+---@field codeDescriptionSupport? boolean
+---
+---Whether code action supports the `data` property which is
+---preserved between a `textDocument/publishDiagnostics` and
+---`textDocument/codeAction` request.
+---
+---@since 3.16.0
+---@field dataSupport? boolean
+
+---@since 3.18.0
+---@class lsp.ClientSemanticTokensRequestOptions
+---
+---The client will send the `textDocument/semanticTokens/range` request if
+---the server provides a corresponding handler.
+---@field range? boolean|lsp._anonym2.range
+---
+---The client will send the `textDocument/semanticTokens/full` request if
+---the server provides a corresponding handler.
+---@field full? boolean|lsp.ClientSemanticTokensRequestFullDelta
+
+---@since 3.18.0
+---@class lsp.ClientInlayHintResolveOptions
+---
+---The properties that a client can resolve lazily.
+---@field properties string[]
+
+---@since 3.18.0
+---@class lsp.ClientShowMessageActionItemOptions
+---
+---Whether the client supports additional attributes which
+---are preserved and send back to the server in the
+---request's response.
+---@field additionalPropertiesSupport? boolean
+
+---@since 3.18.0
+---@class lsp.CompletionItemTagOptions
+---
+---The tags supported by the client.
+---@field valueSet lsp.CompletionItemTag[]
+
+---@since 3.18.0
+---@class lsp.ClientCompletionItemResolveOptions
+---
+---The properties that a client can resolve lazily.
+---@field properties string[]
+
+---@since 3.18.0
+---@class lsp.ClientCompletionItemInsertTextModeOptions
+---
+---@field valueSet lsp.InsertTextMode[]
+
+---@since 3.18.0
+---@class lsp.ClientSignatureParameterInformationOptions
+---
+---The client supports processing label offsets instead of a
+---simple label string.
+---
+---@since 3.14.0
+---@field labelOffsetSupport? boolean
+
+---@since 3.18.0
+---@class lsp.ClientCodeActionKindOptions
+---
+---The code action kind values the client supports. When this
+---property exists the client also guarantees that it will
+---handle values outside its set gracefully and falls back
+---to a default value when unknown.
+---@field valueSet lsp.CodeActionKind[]
+
+---@since 3.18.0
+---@class lsp.ClientDiagnosticsTagOptions
+---
+---The tags supported by the client.
+---@field valueSet lsp.DiagnosticTag[]
+
+---@since 3.18.0
+---@class lsp.ClientSemanticTokensRequestFullDelta
+---
+---The client will send the `textDocument/semanticTokens/full/delta` request if
+---the server provides a corresponding handler.
+---@field delta? boolean
+
---A set of predefined token types. This set is not fixed
---an clients can specify additional token types via the
---corresponding client capabilities.
@@ -4325,6 +5010,7 @@ error('Cannot require a meta file')
---| "regexp" # regexp
---| "operator" # operator
---| "decorator" # decorator
+---| "label" # label
---A set of predefined token modifiers. This set is not fixed
---an clients can specify additional token types via the
@@ -4515,12 +5201,14 @@ error('Cannot require a meta file')
---| "refactor" # Refactor
---| "refactor.extract" # RefactorExtract
---| "refactor.inline" # RefactorInline
+---| "refactor.move" # RefactorMove
---| "refactor.rewrite" # RefactorRewrite
---| "source" # Source
---| "source.organizeImports" # SourceOrganizeImports
---| "source.fixAll" # SourceFixAll
+---| "notebook" # Notebook
----@alias lsp.TraceValues
+---@alias lsp.TraceValue
---| "off" # Off
---| "messages" # Messages
---| "verbose" # Verbose
@@ -4534,13 +5222,79 @@ error('Cannot require a meta file')
---| "plaintext" # PlainText
---| "markdown" # Markdown
+---Predefined Language kinds
+---@since 3.18.0
+---@proposed
+---@alias lsp.LanguageKind
+---| "abap" # ABAP
+---| "bat" # WindowsBat
+---| "bibtex" # BibTeX
+---| "clojure" # Clojure
+---| "coffeescript" # Coffeescript
+---| "c" # C
+---| "cpp" # CPP
+---| "csharp" # CSharp
+---| "css" # CSS
+---| "d" # D
+---| "pascal" # Delphi
+---| "diff" # Diff
+---| "dart" # Dart
+---| "dockerfile" # Dockerfile
+---| "elixir" # Elixir
+---| "erlang" # Erlang
+---| "fsharp" # FSharp
+---| "git-commit" # GitCommit
+---| "rebase" # GitRebase
+---| "go" # Go
+---| "groovy" # Groovy
+---| "handlebars" # Handlebars
+---| "haskell" # Haskell
+---| "html" # HTML
+---| "ini" # Ini
+---| "java" # Java
+---| "javascript" # JavaScript
+---| "javascriptreact" # JavaScriptReact
+---| "json" # JSON
+---| "latex" # LaTeX
+---| "less" # Less
+---| "lua" # Lua
+---| "makefile" # Makefile
+---| "markdown" # Markdown
+---| "objective-c" # ObjectiveC
+---| "objective-cpp" # ObjectiveCPP
+---| "pascal" # Pascal
+---| "perl" # Perl
+---| "perl6" # Perl6
+---| "php" # PHP
+---| "powershell" # Powershell
+---| "jade" # Pug
+---| "python" # Python
+---| "r" # R
+---| "razor" # Razor
+---| "ruby" # Ruby
+---| "rust" # Rust
+---| "scss" # SCSS
+---| "sass" # SASS
+---| "scala" # Scala
+---| "shaderlab" # ShaderLab
+---| "shellscript" # ShellScript
+---| "sql" # SQL
+---| "swift" # Swift
+---| "typescript" # TypeScript
+---| "typescriptreact" # TypeScriptReact
+---| "tex" # TeX
+---| "vb" # VisualBasic
+---| "xml" # XML
+---| "xsl" # XSL
+---| "yaml" # YAML
+
---Describes how an {@link InlineCompletionItemProvider inline completion provider} was triggered.
---
---@since 3.18.0
---@proposed
---@alias lsp.InlineCompletionTriggerKind
----| 0 # Invoked
----| 1 # Automatic
+---| 1 # Invoked
+---| 2 # Automatic
---A set of predefined position encoding kinds.
---
@@ -4684,7 +5438,7 @@ error('Cannot require a meta file')
---@since 3.17.0
---@alias lsp.DocumentDiagnosticReport lsp.RelatedFullDocumentDiagnosticReport|lsp.RelatedUnchangedDocumentDiagnosticReport
----@alias lsp.PrepareRenameResult lsp.Range|lsp._anonym46.PrepareRenameResult|lsp._anonym47.PrepareRenameResult
+---@alias lsp.PrepareRenameResult lsp.Range|lsp.PrepareRenamePlaceholder|lsp.PrepareRenameDefaultBehavior
---A document selector is the combination of one or many document filters.
---
@@ -4705,7 +5459,7 @@ error('Cannot require a meta file')
---An event describing a change to a text document. If only a text is provided
---it is considered to be the full content of the document.
----@alias lsp.TextDocumentContentChangeEvent lsp._anonym48.TextDocumentContentChangeEvent|lsp._anonym49.TextDocumentContentChangeEvent
+---@alias lsp.TextDocumentContentChangeEvent lsp.TextDocumentContentChangePartial|lsp.TextDocumentContentChangeWholeDocument
---MarkedString can be used to render human readable text. It is either a markdown string
---or a code-block that provides a language and a code snippet. The language identifier
@@ -4719,7 +5473,7 @@ error('Cannot require a meta file')
---
---Note that markdown strings will be sanitized - that means html will be escaped.
---@deprecated use MarkupContent instead.
----@alias lsp.MarkedString string|lsp._anonym50.MarkedString
+---@alias lsp.MarkedString string|lsp.MarkedStringWithLanguage
---A document filter describes a top level text document or
---a notebook cell document.
@@ -4752,14 +5506,14 @@ error('Cannot require a meta file')
---\@sample A language filter that applies to all package.json paths: `{ language: 'json', pattern: '**package.json' }`
---
---@since 3.17.0
----@alias lsp.TextDocumentFilter lsp._anonym51.TextDocumentFilter|lsp._anonym52.TextDocumentFilter|lsp._anonym53.TextDocumentFilter
+---@alias lsp.TextDocumentFilter lsp.TextDocumentFilterLanguage|lsp.TextDocumentFilterScheme|lsp.TextDocumentFilterPattern
---A notebook document filter denotes a notebook document by
---different properties. The properties will be match
---against the notebook's URI (same as with documents)
---
---@since 3.17.0
----@alias lsp.NotebookDocumentFilter lsp._anonym54.NotebookDocumentFilter|lsp._anonym55.NotebookDocumentFilter|lsp._anonym56.NotebookDocumentFilter
+---@alias lsp.NotebookDocumentFilter lsp.NotebookDocumentFilterNotebookType|lsp.NotebookDocumentFilterScheme|lsp.NotebookDocumentFilterPattern
---The glob pattern to watch relative to the base path. Glob patterns can have the following syntax:
---- `*` to match one or more characters in a path segment
@@ -4772,512 +5526,8 @@ error('Cannot require a meta file')
---@since 3.17.0
---@alias lsp.Pattern string
----@class lsp._anonym1.serverInfo
----
----The name of the server as defined by the server.
----@field name string
----
----The server's version as defined by the server.
----@field version? string
-
----@class lsp._anonym3.itemDefaults.editRange
----
----@field insert lsp.Range
----
----@field replace lsp.Range
-
----@class lsp._anonym2.itemDefaults
----
----A default commit character set.
----
----@since 3.17.0
----@field commitCharacters? string[]
----
----A default edit range.
----
----@since 3.17.0
----@field editRange? lsp.Range|lsp._anonym3.itemDefaults.editRange
----
----A default insert text format.
----
----@since 3.17.0
----@field insertTextFormat? lsp.InsertTextFormat
----
----A default insert text mode.
----
----@since 3.17.0
----@field insertTextMode? lsp.InsertTextMode
----
----A default data value.
----
----@since 3.17.0
----@field data? lsp.LSPAny
-
----@class lsp._anonym4.disabled
----
----Human readable description of why the code action is currently disabled.
----
----This is displayed in the code actions UI.
----@field reason string
-
----@class lsp._anonym5.location
----
----@field uri lsp.DocumentUri
-
----@class lsp._anonym6.range
-
----@class lsp._anonym7.full
----
----The server supports deltas for full documents.
----@field delta? boolean
-
----@class lsp._anonym9.cells.structure
----
----The change to the cell array.
----@field array lsp.NotebookCellArrayChange
----
----Additional opened cell text documents.
----@field didOpen? lsp.TextDocumentItem[]
----
----Additional closed cell text documents.
----@field didClose? lsp.TextDocumentIdentifier[]
-
----@class lsp._anonym10.cells.textContent
----
----@field document lsp.VersionedTextDocumentIdentifier
----
----@field changes lsp.TextDocumentContentChangeEvent[]
-
----@class lsp._anonym8.cells
----
----Changes to the cell structure to add or
----remove cells.
----@field structure? lsp._anonym9.cells.structure
----
----Changes to notebook cells properties like its
----kind, execution summary or metadata.
----@field data? lsp.NotebookCell[]
----
----Changes to the text content of notebook cells.
----@field textContent? lsp._anonym10.cells.textContent[]
-
----@class lsp._anonym11.clientInfo
----
----The name of the client as defined by the client.
----@field name string
----
----The client's version as defined by the client.
----@field version? string
-
----@class lsp._anonym13.textDocument.diagnostic
----
----Whether the server supports `MarkupContent` in diagnostic messages.
----@field markupMessageSupport? boolean
-
----@class lsp._anonym12.textDocument
----
----Capabilities specific to the diagnostic pull model.
----
----@since 3.18.0
----@field diagnostic? lsp._anonym13.textDocument.diagnostic
-
----@class lsp._anonym14.workspace
----
----The server supports workspace folder.
----
----@since 3.6.0
----@field workspaceFolders? lsp.WorkspaceFoldersServerCapabilities
----
----The server is interested in notifications/requests for operations on files.
----
----@since 3.16.0
----@field fileOperations? lsp.FileOperationOptions
-
----@class lsp._anonym15.completionItem
----
----The server has support for completion item label
----details (see also `CompletionItemLabelDetails`) when
----receiving a completion item in a resolve call.
----
----@since 3.17.0
----@field labelDetailsSupport? boolean
-
----@class lsp._anonym17.notebookSelector.cells
----
----@field language string
-
----@class lsp._anonym16.notebookSelector
----
----The notebook to be synced If a string
----value is provided it matches against the
----notebook type. '*' matches every notebook.
----@field notebook string|lsp.NotebookDocumentFilter
----
----The cells of the matching notebook to be synced.
----@field cells? lsp._anonym17.notebookSelector.cells[]
-
----@class lsp._anonym19.notebookSelector.cells
----
----@field language string
-
----@class lsp._anonym18.notebookSelector
----
----The notebook to be synced If a string
----value is provided it matches against the
----notebook type. '*' matches every notebook.
----@field notebook? string|lsp.NotebookDocumentFilter
----
----The cells of the matching notebook to be synced.
----@field cells lsp._anonym19.notebookSelector.cells[]
-
----@class lsp._anonym20.staleRequestSupport
----
----The client will actively cancel the request.
----@field cancel boolean
----
----The list of requests for which the client
----will retry the request if it receives a
----response with error code `ContentModified`
----@field retryOnContentModified string[]
-
----@class lsp._anonym21.changeAnnotationSupport
----
----Whether the client groups edits with equal labels into tree nodes,
----for instance all edits labelled with "Changes in Strings" would
----be a tree node.
----@field groupsOnLabel? boolean
+---@alias lsp.RegularExpressionEngineKind string
----@class lsp._anonym22.symbolKind
----
----The symbol kind values the client supports. When this
----property exists the client also guarantees that it will
----handle values outside its set gracefully and falls back
----to a default value when unknown.
----
----If this property is not present the client only supports
----the symbol kinds from `File` to `Array` as defined in
----the initial version of the protocol.
----@field valueSet? lsp.SymbolKind[]
+---@class lsp._anonym1.range
----@class lsp._anonym23.tagSupport
----
----The tags supported by the client.
----@field valueSet lsp.SymbolTag[]
-
----@class lsp._anonym24.resolveSupport
----
----The properties that a client can resolve lazily. Usually
----`location.range`
----@field properties string[]
-
----@class lsp._anonym26.completionItem.tagSupport
----
----The tags supported by the client.
----@field valueSet lsp.CompletionItemTag[]
-
----@class lsp._anonym27.completionItem.resolveSupport
----
----The properties that a client can resolve lazily.
----@field properties string[]
-
----@class lsp._anonym28.completionItem.insertTextModeSupport
----
----@field valueSet lsp.InsertTextMode[]
-
----@class lsp._anonym25.completionItem
----
----Client supports snippets as insert text.
----
----A snippet can define tab stops and placeholders with `$1`, `$2`
----and `${3:foo}`. `$0` defines the final tab stop, it defaults to
----the end of the snippet. Placeholders with equal identifiers are linked,
----that is typing in one will update others too.
----@field snippetSupport? boolean
----
----Client supports commit characters on a completion item.
----@field commitCharactersSupport? boolean
----
----Client supports the following content formats for the documentation
----property. The order describes the preferred format of the client.
----@field documentationFormat? lsp.MarkupKind[]
----
----Client supports the deprecated property on a completion item.
----@field deprecatedSupport? boolean
----
----Client supports the preselect property on a completion item.
----@field preselectSupport? boolean
----
----Client supports the tag property on a completion item. Clients supporting
----tags have to handle unknown tags gracefully. Clients especially need to
----preserve unknown tags when sending a completion item back to the server in
----a resolve call.
----
----@since 3.15.0
----@field tagSupport? lsp._anonym26.completionItem.tagSupport
----
----Client support insert replace edit to control different behavior if a
----completion item is inserted in the text or should replace text.
----
----@since 3.16.0
----@field insertReplaceSupport? boolean
----
----Indicates which properties a client can resolve lazily on a completion
----item. Before version 3.16.0 only the predefined properties `documentation`
----and `details` could be resolved lazily.
----
----@since 3.16.0
----@field resolveSupport? lsp._anonym27.completionItem.resolveSupport
----
----The client supports the `insertTextMode` property on
----a completion item to override the whitespace handling mode
----as defined by the client (see `insertTextMode`).
----
----@since 3.16.0
----@field insertTextModeSupport? lsp._anonym28.completionItem.insertTextModeSupport
----
----The client has support for completion item label
----details (see also `CompletionItemLabelDetails`).
----
----@since 3.17.0
----@field labelDetailsSupport? boolean
-
----@class lsp._anonym29.completionItemKind
----
----The completion item kind values the client supports. When this
----property exists the client also guarantees that it will
----handle values outside its set gracefully and falls back
----to a default value when unknown.
----
----If this property is not present the client only supports
----the completion items kinds from `Text` to `Reference` as defined in
----the initial version of the protocol.
----@field valueSet? lsp.CompletionItemKind[]
-
----@class lsp._anonym30.completionList
----
----The client supports the following itemDefaults on
----a completion list.
----
----The value lists the supported property names of the
----`CompletionList.itemDefaults` object. If omitted
----no properties are supported.
----
----@since 3.17.0
----@field itemDefaults? string[]
-
----@class lsp._anonym32.signatureInformation.parameterInformation
----
----The client supports processing label offsets instead of a
----simple label string.
----
----@since 3.14.0
----@field labelOffsetSupport? boolean
-
----@class lsp._anonym31.signatureInformation
----
----Client supports the following content formats for the documentation
----property. The order describes the preferred format of the client.
----@field documentationFormat? lsp.MarkupKind[]
----
----Client capabilities specific to parameter information.
----@field parameterInformation? lsp._anonym32.signatureInformation.parameterInformation
----
----The client supports the `activeParameter` property on `SignatureInformation`
----literal.
----
----@since 3.16.0
----@field activeParameterSupport? boolean
----
----The client supports the `activeParameter` property on
----`SignatureInformation` being set to `null` to indicate that no
----parameter should be active.
----
----@since 3.18.0
----@field noActiveParameterSupport? boolean
-
----@class lsp._anonym33.symbolKind
----
----The symbol kind values the client supports. When this
----property exists the client also guarantees that it will
----handle values outside its set gracefully and falls back
----to a default value when unknown.
----
----If this property is not present the client only supports
----the symbol kinds from `File` to `Array` as defined in
----the initial version of the protocol.
----@field valueSet? lsp.SymbolKind[]
-
----@class lsp._anonym34.tagSupport
----
----The tags supported by the client.
----@field valueSet lsp.SymbolTag[]
-
----@class lsp._anonym36.codeActionLiteralSupport.codeActionKind
----
----The code action kind values the client supports. When this
----property exists the client also guarantees that it will
----handle values outside its set gracefully and falls back
----to a default value when unknown.
----@field valueSet lsp.CodeActionKind[]
-
----@class lsp._anonym35.codeActionLiteralSupport
----
----The code action kind is support with the following value
----set.
----@field codeActionKind lsp._anonym36.codeActionLiteralSupport.codeActionKind
-
----@class lsp._anonym37.resolveSupport
----
----The properties that a client can resolve lazily.
----@field properties string[]
-
----@class lsp._anonym38.foldingRangeKind
----
----The folding range kind values the client supports. When this
----property exists the client also guarantees that it will
----handle values outside its set gracefully and falls back
----to a default value when unknown.
----@field valueSet? lsp.FoldingRangeKind[]
-
----@class lsp._anonym39.foldingRange
----
----If set, the client signals that it supports setting collapsedText on
----folding ranges to display custom labels instead of the default text.
----
----@since 3.17.0
----@field collapsedText? boolean
-
----@class lsp._anonym40.tagSupport
----
----The tags supported by the client.
----@field valueSet lsp.DiagnosticTag[]
-
----@class lsp._anonym42.requests.range
-
----@class lsp._anonym43.requests.full
----
----The client will send the `textDocument/semanticTokens/full/delta` request if
----the server provides a corresponding handler.
----@field delta? boolean
-
----@class lsp._anonym41.requests
----
----The client will send the `textDocument/semanticTokens/range` request if
----the server provides a corresponding handler.
----@field range? boolean|lsp._anonym42.requests.range
----
----The client will send the `textDocument/semanticTokens/full` request if
----the server provides a corresponding handler.
----@field full? boolean|lsp._anonym43.requests.full
-
----@class lsp._anonym44.resolveSupport
----
----The properties that a client can resolve lazily.
----@field properties string[]
-
----@class lsp._anonym45.messageActionItem
----
----Whether the client supports additional attributes which
----are preserved and send back to the server in the
----request's response.
----@field additionalPropertiesSupport? boolean
-
----@class lsp._anonym46.PrepareRenameResult
----
----@field range lsp.Range
----
----@field placeholder string
-
----@class lsp._anonym47.PrepareRenameResult
----
----@field defaultBehavior boolean
-
----@class lsp._anonym48.TextDocumentContentChangeEvent
----
----The range of the document that changed.
----@field range lsp.Range
----
----The optional length of the range that got replaced.
----
----@deprecated use range instead.
----@field rangeLength? uinteger
----
----The new text for the provided range.
----@field text string
-
----@class lsp._anonym49.TextDocumentContentChangeEvent
----
----The new text of the whole document.
----@field text string
-
----@class lsp._anonym50.MarkedString
----
----@field language string
----
----@field value string
-
----@class lsp._anonym51.TextDocumentFilter
----
----A language id, like `typescript`.
----@field language string
----
----A Uri {@link Uri.scheme scheme}, like `file` or `untitled`.
----@field scheme? string
----
----A glob pattern, like **/*.{ts,js}. See TextDocumentFilter for examples.
----@field pattern? string
-
----@class lsp._anonym52.TextDocumentFilter
----
----A language id, like `typescript`.
----@field language? string
----
----A Uri {@link Uri.scheme scheme}, like `file` or `untitled`.
----@field scheme string
----
----A glob pattern, like **/*.{ts,js}. See TextDocumentFilter for examples.
----@field pattern? string
-
----@class lsp._anonym53.TextDocumentFilter
----
----A language id, like `typescript`.
----@field language? string
----
----A Uri {@link Uri.scheme scheme}, like `file` or `untitled`.
----@field scheme? string
----
----A glob pattern, like **/*.{ts,js}. See TextDocumentFilter for examples.
----@field pattern string
-
----@class lsp._anonym54.NotebookDocumentFilter
----
----The type of the enclosing notebook.
----@field notebookType string
----
----A Uri {@link Uri.scheme scheme}, like `file` or `untitled`.
----@field scheme? string
----
----A glob pattern.
----@field pattern? string
-
----@class lsp._anonym55.NotebookDocumentFilter
----
----The type of the enclosing notebook.
----@field notebookType? string
----
----A Uri {@link Uri.scheme scheme}, like `file` or `untitled`.
----@field scheme string
----
----A glob pattern.
----@field pattern? string
-
----@class lsp._anonym56.NotebookDocumentFilter
----
----The type of the enclosing notebook.
----@field notebookType? string
----
----A Uri {@link Uri.scheme scheme}, like `file` or `untitled`.
----@field scheme? string
----
----A glob pattern.
----@field pattern string
+---@class lsp._anonym2.range
diff --git a/runtime/lua/vim/lsp/_watchfiles.lua b/runtime/lua/vim/lsp/_watchfiles.lua
index 49328fbe9b..98e9818bcd 100644
--- a/runtime/lua/vim/lsp/_watchfiles.lua
+++ b/runtime/lua/vim/lsp/_watchfiles.lua
@@ -9,8 +9,8 @@ local M = {}
if vim.fn.has('win32') == 1 or vim.fn.has('mac') == 1 then
M._watchfunc = watch.watch
-elseif vim.fn.executable('fswatch') == 1 then
- M._watchfunc = watch.fswatch
+elseif vim.fn.executable('inotifywait') == 1 then
+ M._watchfunc = watch.inotify
else
M._watchfunc = watch.watchdirs
end
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index 49833eaeec..301c1f0cb6 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -29,7 +29,12 @@ local function request(method, params, handler)
end
--- Displays hover information about the symbol under the cursor in a floating
---- window. Calling the function twice will jump into the floating window.
+--- window. The window will be dismissed on cursor move.
+--- Calling the function twice will jump into the floating window
+--- (thus by default, "KK" will open the hover window and focus it).
+--- In the floating window, all commands and mappings are available as usual,
+--- except that "q" dismisses the window.
+--- You can scroll the contents the same as you would any other buffer.
function M.hover()
local params = util.make_position_params()
request(ms.textDocument_hover, params)
@@ -135,7 +140,7 @@ end
---@param mode "v"|"V"
---@return table {start={row,col}, end={row,col}} using (1, 0) indexing
local function range_from_selection(bufnr, mode)
- -- TODO: Use `vim.region()` instead https://github.com/neovim/neovim/pull/13896
+ -- TODO: Use `vim.fn.getregionpos()` instead.
-- [bufnum, lnum, col, off]; both row and column 1-indexed
local start = vim.fn.getpos('v')
@@ -205,9 +210,11 @@ end
--- Range to format.
--- Table must contain `start` and `end` keys with {row,col} tuples using
--- (1,0) indexing.
+--- Can also be a list of tables that contain `start` and `end` keys as described above,
+--- in which case `textDocument/rangesFormatting` support is required.
--- (Default: current selection in visual mode, `nil` in other modes,
--- formatting the full buffer)
---- @field range? {start:integer[],end:integer[]}
+--- @field range? {start:[integer,integer],end:[integer, integer]}|{start:[integer,integer],end:[integer,integer]}[]
--- Formats a buffer using the attached (and optionally filtered) language
--- server clients.
@@ -218,10 +225,20 @@ function M.format(opts)
local bufnr = opts.bufnr or api.nvim_get_current_buf()
local mode = api.nvim_get_mode().mode
local range = opts.range
+ -- Try to use visual selection if no range is given
if not range and mode == 'v' or mode == 'V' then
range = range_from_selection(bufnr, mode)
end
- local method = range and ms.textDocument_rangeFormatting or ms.textDocument_formatting
+
+ local passed_multiple_ranges = (range and #range ~= 0 and type(range[1]) == 'table')
+ local method ---@type string
+ if passed_multiple_ranges then
+ method = ms.textDocument_rangesFormatting
+ elseif range then
+ method = ms.textDocument_rangeFormatting
+ else
+ method = ms.textDocument_formatting
+ end
local clients = vim.lsp.get_clients({
id = opts.id,
@@ -241,10 +258,14 @@ function M.format(opts)
--- @param params lsp.DocumentFormattingParams
--- @return lsp.DocumentFormattingParams
local function set_range(client, params)
- if range then
- local range_params =
- util.make_given_range_params(range.start, range['end'], bufnr, client.offset_encoding)
- params.range = range_params.range
+ local to_lsp_range = function(r) ---@return lsp.DocumentRangeFormattingParams|lsp.DocumentRangesFormattingParams
+ return util.make_given_range_params(r.start, r['end'], bufnr, client.offset_encoding).range
+ end
+
+ if passed_multiple_ranges then
+ params.ranges = vim.tbl_map(to_lsp_range, range)
+ elseif range then
+ params.range = to_lsp_range(range)
end
return params
end
@@ -431,11 +452,9 @@ function M.document_symbol(opts)
request_with_opts(ms.textDocument_documentSymbol, params, opts)
end
---- @param call_hierarchy_items lsp.CallHierarchyItem[]?
+--- @param call_hierarchy_items lsp.CallHierarchyItem[]
+--- @return lsp.CallHierarchyItem?
local function pick_call_hierarchy_item(call_hierarchy_items)
- if not call_hierarchy_items then
- return
- end
if #call_hierarchy_items == 1 then
return call_hierarchy_items[1]
end
@@ -448,7 +467,7 @@ local function pick_call_hierarchy_item(call_hierarchy_items)
if choice < 1 or choice > #items then
return
end
- return choice
+ return call_hierarchy_items[choice]
end
--- @param method string
@@ -460,7 +479,7 @@ local function call_hierarchy(method)
vim.notify(err.message, vim.log.levels.WARN)
return
end
- if not result then
+ if not result or vim.tbl_isempty(result) then
vim.notify('No item resolved', vim.log.levels.WARN)
return
end
@@ -836,14 +855,10 @@ function M.code_action(opts)
if opts.diagnostics or opts.only then
opts = { options = opts }
end
- local context = opts.context or {}
+ local context = opts.context and vim.deepcopy(opts.context) or {}
if not context.triggerKind then
context.triggerKind = vim.lsp.protocol.CodeActionTriggerKind.Invoked
end
- if not context.diagnostics then
- local bufnr = api.nvim_get_current_buf()
- context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics(bufnr)
- end
local mode = api.nvim_get_mode().mode
local bufnr = api.nvim_get_current_buf()
local win = api.nvim_get_current_win()
@@ -885,7 +900,23 @@ function M.code_action(opts)
else
params = util.make_range_params(win, client.offset_encoding)
end
- params.context = context
+ if context.diagnostics then
+ params.context = context
+ else
+ local ns_push = vim.lsp.diagnostic.get_namespace(client.id, false)
+ local ns_pull = vim.lsp.diagnostic.get_namespace(client.id, true)
+ local diagnostics = {}
+ local lnum = api.nvim_win_get_cursor(0)[1] - 1
+ vim.list_extend(diagnostics, vim.diagnostic.get(bufnr, { namespace = ns_pull, lnum = lnum }))
+ vim.list_extend(diagnostics, vim.diagnostic.get(bufnr, { namespace = ns_push, lnum = lnum }))
+ params.context = vim.tbl_extend('force', context, {
+ ---@diagnostic disable-next-line: no-unknown
+ diagnostics = vim.tbl_map(function(d)
+ return d.user_data.lsp
+ end, diagnostics),
+ })
+ end
+
client.request(ms.textDocument_codeAction, params, on_result, bufnr)
end
end
diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua
index 4beb7fefda..e3c82f4169 100644
--- a/runtime/lua/vim/lsp/client.lua
+++ b/runtime/lua/vim/lsp/client.lua
@@ -182,7 +182,7 @@ local validate = vim.validate
--- It can be `null` if the client supports workspace folders but none are
--- configured.
--- @field workspace_folders lsp.WorkspaceFolder[]?
---- @field root_dir string
+--- @field root_dir string?
---
--- @field attached_buffers table<integer,true>
---
@@ -233,11 +233,11 @@ local validate = vim.validate
---
--- Sends a request to the server and synchronously waits for the response.
--- This is a wrapper around {client.request}
---- Returns: { err=err, result=result }, a dictionary, where `err` and `result`
+--- Returns: { err=err, result=result }, a dict, where `err` and `result`
--- come from the |lsp-handler|. On timeout, cancel or error, returns `(nil,
--- err)` where `err` is a string describing the failure reason. If the request
--- was unsuccessful returns `nil`.
---- @field request_sync fun(method: string, params: table?, timeout_ms: integer?, bufnr: integer): {err: lsp.ResponseError|nil, result:any}|nil, string|nil err # a dictionary, where
+--- @field request_sync fun(method: string, params: table?, timeout_ms: integer?, bufnr: integer): {err: lsp.ResponseError|nil, result:any}|nil, string|nil err # a dict
---
--- Sends a notification to an LSP server.
--- Returns: a boolean to indicate if the notification was successful. If
@@ -436,7 +436,7 @@ local function ensure_list(x)
return { x }
end
---- @package
+--- @nodoc
--- @param config vim.lsp.ClientConfig
--- @return vim.lsp.Client?
function Client.create(config)
@@ -470,7 +470,6 @@ function Client.create(config)
_on_exit_cbs = ensure_list(config.on_exit),
_on_attach_cbs = ensure_list(config.on_attach),
_on_error_cb = config.on_error,
- _root_dir = config.root_dir,
_trace = get_trace(config.trace),
--- Contains $/progress report messages.
@@ -536,7 +535,7 @@ function Client:_run_callbacks(cbs, error_id, ...)
end
end
---- @package
+--- @nodoc
function Client:initialize()
local config = self.config
@@ -657,7 +656,7 @@ end
--- @param method string LSP method name.
--- @param params? table LSP request params.
--- @param handler? lsp.Handler Response |lsp-handler| for this method.
---- @param bufnr? integer Buffer handle (0 for current).
+--- @param bufnr integer Buffer handle (0 for current).
--- @return boolean status, integer? request_id {status} is a bool indicating
--- whether the request was successful. If it is `false`, then it will
--- always be `false` (the client has shutdown). If it was
@@ -739,7 +738,7 @@ end
--- @param timeout_ms (integer|nil) Maximum time in milliseconds to wait for
--- a result. Defaults to 1000
--- @param bufnr (integer) Buffer handle (0 for current).
---- @return {err: lsp.ResponseError|nil, result:any}|nil, string|nil err # a dictionary, where
+--- @return {err: lsp.ResponseError|nil, result:any}|nil, string|nil err # a dict, where
--- `err` and `result` come from the |lsp-handler|.
--- On timeout, cancel or error, returns `(nil, err)` where `err` is a
--- string describing the failure reason. If the request was unsuccessful
@@ -862,14 +861,14 @@ function Client:_is_stopped()
return self.rpc.is_closing()
end
---- @package
--- Execute a lsp command, either via client command function (if available)
--- or via workspace/executeCommand (if supported by the server)
---
--- @param command lsp.Command
--- @param context? {bufnr: integer}
--- @param handler? lsp.Handler only called if a server command
-function Client:_exec_cmd(command, context, handler)
+--- @param on_unsupported? function handler invoked when the command is not supported by the client.
+function Client:_exec_cmd(command, context, handler, on_unsupported)
context = vim.deepcopy(context or {}, true) --[[@as lsp.HandlerContext]]
context.bufnr = context.bufnr or api.nvim_get_current_buf()
context.client_id = self.id
@@ -883,14 +882,18 @@ function Client:_exec_cmd(command, context, handler)
local command_provider = self.server_capabilities.executeCommandProvider
local commands = type(command_provider) == 'table' and command_provider.commands or {}
if not vim.list_contains(commands, cmdname) then
- vim.notify_once(
- string.format(
- 'Language server `%s` does not support command `%s`. This command may require a client extension.',
- self.name,
- cmdname
- ),
- vim.log.levels.WARN
- )
+ if on_unsupported then
+ on_unsupported()
+ else
+ vim.notify_once(
+ string.format(
+ 'Language server `%s` does not support command `%s`. This command may require a client extension.',
+ self.name,
+ cmdname
+ ),
+ vim.log.levels.WARN
+ )
+ end
return
end
-- Not using command directly to exclude extra properties,
@@ -902,7 +905,6 @@ function Client:_exec_cmd(command, context, handler)
self.request(ms.workspace_executeCommand, params, handler, context.bufnr)
end
---- @package
--- Default handler for the 'textDocument/didOpen' LSP notification.
---
--- @param bufnr integer Number of the buffer, or 0 for current
@@ -914,18 +916,16 @@ function Client:_text_document_did_open_handler(bufnr)
if not api.nvim_buf_is_loaded(bufnr) then
return
end
- local filetype = vim.bo[bufnr].filetype
- local params = {
+ local filetype = vim.bo[bufnr].filetype
+ self.notify(ms.textDocument_didOpen, {
textDocument = {
- version = 0,
+ version = lsp.util.buf_versions[bufnr],
uri = vim.uri_from_bufnr(bufnr),
languageId = self.get_language_id(bufnr, filetype),
text = lsp._buf_get_full_text(bufnr),
},
- }
- self.notify(ms.textDocument_didOpen, params)
- lsp.util.buf_versions[bufnr] = params.textDocument.version
+ })
-- Next chance we get, we should re-do the diagnostics
vim.schedule(function()
@@ -938,7 +938,6 @@ function Client:_text_document_did_open_handler(bufnr)
end)
end
---- @package
--- Runs the on_attach function from the client's config if it was defined.
--- @param bufnr integer Buffer number
function Client:_on_attach(bufnr)
@@ -1061,7 +1060,6 @@ function Client:_on_exit(code, signal)
)
end
---- @package
--- Add a directory to the workspace folders.
--- @param dir string?
function Client:_add_workspace_folder(dir)
@@ -1084,7 +1082,6 @@ function Client:_add_workspace_folder(dir)
vim.list_extend(self.workspace_folders, wf)
end
---- @package
--- Remove a directory to the workspace folders.
--- @param dir string?
function Client:_remove_workspace_folder(dir)
diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua
index c85bb6aa32..c1b6bfb28c 100644
--- a/runtime/lua/vim/lsp/codelens.lua
+++ b/runtime/lua/vim/lsp/codelens.lua
@@ -307,7 +307,13 @@ function M.refresh(opts)
}
active_refreshes[buf] = true
- local request_ids = vim.lsp.buf_request(buf, ms.textDocument_codeLens, params, M.on_codelens)
+ local request_ids = vim.lsp.buf_request(
+ buf,
+ ms.textDocument_codeLens,
+ params,
+ M.on_codelens,
+ function() end
+ )
if vim.tbl_isempty(request_ids) then
active_refreshes[buf] = nil
end
diff --git a/runtime/lua/vim/lsp/completion.lua b/runtime/lua/vim/lsp/completion.lua
new file mode 100644
index 0000000000..71ea2df100
--- /dev/null
+++ b/runtime/lua/vim/lsp/completion.lua
@@ -0,0 +1,754 @@
+local M = {}
+
+local api = vim.api
+local lsp = vim.lsp
+local protocol = lsp.protocol
+local ms = protocol.Methods
+
+local rtt_ms = 50
+local ns_to_ms = 0.000001
+
+--- @alias vim.lsp.CompletionResult lsp.CompletionList | lsp.CompletionItem[]
+
+-- TODO(mariasolos): Remove this declaration once we figure out a better way to handle
+-- literal/anonymous types (see https://github.com/neovim/neovim/pull/27542/files#r1495259331).
+--- @nodoc
+--- @class lsp.ItemDefaults
+--- @field editRange lsp.Range | { insert: lsp.Range, replace: lsp.Range } | nil
+--- @field insertTextFormat lsp.InsertTextFormat?
+--- @field insertTextMode lsp.InsertTextMode?
+--- @field data any
+
+--- @nodoc
+--- @class vim.lsp.completion.BufHandle
+--- @field clients table<integer, vim.lsp.Client>
+--- @field triggers table<string, vim.lsp.Client[]>
+--- @field convert? fun(item: lsp.CompletionItem): table
+
+--- @type table<integer, vim.lsp.completion.BufHandle>
+local buf_handles = {}
+
+--- @nodoc
+--- @class vim.lsp.completion.Context
+local Context = {
+ cursor = nil, --- @type [integer, integer]?
+ last_request_time = nil, --- @type integer?
+ pending_requests = {}, --- @type function[]
+ isIncomplete = false,
+}
+
+--- @nodoc
+function Context:cancel_pending()
+ for _, cancel in ipairs(self.pending_requests) do
+ cancel()
+ end
+
+ self.pending_requests = {}
+end
+
+--- @nodoc
+function Context:reset()
+ -- Note that the cursor isn't reset here, it needs to survive a `CompleteDone` event.
+ self.isIncomplete = false
+ self.last_request_time = nil
+ self:cancel_pending()
+end
+
+--- @type uv.uv_timer_t?
+local completion_timer = nil
+
+--- @return uv.uv_timer_t
+local function new_timer()
+ return assert(vim.uv.new_timer())
+end
+
+local function reset_timer()
+ if completion_timer then
+ completion_timer:stop()
+ completion_timer:close()
+ end
+
+ completion_timer = nil
+end
+
+--- @param window integer
+--- @param warmup integer
+--- @return fun(sample: number): number
+local function exp_avg(window, warmup)
+ local count = 0
+ local sum = 0
+ local value = 0
+
+ return function(sample)
+ if count < warmup then
+ count = count + 1
+ sum = sum + sample
+ value = sum / count
+ else
+ local factor = 2.0 / (window + 1)
+ value = value * (1 - factor) + sample * factor
+ end
+ return value
+ end
+end
+local compute_new_average = exp_avg(10, 10)
+
+--- @return number
+local function next_debounce()
+ if not Context.last_request_time then
+ return rtt_ms
+ end
+
+ local ms_since_request = (vim.uv.hrtime() - Context.last_request_time) * ns_to_ms
+ return math.max((ms_since_request - rtt_ms) * -1, 0)
+end
+
+--- @param input string Unparsed snippet
+--- @return string # Parsed snippet if successful, else returns its input
+local function parse_snippet(input)
+ local ok, parsed = pcall(function()
+ return lsp._snippet_grammar.parse(input)
+ end)
+ return ok and tostring(parsed) or input
+end
+
+--- @param item lsp.CompletionItem
+--- @param suffix? string
+local function apply_snippet(item, suffix)
+ if item.textEdit then
+ vim.snippet.expand(item.textEdit.newText .. suffix)
+ elseif item.insertText then
+ vim.snippet.expand(item.insertText .. suffix)
+ end
+end
+
+--- Returns text that should be inserted when a selecting completion item. The
+--- precedence is as follows: textEdit.newText > insertText > label
+---
+--- See https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion
+---
+--- @param item lsp.CompletionItem
+--- @return string
+local function get_completion_word(item)
+ if item.insertTextFormat == protocol.InsertTextFormat.Snippet then
+ if item.textEdit then
+ -- Use label instead of text if text has different starting characters.
+ -- label is used as abbr (=displayed), but word is used for filtering
+ -- This is required for things like postfix completion.
+ -- E.g. in lua:
+ --
+ -- local f = {}
+ -- f@|
+ -- ▲
+ -- └─ cursor
+ --
+ -- item.textEdit.newText: table.insert(f, $0)
+ -- label: insert
+ --
+ -- Typing `i` would remove the candidate because newText starts with `t`.
+ local text = parse_snippet(item.insertText or item.textEdit.newText)
+ return #text < #item.label and vim.fn.matchstr(text, '\\k*') or item.label
+ elseif item.insertText and item.insertText ~= '' then
+ return parse_snippet(item.insertText)
+ else
+ return item.label
+ end
+ elseif item.textEdit then
+ local word = item.textEdit.newText
+ return word:match('^(%S*)') or word
+ elseif item.insertText and item.insertText ~= '' then
+ return item.insertText
+ end
+ return item.label
+end
+
+--- Applies the given defaults to the completion item, modifying it in place.
+---
+--- @param item lsp.CompletionItem
+--- @param defaults lsp.ItemDefaults?
+local function apply_defaults(item, defaults)
+ if not defaults then
+ return
+ end
+
+ item.insertTextFormat = item.insertTextFormat or defaults.insertTextFormat
+ item.insertTextMode = item.insertTextMode or defaults.insertTextMode
+ item.data = item.data or defaults.data
+ if defaults.editRange then
+ local textEdit = item.textEdit or {}
+ item.textEdit = textEdit
+ textEdit.newText = textEdit.newText or item.textEditText or item.insertText or item.label
+ if defaults.editRange.start then
+ textEdit.range = textEdit.range or defaults.editRange
+ elseif defaults.editRange.insert then
+ textEdit.insert = defaults.editRange.insert
+ textEdit.replace = defaults.editRange.replace
+ end
+ end
+end
+
+--- @param result vim.lsp.CompletionResult
+--- @return lsp.CompletionItem[]
+local function get_items(result)
+ if result.items then
+ -- When we have a list, apply the defaults and return an array of items.
+ for _, item in ipairs(result.items) do
+ ---@diagnostic disable-next-line: param-type-mismatch
+ apply_defaults(item, result.itemDefaults)
+ end
+ return result.items
+ else
+ -- Else just return the items as they are.
+ return result
+ end
+end
+
+---@param item lsp.CompletionItem
+---@return string
+local function get_doc(item)
+ local doc = item.documentation
+ if not doc then
+ return ''
+ end
+ if type(doc) == 'string' then
+ return doc
+ end
+ if type(doc) == 'table' and type(doc.value) == 'string' then
+ return doc.value
+ end
+
+ vim.notify('invalid documentation value: ' .. vim.inspect(doc), vim.log.levels.WARN)
+ return ''
+end
+
+--- Turns the result of a `textDocument/completion` request into vim-compatible
+--- |complete-items|.
+---
+--- @private
+--- @param result vim.lsp.CompletionResult Result of `textDocument/completion`
+--- @param prefix string prefix to filter the completion items
+--- @param client_id integer? Client ID
+--- @return table[]
+--- @see complete-items
+function M._lsp_to_complete_items(result, prefix, client_id)
+ local items = get_items(result)
+ if vim.tbl_isempty(items) then
+ return {}
+ end
+
+ ---@type fun(item: lsp.CompletionItem):boolean
+ local matches
+ if not prefix:find('%w') then
+ matches = function(_)
+ return true
+ end
+ else
+ ---@param item lsp.CompletionItem
+ matches = function(item)
+ local text = item.filterText or item.label
+ return next(vim.fn.matchfuzzy({ text }, prefix)) ~= nil
+ end
+ end
+
+ local candidates = {}
+ local bufnr = api.nvim_get_current_buf()
+ local user_convert = vim.tbl_get(buf_handles, bufnr, 'convert')
+ for _, item in ipairs(items) do
+ if matches(item) then
+ local word = get_completion_word(item)
+ local hl_group = ''
+ if
+ item.deprecated
+ or vim.list_contains((item.tags or {}), protocol.CompletionTag.Deprecated)
+ then
+ hl_group = 'DiagnosticDeprecated'
+ end
+ local completion_item = {
+ word = word,
+ abbr = item.label,
+ kind = protocol.CompletionItemKind[item.kind] or 'Unknown',
+ menu = item.detail or '',
+ info = get_doc(item),
+ icase = 1,
+ dup = 1,
+ empty = 1,
+ hl_group = hl_group,
+ user_data = {
+ nvim = {
+ lsp = {
+ completion_item = item,
+ client_id = client_id,
+ },
+ },
+ },
+ }
+ if user_convert then
+ completion_item = vim.tbl_extend('keep', user_convert(item), completion_item)
+ end
+ table.insert(candidates, completion_item)
+ end
+ end
+ ---@diagnostic disable-next-line: no-unknown
+ table.sort(candidates, function(a, b)
+ ---@type lsp.CompletionItem
+ local itema = a.user_data.nvim.lsp.completion_item
+ ---@type lsp.CompletionItem
+ local itemb = b.user_data.nvim.lsp.completion_item
+ return (itema.sortText or itema.label) < (itemb.sortText or itemb.label)
+ end)
+
+ return candidates
+end
+
+--- @param lnum integer 0-indexed
+--- @param line string
+--- @param items lsp.CompletionItem[]
+--- @param encoding string
+--- @return integer?
+local function adjust_start_col(lnum, line, items, encoding)
+ local min_start_char = nil
+ for _, item in pairs(items) do
+ if item.textEdit and item.textEdit.range.start.line == lnum then
+ if min_start_char and min_start_char ~= item.textEdit.range.start.character then
+ return nil
+ end
+ min_start_char = item.textEdit.range.start.character
+ end
+ end
+ if min_start_char then
+ return lsp.util._str_byteindex_enc(line, min_start_char, encoding)
+ else
+ return nil
+ end
+end
+
+--- @private
+--- @param line string line content
+--- @param lnum integer 0-indexed line number
+--- @param cursor_col integer
+--- @param client_id integer client ID
+--- @param client_start_boundary integer 0-indexed word boundary
+--- @param server_start_boundary? integer 0-indexed word boundary, based on textEdit.range.start.character
+--- @param result vim.lsp.CompletionResult
+--- @param encoding string
+--- @return table[] matches
+--- @return integer? server_start_boundary
+function M._convert_results(
+ line,
+ lnum,
+ cursor_col,
+ client_id,
+ client_start_boundary,
+ server_start_boundary,
+ result,
+ encoding
+)
+ -- Completion response items may be relative to a position different than `client_start_boundary`.
+ -- Concrete example, with lua-language-server:
+ --
+ -- require('plenary.asy|
+ -- ▲ ▲ ▲
+ -- │ │ └── cursor_pos: 20
+ -- │ └────── client_start_boundary: 17
+ -- └────────────── textEdit.range.start.character: 9
+ -- .newText = 'plenary.async'
+ -- ^^^
+ -- prefix (We'd remove everything not starting with `asy`,
+ -- so we'd eliminate the `plenary.async` result
+ --
+ -- `adjust_start_col` is used to prefer the language server boundary.
+ --
+ local candidates = get_items(result)
+ local curstartbyte = adjust_start_col(lnum, line, candidates, encoding)
+ if server_start_boundary == nil then
+ server_start_boundary = curstartbyte
+ elseif curstartbyte ~= nil and curstartbyte ~= server_start_boundary then
+ server_start_boundary = client_start_boundary
+ end
+ local prefix = line:sub((server_start_boundary or client_start_boundary) + 1, cursor_col)
+ local matches = M._lsp_to_complete_items(result, prefix, client_id)
+ return matches, server_start_boundary
+end
+
+--- @param clients table<integer, vim.lsp.Client> # keys != client_id
+--- @param bufnr integer
+--- @param win integer
+--- @param callback fun(responses: table<integer, { err: lsp.ResponseError, result: vim.lsp.CompletionResult }>)
+--- @return function # Cancellation function
+local function request(clients, bufnr, win, callback)
+ local responses = {} --- @type table<integer, { err: lsp.ResponseError, result: any }>
+ local request_ids = {} --- @type table<integer, integer>
+ local remaining_requests = vim.tbl_count(clients)
+
+ for _, client in pairs(clients) do
+ local client_id = client.id
+ local params = lsp.util.make_position_params(win, client.offset_encoding)
+ local ok, request_id = client.request(ms.textDocument_completion, params, function(err, result)
+ responses[client_id] = { err = err, result = result }
+ remaining_requests = remaining_requests - 1
+ if remaining_requests == 0 then
+ callback(responses)
+ end
+ end, bufnr)
+
+ if ok then
+ request_ids[client_id] = request_id
+ end
+ end
+
+ return function()
+ for client_id, request_id in pairs(request_ids) do
+ local client = lsp.get_client_by_id(client_id)
+ if client then
+ client.cancel_request(request_id)
+ end
+ end
+ end
+end
+
+local function trigger(bufnr, clients)
+ reset_timer()
+ Context:cancel_pending()
+
+ if tonumber(vim.fn.pumvisible()) == 1 and not Context.isIncomplete then
+ return
+ end
+
+ local win = api.nvim_get_current_win()
+ local cursor_row, cursor_col = unpack(api.nvim_win_get_cursor(win)) --- @type integer, integer
+ local line = api.nvim_get_current_line()
+ local line_to_cursor = line:sub(1, cursor_col)
+ local word_boundary = vim.fn.match(line_to_cursor, '\\k*$')
+ local start_time = vim.uv.hrtime()
+ Context.last_request_time = start_time
+
+ local cancel_request = request(clients, bufnr, win, function(responses)
+ local end_time = vim.uv.hrtime()
+ rtt_ms = compute_new_average((end_time - start_time) * ns_to_ms)
+
+ Context.pending_requests = {}
+ Context.isIncomplete = false
+
+ local row_changed = api.nvim_win_get_cursor(win)[1] ~= cursor_row
+ local mode = api.nvim_get_mode().mode
+ if row_changed or not (mode == 'i' or mode == 'ic') then
+ return
+ end
+
+ local matches = {}
+ local server_start_boundary --- @type integer?
+ for client_id, response in pairs(responses) do
+ if response.err then
+ vim.notify_once(response.err.message, vim.log.levels.warn)
+ end
+
+ local result = response.result
+ if result then
+ Context.isIncomplete = Context.isIncomplete or result.isIncomplete
+ local client = lsp.get_client_by_id(client_id)
+ local encoding = client and client.offset_encoding or 'utf-16'
+ local client_matches
+ client_matches, server_start_boundary = M._convert_results(
+ line,
+ cursor_row - 1,
+ cursor_col,
+ client_id,
+ word_boundary,
+ nil,
+ result,
+ encoding
+ )
+ vim.list_extend(matches, client_matches)
+ end
+ end
+ local start_col = (server_start_boundary or word_boundary) + 1
+ vim.fn.complete(start_col, matches)
+ end)
+
+ table.insert(Context.pending_requests, cancel_request)
+end
+
+--- @param handle vim.lsp.completion.BufHandle
+local function on_insert_char_pre(handle)
+ if tonumber(vim.fn.pumvisible()) == 1 then
+ if Context.isIncomplete then
+ reset_timer()
+
+ local debounce_ms = next_debounce()
+ if debounce_ms == 0 then
+ vim.schedule(M.trigger)
+ else
+ completion_timer = new_timer()
+ completion_timer:start(debounce_ms, 0, vim.schedule_wrap(M.trigger))
+ end
+ end
+
+ return
+ end
+
+ local char = api.nvim_get_vvar('char')
+ if not completion_timer and handle.triggers[char] then
+ completion_timer = assert(vim.uv.new_timer())
+ completion_timer:start(25, 0, function()
+ reset_timer()
+ vim.schedule(M.trigger)
+ end)
+ end
+end
+
+local function on_insert_leave()
+ reset_timer()
+ Context.cursor = nil
+ Context:reset()
+end
+
+local function on_complete_done()
+ local completed_item = api.nvim_get_vvar('completed_item')
+ if not completed_item or not completed_item.user_data or not completed_item.user_data.nvim then
+ Context:reset()
+ return
+ end
+
+ local cursor_row, cursor_col = unpack(api.nvim_win_get_cursor(0)) --- @type integer, integer
+ cursor_row = cursor_row - 1
+ local completion_item = completed_item.user_data.nvim.lsp.completion_item --- @type lsp.CompletionItem
+ local client_id = completed_item.user_data.nvim.lsp.client_id --- @type integer
+ if not completion_item or not client_id then
+ Context:reset()
+ return
+ end
+
+ local bufnr = api.nvim_get_current_buf()
+ local expand_snippet = completion_item.insertTextFormat == protocol.InsertTextFormat.Snippet
+ and (completion_item.textEdit ~= nil or completion_item.insertText ~= nil)
+
+ Context:reset()
+
+ local client = lsp.get_client_by_id(client_id)
+ if not client then
+ return
+ end
+
+ local offset_encoding = client.offset_encoding or 'utf-16'
+ local resolve_provider = (client.server_capabilities.completionProvider or {}).resolveProvider
+
+ local function clear_word()
+ if not expand_snippet then
+ return nil
+ end
+
+ -- Remove the already inserted word.
+ local start_char = cursor_col - #completed_item.word
+ local line = api.nvim_buf_get_lines(bufnr, cursor_row, cursor_row + 1, true)[1]
+ api.nvim_buf_set_text(bufnr, cursor_row, start_char, cursor_row, #line, { '' })
+ return line:sub(cursor_col + 1)
+ end
+
+ --- @param suffix? string
+ local function apply_snippet_and_command(suffix)
+ if expand_snippet then
+ apply_snippet(completion_item, suffix)
+ end
+
+ local command = completion_item.command
+ if command then
+ client:_exec_cmd(command, { bufnr = bufnr }, nil, function()
+ vim.lsp.log.warn(
+ string.format(
+ 'Language server `%s` does not support command `%s`. This command may require a client extension.',
+ client.name,
+ command.command
+ )
+ )
+ end)
+ end
+ end
+
+ if completion_item.additionalTextEdits and next(completion_item.additionalTextEdits) then
+ local suffix = clear_word()
+ lsp.util.apply_text_edits(completion_item.additionalTextEdits, bufnr, offset_encoding)
+ apply_snippet_and_command(suffix)
+ elseif resolve_provider and type(completion_item) == 'table' then
+ local changedtick = vim.b[bufnr].changedtick
+
+ --- @param result lsp.CompletionItem
+ client.request(ms.completionItem_resolve, completion_item, function(err, result)
+ if changedtick ~= vim.b[bufnr].changedtick then
+ return
+ end
+
+ local suffix = clear_word()
+ if err then
+ vim.notify_once(err.message, vim.log.levels.WARN)
+ elseif result and result.additionalTextEdits then
+ lsp.util.apply_text_edits(result.additionalTextEdits, bufnr, offset_encoding)
+ if result.command then
+ completion_item.command = result.command
+ end
+ end
+
+ apply_snippet_and_command(suffix)
+ end, bufnr)
+ else
+ local suffix = clear_word()
+ apply_snippet_and_command(suffix)
+ end
+end
+
+--- @class vim.lsp.completion.BufferOpts
+--- @field autotrigger? boolean Whether to trigger completion automatically. Default: false
+--- @field convert? fun(item: lsp.CompletionItem): table Transforms an LSP CompletionItem to |complete-items|.
+
+---@param client_id integer
+---@param bufnr integer
+---@param opts vim.lsp.completion.BufferOpts
+local function enable_completions(client_id, bufnr, opts)
+ local buf_handle = buf_handles[bufnr]
+ if not buf_handle then
+ buf_handle = { clients = {}, triggers = {}, convert = opts.convert }
+ buf_handles[bufnr] = buf_handle
+
+ -- Attach to buffer events.
+ api.nvim_buf_attach(bufnr, false, {
+ on_detach = function(_, buf)
+ buf_handles[buf] = nil
+ end,
+ on_reload = function(_, buf)
+ M.enable(true, client_id, buf, opts)
+ end,
+ })
+
+ -- Set up autocommands.
+ local group =
+ api.nvim_create_augroup(string.format('vim/lsp/completion-%d', bufnr), { clear = true })
+ api.nvim_create_autocmd('CompleteDone', {
+ group = group,
+ buffer = bufnr,
+ callback = function()
+ local reason = api.nvim_get_vvar('event').reason --- @type string
+ if reason == 'accept' then
+ on_complete_done()
+ end
+ end,
+ })
+ if opts.autotrigger then
+ api.nvim_create_autocmd('InsertCharPre', {
+ group = group,
+ buffer = bufnr,
+ callback = function()
+ on_insert_char_pre(buf_handles[bufnr])
+ end,
+ })
+ api.nvim_create_autocmd('InsertLeave', {
+ group = group,
+ buffer = bufnr,
+ callback = on_insert_leave,
+ })
+ end
+ end
+
+ if not buf_handle.clients[client_id] then
+ local client = lsp.get_client_by_id(client_id)
+ assert(client, 'invalid client ID')
+
+ -- Add the new client to the buffer's clients.
+ buf_handle.clients[client_id] = client
+
+ -- Add the new client to the clients that should be triggered by its trigger characters.
+ --- @type string[]
+ local triggers = vim.tbl_get(
+ client.server_capabilities,
+ 'completionProvider',
+ 'triggerCharacters'
+ ) or {}
+ for _, char in ipairs(triggers) do
+ local clients_for_trigger = buf_handle.triggers[char]
+ if not clients_for_trigger then
+ clients_for_trigger = {}
+ buf_handle.triggers[char] = clients_for_trigger
+ end
+ local client_exists = vim.iter(clients_for_trigger):any(function(c)
+ return c.id == client_id
+ end)
+ if not client_exists then
+ table.insert(clients_for_trigger, client)
+ end
+ end
+ end
+end
+
+--- @param client_id integer
+--- @param bufnr integer
+local function disable_completions(client_id, bufnr)
+ local handle = buf_handles[bufnr]
+ if not handle then
+ return
+ end
+
+ handle.clients[client_id] = nil
+ if not next(handle.clients) then
+ buf_handles[bufnr] = nil
+ api.nvim_del_augroup_by_name(string.format('vim/lsp/completion-%d', bufnr))
+ else
+ for char, clients in pairs(handle.triggers) do
+ --- @param c vim.lsp.Client
+ handle.triggers[char] = vim.tbl_filter(function(c)
+ return c.id ~= client_id
+ end, clients)
+ end
+ end
+end
+
+--- Enables or disables completions from the given language client in the given buffer.
+---
+--- @param enable boolean True to enable, false to disable
+--- @param client_id integer Client ID
+--- @param bufnr integer Buffer handle, or 0 for the current buffer
+--- @param opts? vim.lsp.completion.BufferOpts
+function M.enable(enable, client_id, bufnr, opts)
+ bufnr = (bufnr == 0 and api.nvim_get_current_buf()) or bufnr
+
+ if enable then
+ enable_completions(client_id, bufnr, opts or {})
+ else
+ disable_completions(client_id, bufnr)
+ end
+end
+
+--- Trigger LSP completion in the current buffer.
+function M.trigger()
+ local bufnr = api.nvim_get_current_buf()
+ local clients = (buf_handles[bufnr] or {}).clients or {}
+ trigger(bufnr, clients)
+end
+
+--- Implements 'omnifunc' compatible LSP completion.
+---
+--- @see |complete-functions|
+--- @see |complete-items|
+--- @see |CompleteDone|
+---
+--- @param findstart integer 0 or 1, decides behavior
+--- @param base integer findstart=0, text to match against
+---
+--- @return integer|table Decided by {findstart}:
+--- - findstart=0: column where the completion starts, or -2 or -3
+--- - findstart=1: list of matches (actually just calls |complete()|)
+function M._omnifunc(findstart, base)
+ vim.lsp.log.debug('omnifunc.findstart', { findstart = findstart, base = base })
+ assert(base) -- silence luals
+ local bufnr = api.nvim_get_current_buf()
+ local clients = lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_completion })
+ local remaining = #clients
+ if remaining == 0 then
+ return findstart == 1 and -1 or {}
+ end
+
+ trigger(bufnr, clients)
+
+ -- Return -2 to signal that we should continue completion so that we can
+ -- async complete.
+ return -2
+end
+
+return M
diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua
index 08cea13548..c10312484b 100644
--- a/runtime/lua/vim/lsp/diagnostic.lua
+++ b/runtime/lua/vim/lsp/diagnostic.lua
@@ -110,6 +110,14 @@ local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)
return vim.tbl_map(function(diagnostic)
local start = diagnostic.range.start
local _end = diagnostic.range['end']
+ local message = diagnostic.message
+ if type(message) ~= 'string' then
+ vim.notify_once(
+ string.format('Unsupported Markup message from LSP client %d', client_id),
+ vim.lsp.log_levels.ERROR
+ )
+ message = diagnostic.message.value
+ end
--- @type vim.Diagnostic
return {
lnum = start.line,
@@ -117,18 +125,12 @@ local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)
end_lnum = _end.line,
end_col = line_byte_from_position(buf_lines, _end.line, _end.character, offset_encoding),
severity = severity_lsp_to_vim(diagnostic.severity),
- message = diagnostic.message,
+ message = message,
source = diagnostic.source,
code = diagnostic.code,
_tags = tags_lsp_to_vim(diagnostic, client_id),
user_data = {
- lsp = {
- -- usage of user_data.lsp.code is deprecated in favor of the top-level code field
- code = diagnostic.code,
- codeDescription = diagnostic.codeDescription,
- relatedInformation = diagnostic.relatedInformation,
- data = diagnostic.data,
- },
+ lsp = diagnostic,
},
}
end, diagnostics)
@@ -151,14 +153,18 @@ local function tags_vim_to_lsp(diagnostic)
return tags
end
+--- Converts the input `vim.Diagnostic`s to LSP diagnostics.
--- @param diagnostics vim.Diagnostic[]
--- @return lsp.Diagnostic[]
-local function diagnostic_vim_to_lsp(diagnostics)
+function M.from(diagnostics)
---@param diagnostic vim.Diagnostic
---@return lsp.Diagnostic
return vim.tbl_map(function(diagnostic)
- return vim.tbl_extend('keep', {
- -- "keep" the below fields over any duplicate fields in diagnostic.user_data.lsp
+ local user_data = diagnostic.user_data or {}
+ if user_data.lsp then
+ return user_data.lsp
+ end
+ return {
range = {
start = {
line = diagnostic.lnum,
@@ -174,7 +180,7 @@ local function diagnostic_vim_to_lsp(diagnostics)
source = diagnostic.source,
code = diagnostic.code,
tags = tags_vim_to_lsp(diagnostic),
- }, diagnostic.user_data and (diagnostic.user_data.lsp or {}) or {})
+ }
end, diagnostics)
end
@@ -366,6 +372,7 @@ end
--- Structured: { [1] = {...}, [5] = {.... } }
---@private
function M.get_line_diagnostics(bufnr, line_nr, opts, client_id)
+ vim.deprecate('vim.lsp.diagnostic.get_line_diagnostics', 'vim.diagnostic.get', '0.12')
convert_severity(opts)
local diag_opts = {} --- @type vim.diagnostic.GetOpts
@@ -379,7 +386,7 @@ function M.get_line_diagnostics(bufnr, line_nr, opts, client_id)
diag_opts.lnum = line_nr or (api.nvim_win_get_cursor(0)[1] - 1)
- return diagnostic_vim_to_lsp(vim.diagnostic.get(bufnr, diag_opts))
+ return M.from(vim.diagnostic.get(bufnr, diag_opts))
end
--- Clear diagnostics from pull based clients
diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index f9d394642c..44548fec92 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -3,7 +3,7 @@ local protocol = require('vim.lsp.protocol')
local ms = protocol.Methods
local util = require('vim.lsp.util')
local api = vim.api
-local completion = require('vim.lsp._completion')
+local completion = require('vim.lsp.completion')
--- @type table<string,lsp.Handler>
local M = {}
@@ -646,6 +646,7 @@ M[ms.window_showMessage] = function(_, result, ctx, _)
if message_type == protocol.MessageType.Error then
err_message('LSP[', client_name, '] ', message)
else
+ --- @type string
local message_type_name = protocol.MessageType[message_type]
api.nvim_out_write(string.format('LSP[%s][%s] %s\n', client_name, message_type_name, message))
end
diff --git a/runtime/lua/vim/lsp/health.lua b/runtime/lua/vim/lsp/health.lua
index a79ae76eb9..18066a84db 100644
--- a/runtime/lua/vim/lsp/health.lua
+++ b/runtime/lua/vim/lsp/health.lua
@@ -33,16 +33,25 @@ local function check_active_clients()
local clients = vim.lsp.get_clients()
if next(clients) then
for _, client in pairs(clients) do
- local attached_to = table.concat(vim.tbl_keys(client.attached_buffers or {}), ',')
- report_info(
+ local cmd ---@type string
+ if type(client.config.cmd) == 'table' then
+ cmd = table.concat(client.config.cmd --[[@as table]], ' ')
+ elseif type(client.config.cmd) == 'function' then
+ cmd = tostring(client.config.cmd)
+ end
+ report_info(table.concat({
+ string.format('%s (id: %d)', client.name, client.id),
string.format(
- '%s (id=%s, root_dir=%s, attached_to=[%s])',
- client.name,
- client.id,
- vim.fn.fnamemodify(client.root_dir, ':~'),
- attached_to
- )
- )
+ ' Root directory: %s',
+ client.root_dir and vim.fn.fnamemodify(client.root_dir, ':~') or nil
+ ),
+ string.format(' Command: %s', cmd),
+ string.format(' Settings: %s', vim.inspect(client.settings, { newline = '\n ' })),
+ string.format(
+ ' Attached buffers: %s',
+ vim.iter(pairs(client.attached_buffers)):map(tostring):join(', ')
+ ),
+ }, '\n'))
end
else
report_info('No active clients')
@@ -50,7 +59,7 @@ local function check_active_clients()
end
local function check_watcher()
- vim.health.start('vim.lsp: File watcher')
+ vim.health.start('vim.lsp: File Watcher')
-- Only run the check if file watching has been enabled by a client.
local clients = vim.lsp.get_clients()
@@ -81,8 +90,8 @@ local function check_watcher()
watchfunc_name = 'libuv-watch'
elseif watchfunc == vim._watch.watchdirs then
watchfunc_name = 'libuv-watchdirs'
- elseif watchfunc == vim._watch.fswatch then
- watchfunc_name = 'fswatch'
+ elseif watchfunc == vim._watch.inotifywait then
+ watchfunc_name = 'inotifywait'
else
local nm = debug.getinfo(watchfunc, 'S').source
watchfunc_name = string.format('Custom (%s)', nm)
@@ -90,7 +99,63 @@ local function check_watcher()
report_info('File watch backend: ' .. watchfunc_name)
if watchfunc_name == 'libuv-watchdirs' then
- report_warn('libuv-watchdirs has known performance issues. Consider installing fswatch.')
+ report_warn('libuv-watchdirs has known performance issues. Consider installing inotify-tools.')
+ end
+end
+
+local function check_position_encodings()
+ vim.health.start('vim.lsp: Position Encodings')
+ local clients = vim.lsp.get_clients()
+ if next(clients) then
+ local position_encodings = {} ---@type table<integer, table<string, integer[]>>
+ for _, client in pairs(clients) do
+ for bufnr in pairs(client.attached_buffers) do
+ if not position_encodings[bufnr] then
+ position_encodings[bufnr] = {}
+ end
+ if not position_encodings[bufnr][client.offset_encoding] then
+ position_encodings[bufnr][client.offset_encoding] = {}
+ end
+ table.insert(position_encodings[bufnr][client.offset_encoding], client.id)
+ end
+ end
+
+ -- Check if any buffers are attached to multiple clients with different position encodings
+ local buffers = {} ---@type integer[]
+ for bufnr, encodings in pairs(position_encodings) do
+ local list = {} ---@type string[]
+ for k in pairs(encodings) do
+ list[#list + 1] = k
+ end
+
+ if #list > 1 then
+ buffers[#buffers + 1] = bufnr
+ end
+ end
+
+ if #buffers > 0 then
+ local lines =
+ { 'Found buffers attached to multiple clients with different position encodings.' }
+ for _, bufnr in ipairs(buffers) do
+ local encodings = position_encodings[bufnr]
+ local parts = {}
+ for encoding, client_ids in pairs(encodings) do
+ table.insert(
+ parts,
+ string.format('%s (client id(s): %s)', encoding:upper(), table.concat(client_ids, ', '))
+ )
+ end
+ table.insert(lines, string.format('- Buffer %d: %s', bufnr, table.concat(parts, ', ')))
+ end
+ report_warn(
+ table.concat(lines, '\n'),
+ 'Use the positionEncodings client capability to ensure all clients use the same position encoding'
+ )
+ else
+ report_info('No buffers contain mixed position encodings')
+ end
+ else
+ report_info('No active clients')
end
end
@@ -99,6 +164,7 @@ function M.check()
check_log()
check_active_clients()
check_watcher()
+ check_position_encodings()
end
return M
diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua
index f98496456b..61059180fe 100644
--- a/runtime/lua/vim/lsp/inlay_hint.lua
+++ b/runtime/lua/vim/lsp/inlay_hint.lua
@@ -43,17 +43,16 @@ function M.on_inlayhint(err, result, ctx, _)
return
end
local bufnr = assert(ctx.bufnr)
- if util.buf_versions[bufnr] ~= ctx.version then
+ if
+ util.buf_versions[bufnr] ~= ctx.version
+ or not result
+ or not api.nvim_buf_is_loaded(bufnr)
+ or not bufstates[bufnr].enabled
+ then
return
end
local client_id = ctx.client_id
- if not result then
- return
- end
local bufstate = bufstates[bufnr]
- if not bufstate.enabled then
- return
- end
if not (bufstate.client_hints and bufstate.version) then
bufstate.client_hints = vim.defaulttable()
bufstate.version = ctx.version
@@ -77,12 +76,7 @@ function M.on_inlayhint(err, result, ctx, _)
local col = position.character
if col > 0 then
local line = lines[position.line + 1] or ''
- local ok, convert_result
- ok, convert_result = pcall(util._str_byteindex_enc, line, col, client.offset_encoding)
- if ok then
- return convert_result
- end
- return math.min(#line, col)
+ return util._str_byteindex_enc(line, col, client.offset_encoding)
end
return col
end
@@ -336,6 +330,8 @@ api.nvim_set_decoration_provider(namespace, {
for lnum = topline, botline do
if bufstate.applied[lnum] ~= bufstate.version then
api.nvim_buf_clear_namespace(bufnr, namespace, lnum, lnum + 1)
+
+ local hint_virtual_texts = {} --- @type table<integer, [string, string?][]>
for _, lnum_hints in pairs(client_hints) do
local hints = lnum_hints[lnum] or {}
for _, hint in pairs(hints) do
@@ -348,7 +344,7 @@ api.nvim_set_decoration_provider(namespace, {
text = text .. part.value
end
end
- local vt = {} --- @type {[1]: string, [2]: string?}[]
+ local vt = hint_virtual_texts[hint.position.character] or {}
if hint.paddingLeft then
vt[#vt + 1] = { ' ' }
end
@@ -356,13 +352,18 @@ api.nvim_set_decoration_provider(namespace, {
if hint.paddingRight then
vt[#vt + 1] = { ' ' }
end
- api.nvim_buf_set_extmark(bufnr, namespace, lnum, hint.position.character, {
- virt_text_pos = 'inline',
- ephemeral = false,
- virt_text = vt,
- })
+ hint_virtual_texts[hint.position.character] = vt
end
end
+
+ for pos, vt in pairs(hint_virtual_texts) do
+ api.nvim_buf_set_extmark(bufnr, namespace, lnum, pos, {
+ virt_text_pos = 'inline',
+ ephemeral = false,
+ virt_text = vt,
+ })
+ end
+
bufstate.applied[lnum] = bufstate.version
end
end
@@ -370,7 +371,7 @@ api.nvim_set_decoration_provider(namespace, {
})
--- Query whether inlay hint is enabled in the {filter}ed scope
---- @param filter vim.lsp.inlay_hint.enable.Filter
+--- @param filter? vim.lsp.inlay_hint.enable.Filter
--- @return boolean
--- @since 12
function M.is_enabled(filter)
diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua
index 9f2bd71158..4f177b47fd 100644
--- a/runtime/lua/vim/lsp/log.lua
+++ b/runtime/lua/vim/lsp/log.lua
@@ -9,7 +9,7 @@ local log_levels = vim.log.levels
--- Can be used to lookup the number from the name or the name from the number.
--- Levels by name: "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "OFF"
--- Level numbers begin with "TRACE" at 0
---- @type table<string|integer, string|integer>
+--- @type table<string,integer> | table<integer, string>
--- @nodoc
log.levels = vim.deepcopy(log_levels)
@@ -19,7 +19,7 @@ local current_log_level = log_levels.WARN
local log_date_format = '%F %H:%M:%S'
local function format_func(arg)
- return vim.inspect(arg, { newline = '' })
+ return vim.inspect(arg, { newline = ' ', indent = '' })
end
local function notify(msg, level)
diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua
index 419c2ff644..1699fff0c1 100644
--- a/runtime/lua/vim/lsp/protocol.lua
+++ b/runtime/lua/vim/lsp/protocol.lua
@@ -12,9 +12,6 @@ end
local sysname = vim.uv.os_uname().sysname
--- Protocol for the Microsoft Language Server Protocol (mslsp)
-local protocol = {}
-
local constants = {
--- @enum lsp.DiagnosticSeverity
DiagnosticSeverity = {
@@ -46,6 +43,8 @@ local constants = {
Info = 3,
-- A log message.
Log = 4,
+ -- A debug message.
+ Debug = 5,
},
-- The file event type.
@@ -100,6 +99,13 @@ local constants = {
TriggerForIncompleteCompletions = 3,
},
+ -- Completion item tags are extra annotations that tweak the rendering of a
+ -- completion item
+ CompletionTag = {
+ -- Render a completion as obsolete, usually using a strike-out.
+ Deprecated = 1,
+ },
+
-- A document highlight kind.
DocumentHighlightKind = {
-- A textual occurrence.
@@ -308,326 +314,18 @@ local constants = {
},
}
-for k1, v1 in pairs(constants) do
- local tbl = vim.deepcopy(v1, true)
- for _, k2 in ipairs(vim.tbl_keys(tbl)) do
- local v2 = tbl[k2]
- tbl[v2] = k2
+-- Protocol for the Microsoft Language Server Protocol (mslsp)
+local protocol = {}
+
+--- @diagnostic disable:no-unknown
+for k1, v1 in pairs(vim.deepcopy(constants, true)) do
+ for _, k2 in ipairs(vim.tbl_keys(v1)) do
+ local v2 = v1[k2]
+ v1[v2] = k2
end
- protocol[k1] = tbl
+ protocol[k1] = v1
end
-
---[=[
---Text document specific client capabilities.
-export interface TextDocumentClientCapabilities {
- synchronization?: {
- --Whether text document synchronization supports dynamic registration.
- dynamicRegistration?: boolean;
- --The client supports sending will save notifications.
- willSave?: boolean;
- --The client supports sending a will save request and
- --waits for a response providing text edits which will
- --be applied to the document before it is saved.
- willSaveWaitUntil?: boolean;
- --The client supports did save notifications.
- didSave?: boolean;
- }
- --Capabilities specific to the `textDocument/completion`
- completion?: {
- --Whether completion supports dynamic registration.
- dynamicRegistration?: boolean;
- --The client supports the following `CompletionItem` specific
- --capabilities.
- completionItem?: {
- --The client supports snippets as insert text.
- --
- --A snippet can define tab stops and placeholders with `$1`, `$2`
- --and `${3:foo}`. `$0` defines the final tab stop, it defaults to
- --the end of the snippet. Placeholders with equal identifiers are linked,
- --that is typing in one will update others too.
- snippetSupport?: boolean;
- --The client supports commit characters on a completion item.
- commitCharactersSupport?: boolean
- --The client supports the following content formats for the documentation
- --property. The order describes the preferred format of the client.
- documentationFormat?: MarkupKind[];
- --The client supports the deprecated property on a completion item.
- deprecatedSupport?: boolean;
- --The client supports the preselect property on a completion item.
- preselectSupport?: boolean;
- }
- completionItemKind?: {
- --The completion item kind values the client supports. When this
- --property exists the client also guarantees that it will
- --handle values outside its set gracefully and falls back
- --to a default value when unknown.
- --
- --If this property is not present the client only supports
- --the completion items kinds from `Text` to `Reference` as defined in
- --the initial version of the protocol.
- valueSet?: CompletionItemKind[];
- },
- --The client supports to send additional context information for a
- --`textDocument/completion` request.
- contextSupport?: boolean;
- };
- --Capabilities specific to the `textDocument/hover`
- hover?: {
- --Whether hover supports dynamic registration.
- dynamicRegistration?: boolean;
- --The client supports the follow content formats for the content
- --property. The order describes the preferred format of the client.
- contentFormat?: MarkupKind[];
- };
- --Capabilities specific to the `textDocument/signatureHelp`
- signatureHelp?: {
- --Whether signature help supports dynamic registration.
- dynamicRegistration?: boolean;
- --The client supports the following `SignatureInformation`
- --specific properties.
- signatureInformation?: {
- --The client supports the follow content formats for the documentation
- --property. The order describes the preferred format of the client.
- documentationFormat?: MarkupKind[];
- --Client capabilities specific to parameter information.
- parameterInformation?: {
- --The client supports processing label offsets instead of a
- --simple label string.
- --
- --Since 3.14.0
- labelOffsetSupport?: boolean;
- }
- };
- };
- --Capabilities specific to the `textDocument/references`
- references?: {
- --Whether references supports dynamic registration.
- dynamicRegistration?: boolean;
- };
- --Capabilities specific to the `textDocument/documentHighlight`
- documentHighlight?: {
- --Whether document highlight supports dynamic registration.
- dynamicRegistration?: boolean;
- };
- --Capabilities specific to the `textDocument/documentSymbol`
- documentSymbol?: {
- --Whether document symbol supports dynamic registration.
- dynamicRegistration?: boolean;
- --Specific capabilities for the `SymbolKind`.
- symbolKind?: {
- --The symbol kind values the client supports. When this
- --property exists the client also guarantees that it will
- --handle values outside its set gracefully and falls back
- --to a default value when unknown.
- --
- --If this property is not present the client only supports
- --the symbol kinds from `File` to `Array` as defined in
- --the initial version of the protocol.
- valueSet?: SymbolKind[];
- }
- --The client supports hierarchical document symbols.
- hierarchicalDocumentSymbolSupport?: boolean;
- };
- --Capabilities specific to the `textDocument/formatting`
- formatting?: {
- --Whether formatting supports dynamic registration.
- dynamicRegistration?: boolean;
- };
- --Capabilities specific to the `textDocument/rangeFormatting`
- rangeFormatting?: {
- --Whether range formatting supports dynamic registration.
- dynamicRegistration?: boolean;
- };
- --Capabilities specific to the `textDocument/onTypeFormatting`
- onTypeFormatting?: {
- --Whether on type formatting supports dynamic registration.
- dynamicRegistration?: boolean;
- };
- --Capabilities specific to the `textDocument/declaration`
- declaration?: {
- --Whether declaration supports dynamic registration. If this is set to `true`
- --the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)`
- --return value for the corresponding server capability as well.
- dynamicRegistration?: boolean;
- --The client supports additional metadata in the form of declaration links.
- --
- --Since 3.14.0
- linkSupport?: boolean;
- };
- --Capabilities specific to the `textDocument/definition`.
- --
- --Since 3.14.0
- definition?: {
- --Whether definition supports dynamic registration.
- dynamicRegistration?: boolean;
- --The client supports additional metadata in the form of definition links.
- linkSupport?: boolean;
- };
- --Capabilities specific to the `textDocument/typeDefinition`
- --
- --Since 3.6.0
- typeDefinition?: {
- --Whether typeDefinition supports dynamic registration. If this is set to `true`
- --the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)`
- --return value for the corresponding server capability as well.
- dynamicRegistration?: boolean;
- --The client supports additional metadata in the form of definition links.
- --
- --Since 3.14.0
- linkSupport?: boolean;
- };
- --Capabilities specific to the `textDocument/implementation`.
- --
- --Since 3.6.0
- implementation?: {
- --Whether implementation supports dynamic registration. If this is set to `true`
- --the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)`
- --return value for the corresponding server capability as well.
- dynamicRegistration?: boolean;
- --The client supports additional metadata in the form of definition links.
- --
- --Since 3.14.0
- linkSupport?: boolean;
- };
- --Capabilities specific to the `textDocument/codeAction`
- codeAction?: {
- --Whether code action supports dynamic registration.
- dynamicRegistration?: boolean;
- --The client support code action literals as a valid
- --response of the `textDocument/codeAction` request.
- --
- --Since 3.8.0
- codeActionLiteralSupport?: {
- --The code action kind is support with the following value
- --set.
- codeActionKind: {
- --The code action kind values the client supports. When this
- --property exists the client also guarantees that it will
- --handle values outside its set gracefully and falls back
- --to a default value when unknown.
- valueSet: CodeActionKind[];
- };
- };
- };
- --Capabilities specific to the `textDocument/codeLens`
- codeLens?: {
- --Whether code lens supports dynamic registration.
- dynamicRegistration?: boolean;
- };
- --Capabilities specific to the `textDocument/documentLink`
- documentLink?: {
- --Whether document link supports dynamic registration.
- dynamicRegistration?: boolean;
- };
- --Capabilities specific to the `textDocument/documentColor` and the
- --`textDocument/colorPresentation` request.
- --
- --Since 3.6.0
- colorProvider?: {
- --Whether colorProvider supports dynamic registration. If this is set to `true`
- --the client supports the new `(ColorProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions)`
- --return value for the corresponding server capability as well.
- dynamicRegistration?: boolean;
- }
- --Capabilities specific to the `textDocument/rename`
- rename?: {
- --Whether rename supports dynamic registration.
- dynamicRegistration?: boolean;
- --The client supports testing for validity of rename operations
- --before execution.
- prepareSupport?: boolean;
- };
- --Capabilities specific to `textDocument/publishDiagnostics`.
- publishDiagnostics?: {
- --Whether the clients accepts diagnostics with related information.
- relatedInformation?: boolean;
- --Client supports the tag property to provide meta data about a diagnostic.
- --Clients supporting tags have to handle unknown tags gracefully.
- --Since 3.15.0
- tagSupport?: {
- --The tags supported by this client
- valueSet: DiagnosticTag[];
- };
- };
- --Capabilities specific to `textDocument/foldingRange` requests.
- --
- --Since 3.10.0
- foldingRange?: {
- --Whether implementation supports dynamic registration for folding range providers. If this is set to `true`
- --the client supports the new `(FoldingRangeProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions)`
- --return value for the corresponding server capability as well.
- dynamicRegistration?: boolean;
- --The maximum number of folding ranges that the client prefers to receive per document. The value serves as a
- --hint, servers are free to follow the limit.
- rangeLimit?: number;
- --If set, the client signals that it only supports folding complete lines. If set, client will
- --ignore specified `startCharacter` and `endCharacter` properties in a FoldingRange.
- lineFoldingOnly?: boolean;
- };
-}
---]=]
-
---[=[
---Workspace specific client capabilities.
-export interface WorkspaceClientCapabilities {
- --The client supports applying batch edits to the workspace by supporting
- --the request 'workspace/applyEdit'
- applyEdit?: boolean;
- --Capabilities specific to `WorkspaceEdit`s
- workspaceEdit?: {
- --The client supports versioned document changes in `WorkspaceEdit`s
- documentChanges?: boolean;
- --The resource operations the client supports. Clients should at least
- --support 'create', 'rename' and 'delete' files and folders.
- resourceOperations?: ResourceOperationKind[];
- --The failure handling strategy of a client if applying the workspace edit
- --fails.
- failureHandling?: FailureHandlingKind;
- };
- --Capabilities specific to the `workspace/didChangeConfiguration` notification.
- didChangeConfiguration?: {
- --Did change configuration notification supports dynamic registration.
- dynamicRegistration?: boolean;
- };
- --Capabilities specific to the `workspace/didChangeWatchedFiles` notification.
- didChangeWatchedFiles?: {
- --Did change watched files notification supports dynamic registration. Please note
- --that the current protocol doesn't support static configuration for file changes
- --from the server side.
- dynamicRegistration?: boolean;
- };
- --Capabilities specific to the `workspace/symbol` request.
- symbol?: {
- --Symbol request supports dynamic registration.
- dynamicRegistration?: boolean;
- --Specific capabilities for the `SymbolKind` in the `workspace/symbol` request.
- symbolKind?: {
- --The symbol kind values the client supports. When this
- --property exists the client also guarantees that it will
- --handle values outside its set gracefully and falls back
- --to a default value when unknown.
- --
- --If this property is not present the client only supports
- --the symbol kinds from `File` to `Array` as defined in
- --the initial version of the protocol.
- valueSet?: SymbolKind[];
- }
- };
- --Capabilities specific to the `workspace/executeCommand` request.
- executeCommand?: {
- --Execute command supports dynamic registration.
- dynamicRegistration?: boolean;
- };
- --The client has support for workspace folders.
- --
- --Since 3.6.0
- workspaceFolders?: boolean;
- --The client supports `workspace/configuration` requests.
- --
- --Since 3.6.0
- configuration?: boolean;
-}
---]=]
+--- @diagnostic enable:no-unknown
--- Gets a new ClientCapabilities object describing the LSP client
--- capabilities.
@@ -729,23 +427,35 @@ function protocol.make_client_capabilities()
properties = { 'edit' },
},
},
+ codeLens = {
+ dynamicRegistration = false,
+ resolveSupport = {
+ properties = { 'command' },
+ },
+ },
formatting = {
dynamicRegistration = true,
},
rangeFormatting = {
dynamicRegistration = true,
+ rangesSupport = true,
},
completion = {
dynamicRegistration = false,
completionItem = {
- -- Until we can actually expand snippet, move cursor and allow for true snippet experience,
- -- this should be disabled out of the box.
- -- However, users can turn this back on if they have a snippet plugin.
- snippetSupport = false,
+ snippetSupport = true,
commitCharactersSupport = false,
preselectSupport = false,
- deprecatedSupport = false,
+ deprecatedSupport = true,
documentationFormat = { constants.MarkupKind.Markdown, constants.MarkupKind.PlainText },
+ resolveSupport = {
+ properties = {
+ 'additionalTextEdits',
+ },
+ },
+ tagSupport = {
+ valueSet = get_value_set(constants.CompletionTag),
+ },
},
completionItemKind = {
valueSet = get_value_set(constants.CompletionItemKind),
@@ -852,7 +562,7 @@ function protocol.make_client_capabilities()
workDoneProgress = true,
showMessage = {
messageActionItem = {
- additionalPropertiesSupport = false,
+ additionalPropertiesSupport = true,
},
},
showDocument = {
@@ -905,9 +615,10 @@ function protocol.resolve_capabilities(server_capabilities)
end
-- Generated by gen_lsp.lua, keep at end of file.
---- LSP method names.
---
+---@enum vim.lsp.protocol.Methods
---@see https://microsoft.github.io/language-server-protocol/specification/#metaModel
+--- LSP method names.
protocol.Methods = {
--- A request to resolve the incoming calls for a given `CallHierarchyItem`.
--- @since 3.16.0
@@ -1170,14 +881,14 @@ protocol.Methods = {
--- symbol's location.
--- @since 3.17.0
workspaceSymbol_resolve = 'workspaceSymbol/resolve',
- --- A request sent from the server to the client to modify certain resources.
+ --- A request sent from the server to the client to modified certain resources.
workspace_applyEdit = 'workspace/applyEdit',
--- A request to refresh all code actions
--- @since 3.16.0
workspace_codeLens_refresh = 'workspace/codeLens/refresh',
--- The 'workspace/configuration' request is sent from the server to the client to fetch a certain
--- configuration setting.
- --- This pull model replaces the old push model where the client signaled configuration change via an
+ --- This pull model replaces the old push model were the client signaled configuration change via an
--- event. If the server still needs to react to configuration changes (since the server caches the
--- result of `workspace/configuration` requests) the server should register for an empty configuration
--- change event and empty the cache if such an event is received.
@@ -1210,7 +921,7 @@ protocol.Methods = {
--- files were renamed from within the client.
--- @since 3.16.0
workspace_didRenameFiles = 'workspace/didRenameFiles',
- --- A request sent from the client to the server to execute a command. The request might return
+ --- A request send from the client to the server to execute a command. The request might return
--- a workspace edit which the client will apply to the workspace.
workspace_executeCommand = 'workspace/executeCommand',
--- @since 3.18.0
@@ -1248,14 +959,5 @@ protocol.Methods = {
--- The `workspace/workspaceFolders` is sent from the server to the client to fetch the open workspace folders.
workspace_workspaceFolders = 'workspace/workspaceFolders',
}
-local function freeze(t)
- return setmetatable({}, {
- __index = t,
- __newindex = function()
- error('cannot modify immutable table')
- end,
- })
-end
-protocol.Methods = freeze(protocol.Methods)
return protocol
diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua
index 3c63a12da2..e79dbd2db3 100644
--- a/runtime/lua/vim/lsp/rpc.lua
+++ b/runtime/lua/vim/lsp/rpc.lua
@@ -3,7 +3,7 @@ local log = require('vim.lsp.log')
local protocol = require('vim.lsp.protocol')
local validate, schedule, schedule_wrap = vim.validate, vim.schedule, vim.schedule_wrap
-local is_win = uv.os_uname().version:find('Windows')
+local is_win = vim.fn.has('win32') == 1
--- Checks whether a given path exists and is a directory.
---@param filename string path to check
@@ -140,7 +140,7 @@ local client_errors = {
SERVER_RESULT_CALLBACK_ERROR = 7,
}
---- @type table<string|integer, string|integer>
+--- @type table<string,integer> | table<integer,string>
--- @nodoc
M.client_errors = vim.deepcopy(client_errors)
for k, v in pairs(client_errors) do
@@ -407,7 +407,9 @@ function Client:handle_body(body)
end
log.debug('rpc.receive', decoded)
- if type(decoded.method) == 'string' and decoded.id then
+ if type(decoded) ~= 'table' then
+ self:on_error(M.client_errors.INVALID_SERVER_MESSAGE, decoded)
+ elseif type(decoded.method) == 'string' and decoded.id then
local err --- @type lsp.ResponseError|nil
-- Schedule here so that the users functions don't trigger an error and
-- we can still use the result.
@@ -502,7 +504,7 @@ function Client:handle_body(body)
if decoded.error then
decoded.error = setmetatable(decoded.error, {
__tostring = M.format_rpc_error,
- }) --- @type table
+ })
end
self:try_call(
M.client_errors.SERVER_RESULT_CALLBACK_ERROR,
@@ -548,7 +550,7 @@ local function new_client(dispatchers, transport)
end
---@class vim.lsp.rpc.PublicClient
----@field request fun(method: string, params: table?, callback: fun(err: lsp.ResponseError|nil, result: any), notify_reply_callback: fun(integer)|nil):boolean,integer? see |vim.lsp.rpc.request()|
+---@field request fun(method: string, params: table?, callback: fun(err: lsp.ResponseError|nil, result: any), notify_reply_callback: fun(message_id: integer)|nil):boolean,integer? see |vim.lsp.rpc.request()|
---@field notify fun(method: string, params: any):boolean see |vim.lsp.rpc.notify()|
---@field is_closing fun(): boolean
---@field terminate fun()
@@ -701,7 +703,9 @@ function M.connect(host_or_path, port)
if port == nil then
handle:connect(host_or_path, on_connect)
else
- handle:connect(host_or_path, port, on_connect)
+ local info = uv.getaddrinfo(host_or_path, nil)
+ local resolved_host = info and info[1] and info[1].addr or host_or_path
+ handle:connect(resolved_host, port, on_connect)
end
return public_client(client)
diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua
index ef2502b12e..8182457dd0 100644
--- a/runtime/lua/vim/lsp/semantic_tokens.lua
+++ b/runtime/lua/vim/lsp/semantic_tokens.lua
@@ -140,12 +140,7 @@ local function tokens_to_ranges(data, bufnr, client, request)
local function _get_byte_pos(col)
if col > 0 then
local buf_line = lines[line + 1] or ''
- local ok, result
- ok, result = pcall(util._str_byteindex_enc, buf_line, col, client.offset_encoding)
- if ok then
- return result
- end
- return math.min(#buf_line, col)
+ return util._str_byteindex_enc(buf_line, col, client.offset_encoding)
end
return col
end
@@ -197,12 +192,6 @@ function STHighlighter.new(bufnr)
highlighter:send_request()
end
end,
- on_detach = function(_, buf)
- local highlighter = STHighlighter.active[buf]
- if highlighter then
- highlighter:destroy()
- end
- end,
})
api.nvim_create_autocmd({ 'BufWinEnter', 'InsertLeave' }, {
@@ -418,7 +407,7 @@ end
function STHighlighter:on_win(topline, botline)
for client_id, state in pairs(self.client_state) do
local current_result = state.current_result
- if current_result.version and current_result.version == util.buf_versions[self.bufnr] then
+ if current_result.version == util.buf_versions[self.bufnr] then
if not current_result.namespace_cleared then
api.nvim_buf_clear_namespace(self.bufnr, state.namespace, 0, -1)
current_result.namespace_cleared = true
@@ -779,7 +768,6 @@ function M.highlight_token(token, bufnr, client_id, hl_group, opts)
})
end
---- @package
--- |lsp-handler| for the method `workspace/semanticTokens/refresh`
---
--- Refresh requests are sent by the server to indicate a project-wide change
diff --git a/runtime/lua/vim/lsp/sync.lua b/runtime/lua/vim/lsp/sync.lua
index 936579e003..bdfe8d51b8 100644
--- a/runtime/lua/vim/lsp/sync.lua
+++ b/runtime/lua/vim/lsp/sync.lua
@@ -212,7 +212,8 @@ end
---@param lastline integer
---@param new_lastline integer
---@param offset_encoding string
----@return vim.lsp.sync.Range, vim.lsp.sync.Range
+---@return vim.lsp.sync.Range prev_end_range
+---@return vim.lsp.sync.Range curr_end_range
local function compute_end_range(
prev_lines,
curr_lines,
@@ -222,6 +223,16 @@ local function compute_end_range(
new_lastline,
offset_encoding
)
+ -- A special case for the following `firstline == new_lastline` case where lines are deleted.
+ -- Even if the buffer has become empty, nvim behaves as if it has an empty line with eol.
+ if #curr_lines == 1 and curr_lines[1] == '' then
+ local prev_line = prev_lines[lastline - 1]
+ return {
+ line_idx = lastline - 1,
+ byte_idx = #prev_line + 1,
+ char_idx = compute_line_length(prev_line, offset_encoding) + 1,
+ }, { line_idx = 1, byte_idx = 1, char_idx = 1 }
+ end
-- If firstline == new_lastline, the first change occurred on a line that was deleted.
-- In this case, the last_byte...
if firstline == new_lastline then
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 5a229a1169..882ec22ca6 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -99,9 +99,26 @@ local function get_border_size(opts)
return { height = height, width = width }
end
-local function split_lines(value)
- value = string.gsub(value, '\r\n?', '\n')
- return split(value, '\n', { plain = true, trimempty = true })
+--- Splits string at newlines, optionally removing unwanted blank lines.
+---
+--- @param s string Multiline string
+--- @param no_blank boolean? Drop blank lines for each @param/@return (except one empty line
+--- separating each). Workaround for https://github.com/LuaLS/lua-language-server/issues/2333
+local function split_lines(s, no_blank)
+ s = string.gsub(s, '\r\n?', '\n')
+ local lines = {}
+ local in_desc = true -- Main description block, before seeing any @foo.
+ for line in vim.gsplit(s, '\n', { plain = true, trimempty = true }) do
+ local start_annotation = not not line:find('^ ?%@.?[pr]')
+ in_desc = (not start_annotation) and in_desc or false
+ if start_annotation and no_blank and not (lines[#lines] or ''):find('^%s*$') then
+ table.insert(lines, '') -- Separate each @foo with a blank line.
+ end
+ if in_desc or not no_blank or not line:find('^%s*$') then
+ table.insert(lines, line)
+ end
+ end
+ return lines
end
local function create_window_without_focus()
@@ -116,9 +133,10 @@ end
--- Convenience wrapper around vim.str_utfindex
---@param line string line to be indexed
---@param index integer|nil byte index (utf-8), or `nil` for length
----@param encoding string|nil utf-8|utf-16|utf-32|nil defaults to utf-16
+---@param encoding 'utf-8'|'utf-16'|'utf-32'|nil defaults to utf-16
---@return integer `encoding` index of `index` in `line`
function M._str_utfindex_enc(line, index, encoding)
+ local len32, len16 = vim.str_utfindex(line)
if not encoding then
encoding = 'utf-16'
end
@@ -129,9 +147,15 @@ function M._str_utfindex_enc(line, index, encoding)
return #line
end
elseif encoding == 'utf-16' then
+ if not index or index > len16 then
+ return len16
+ end
local _, col16 = vim.str_utfindex(line, index)
return col16
elseif encoding == 'utf-32' then
+ if not index or index > len32 then
+ return len32
+ end
local col32, _ = vim.str_utfindex(line, index)
return col32
else
@@ -147,6 +171,12 @@ end
---@param encoding string utf-8|utf-16|utf-32| defaults to utf-16
---@return integer byte (utf-8) index of `encoding` index `index` in `line`
function M._str_byteindex_enc(line, index, encoding)
+ local len = #line
+ if index > len then
+ -- LSP spec: if character > line length, default to the line length.
+ -- https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#position
+ return len
+ end
if not encoding then
encoding = 'utf-16'
end
@@ -154,7 +184,7 @@ function M._str_byteindex_enc(line, index, encoding)
if index then
return index
else
- return #line
+ return len
end
elseif encoding == 'utf-16' then
return vim.str_byteindex(line, index, true)
@@ -165,19 +195,16 @@ function M._str_byteindex_enc(line, index, encoding)
end
end
-local _str_utfindex_enc = M._str_utfindex_enc
-local _str_byteindex_enc = M._str_byteindex_enc
-
--- Replaces text in a range with new text.
---
--- CAUTION: Changes in-place!
---
---@deprecated
----@param lines (table) Original list of strings
----@param A (table) Start position; a 2-tuple of {line,col} numbers
----@param B (table) End position; a 2-tuple of {line,col} numbers
----@param new_lines (table) list of strings to replace the original
----@return table The modified {lines} object
+---@param lines string[] Original list of strings
+---@param A [integer, integer] Start position; a 2-tuple of {line,col} numbers
+---@param B [integer, integer] End position; a 2-tuple {line,col} numbers
+---@param new_lines string[] list of strings to replace the original
+---@return string[] The modified {lines} object
function M.set_lines(lines, A, B, new_lines)
vim.deprecate('vim.lsp.util.set_lines()', 'nil', '0.12')
-- 0-indexing to 1-indexing
@@ -238,6 +265,7 @@ end
---@param rows integer[] zero-indexed line numbers
---@return table<integer, string>|string a table mapping rows to lines
local function get_lines(bufnr, rows)
+ --- @type integer[]
rows = type(rows) == 'table' and rows or { rows }
-- This is needed for bufload and bufloaded
@@ -246,7 +274,7 @@ local function get_lines(bufnr, rows)
end
local function buf_lines()
- local lines = {}
+ local lines = {} --- @type table<integer,string>
for _, row in ipairs(rows) do
lines[row] = (api.nvim_buf_get_lines(bufnr, row, row + 1, false) or { '' })[1]
end
@@ -268,17 +296,20 @@ local function get_lines(bufnr, rows)
end
local filename = api.nvim_buf_get_name(bufnr)
+ if vim.fn.isdirectory(filename) ~= 0 then
+ return {}
+ end
-- get the data from the file
local fd = uv.fs_open(filename, 'r', 438)
if not fd then
return ''
end
- local stat = uv.fs_fstat(fd)
- local data = uv.fs_read(fd, stat.size, 0)
+ local stat = assert(uv.fs_fstat(fd))
+ local data = assert(uv.fs_read(fd, stat.size, 0))
uv.fs_close(fd)
- local lines = {} -- rows we need to retrieve
+ local lines = {} --- @type table<integer,true|string> rows we need to retrieve
local need = 0 -- keep track of how many unique rows we need
for _, row in pairs(rows) do
if not lines[row] then
@@ -307,7 +338,7 @@ local function get_lines(bufnr, rows)
lines[i] = ''
end
end
- return lines
+ return lines --[[@as table<integer,string>]]
end
--- Gets the zero-indexed line from the given buffer.
@@ -322,7 +353,8 @@ local function get_line(bufnr, row)
end
--- Position is a https://microsoft.github.io/language-server-protocol/specifications/specification-current/#position
----@param offset_encoding string|nil utf-8|utf-16|utf-32
+---@param position lsp.Position
+---@param offset_encoding? string utf-8|utf-16|utf-32
---@return integer
local function get_line_byte_from_position(bufnr, position, offset_encoding)
-- LSP's line and characters are 0-indexed
@@ -332,18 +364,13 @@ local function get_line_byte_from_position(bufnr, position, offset_encoding)
-- character
if col > 0 then
local line = get_line(bufnr, position.line) or ''
- local ok, result
- ok, result = pcall(_str_byteindex_enc, line, col, offset_encoding)
- if ok then
- return result
- end
- return math.min(#line, col)
+ return M._str_byteindex_enc(line, col, offset_encoding or 'utf-16')
end
return col
end
--- Applies a list of text edits to a buffer.
----@param text_edits table list of `TextEdit` objects
+---@param text_edits lsp.TextEdit[]
---@param bufnr integer Buffer id
---@param offset_encoding string utf-8|utf-16|utf-32
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textEdit
@@ -366,6 +393,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding)
-- Fix reversed range and indexing each text_edits
local index = 0
+ --- @param text_edit lsp.TextEdit
text_edits = vim.tbl_map(function(text_edit)
index = index + 1
text_edit._index = index
@@ -383,6 +411,9 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding)
end, text_edits)
-- Sort text_edits
+ ---@param a lsp.TextEdit | { _index: integer }
+ ---@param b lsp.TextEdit | { _index: integer }
+ ---@return boolean
table.sort(text_edits, function(a, b)
if a.range.start.line ~= b.range.start.line then
return a.range.start.line > b.range.start.line
@@ -390,13 +421,11 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding)
if a.range.start.character ~= b.range.start.character then
return a.range.start.character > b.range.start.character
end
- if a._index ~= b._index then
- return a._index > b._index
- end
+ return a._index > b._index
end)
-- save and restore local marks since they get deleted by nvim_buf_set_lines
- local marks = {}
+ local marks = {} --- @type table<string,[integer,integer]>
for _, m in pairs(vim.fn.getmarklist(bufnr)) do
if m.mark:match("^'[a-z]$") then
marks[m.mark:sub(2, 2)] = { m.pos[2], m.pos[3] - 1 } -- api-indexed
@@ -432,14 +461,15 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding)
e.end_col = last_line_len
has_eol_text_edit = true
else
- -- If the replacement is over the end of a line (i.e. e.end_col is out of bounds and the
+ -- If the replacement is over the end of a line (i.e. e.end_col is equal to the line length and the
-- replacement text ends with a newline We can likely assume that the replacement is assumed
-- to be meant to replace the newline with another newline and we need to make sure this
-- doesn't add an extra empty line. E.g. when the last line to be replaced contains a '\r'
-- in the file some servers (clangd on windows) will include that character in the line
-- while nvim_buf_set_text doesn't count it as part of the line.
if
- e.end_col > last_line_len
+ e.end_col >= last_line_len
+ and text_edit.range['end'].character > e.end_col
and #text_edit.newText > 0
and string.sub(text_edit.newText, -1) == '\n'
then
@@ -481,8 +511,8 @@ end
--- Applies a `TextDocumentEdit`, which is a list of changes to a single
--- document.
---
----@param text_document_edit table: a `TextDocumentEdit` object
----@param index integer: Optional index of the edit, if from a list of edits (or nil, if not from a list)
+---@param text_document_edit lsp.TextDocumentEdit
+---@param index? integer: Optional index of the edit, if from a list of edits (or nil, if not from a list)
---@param offset_encoding? string
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentEdit
function M.apply_text_document_edit(text_document_edit, index, offset_encoding)
@@ -509,7 +539,6 @@ function M.apply_text_document_edit(text_document_edit, index, offset_encoding)
and (
text_document.version
and text_document.version > 0
- and M.buf_versions[bufnr]
and M.buf_versions[bufnr] > text_document.version
)
then
@@ -534,6 +563,7 @@ local function path_under_prefix(path, prefix)
end
--- Get list of buffers whose filename matches the given path prefix (normalized full path)
+---@param prefix string
---@return integer[]
local function get_bufs_with_prefix(prefix)
prefix = path_components(prefix)
@@ -616,7 +646,7 @@ function M.rename(old_fname, new_fname, opts)
buf_rename[b] = { from = old_bname, to = new_bname }
end
- local newdir = assert(vim.fs.dirname(new_fname))
+ local newdir = vim.fs.dirname(new_fname)
vim.fn.mkdir(newdir, 'p')
local ok, err = os.rename(old_fname_full, new_fname)
@@ -625,7 +655,7 @@ function M.rename(old_fname, new_fname, opts)
local old_undofile = vim.fn.undofile(old_fname_full)
if uv.fs_stat(old_undofile) ~= nil then
local new_undofile = vim.fn.undofile(new_fname)
- vim.fn.mkdir(assert(vim.fs.dirname(new_undofile)), 'p')
+ vim.fn.mkdir(vim.fs.dirname(new_undofile), 'p')
os.rename(old_undofile, new_undofile)
end
@@ -633,7 +663,7 @@ function M.rename(old_fname, new_fname, opts)
-- Rename with :saveas. This does two things:
-- * Unset BF_WRITE_MASK, so that users don't get E13 when they do :write.
-- * Send didClose and didOpen via textDocument/didSave handler.
- api.nvim_buf_call(b, function()
+ vim._with({ buf = b }, function()
vim.cmd('keepalt saveas! ' .. vim.fn.fnameescape(rename.to))
end)
-- Delete the new buffer with the old name created by :saveas. nvim_buf_delete and
@@ -642,6 +672,7 @@ function M.rename(old_fname, new_fname, opts)
end
end
+--- @param change lsp.CreateFile
local function create_file(change)
local opts = change.options or {}
-- from spec: Overwrite wins over `ignoreIfExists`
@@ -656,29 +687,21 @@ local function create_file(change)
vim.fn.bufadd(fname)
end
+--- @param change lsp.DeleteFile
local function delete_file(change)
local opts = change.options or {}
local fname = vim.uri_to_fname(change.uri)
- local stat = uv.fs_stat(fname)
- if opts.ignoreIfNotExists and not stat then
- return
- end
- assert(stat, 'Cannot delete not existing file or folder ' .. fname)
- local flags
- if stat and stat.type == 'directory' then
- flags = opts.recursive and 'rf' or 'd'
- else
- flags = ''
- end
local bufnr = vim.fn.bufadd(fname)
- local result = tonumber(vim.fn.delete(fname, flags))
- assert(result == 0, 'Could not delete file: ' .. fname .. ', stat: ' .. vim.inspect(stat))
+ vim.fs.rm(fname, {
+ force = opts.ignoreIfNotExists,
+ recursive = opts.recursive,
+ })
api.nvim_buf_delete(bufnr, { force = true })
end
--- Applies a `WorkspaceEdit`.
---
----@param workspace_edit table `WorkspaceEdit`
+---@param workspace_edit lsp.WorkspaceEdit
---@param offset_encoding string utf-8|utf-16|utf-32 (required)
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit
function M.apply_workspace_edit(workspace_edit, offset_encoding)
@@ -724,21 +747,21 @@ end
--- Note that if the input is of type `MarkupContent` and its kind is `plaintext`,
--- then the corresponding value is returned without further modifications.
---
----@param input (lsp.MarkedString | lsp.MarkedString[] | lsp.MarkupContent)
----@param contents (table|nil) List of strings to extend with converted lines. Defaults to {}.
+---@param input lsp.MarkedString|lsp.MarkedString[]|lsp.MarkupContent
+---@param contents string[]|nil List of strings to extend with converted lines. Defaults to {}.
---@return string[] extended with lines of converted markdown.
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover
function M.convert_input_to_markdown_lines(input, contents)
contents = contents or {}
-- MarkedString variation 1
if type(input) == 'string' then
- list_extend(contents, split_lines(input))
+ list_extend(contents, split_lines(input, true))
else
assert(type(input) == 'table', 'Expected a table for LSP input')
-- MarkupContent
if input.kind then
local value = input.value or ''
- list_extend(contents, split_lines(value))
+ list_extend(contents, split_lines(value, true))
-- MarkupString variation 2
elseif input.language then
table.insert(contents, '```' .. input.language)
@@ -760,11 +783,11 @@ end
--- Converts `textDocument/signatureHelp` response to markdown lines.
---
----@param signature_help table Response of `textDocument/SignatureHelp`
+---@param signature_help lsp.SignatureHelp Response of `textDocument/SignatureHelp`
---@param ft string|nil filetype that will be use as the `lang` for the label markdown code block
---@param triggers table|nil list of trigger characters from the lsp server. used to better determine parameter offsets
----@return table|nil table list of lines of converted markdown.
----@return table|nil table of active hl
+---@return string[]|nil table list of lines of converted markdown.
+---@return number[]|nil table of active hl
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp
function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers)
if not signature_help.signatures then
@@ -961,7 +984,7 @@ end
--- Shows document and optionally jumps to the location.
---
----@param location table (`Location`|`LocationLink`)
+---@param location lsp.Location|lsp.LocationLink
---@param offset_encoding string|nil utf-8|utf-16|utf-32
---@param opts table|nil options
--- - reuse_win (boolean) Jump to existing window if buffer is already open.
@@ -1007,7 +1030,7 @@ function M.show_document(location, offset_encoding, opts)
local row = range.start.line
local col = get_line_byte_from_position(bufnr, range.start, offset_encoding)
api.nvim_win_set_cursor(win, { row + 1, col })
- api.nvim_win_call(win, function()
+ vim._with({ win = win }, function()
-- Open folds under the cursor
vim.cmd('normal! zv')
end)
@@ -1018,7 +1041,7 @@ end
--- Jumps to a location.
---
----@param location table (`Location`|`LocationLink`)
+---@param location lsp.Location|lsp.LocationLink
---@param offset_encoding string|nil utf-8|utf-16|utf-32
---@param reuse_win boolean|nil Jump to existing window if buffer is already open.
---@return boolean `true` if the jump succeeded
@@ -1039,7 +1062,7 @@ end
--- - for Location, range is shown (e.g., function definition)
--- - for LocationLink, targetRange is shown (e.g., body of function definition)
---
----@param location table a single `Location` or `LocationLink`
+---@param location lsp.Location|lsp.LocationLink
---@param opts table
---@return integer|nil buffer id of float window
---@return integer|nil window id of float window
@@ -1155,7 +1178,7 @@ end
--- If you want to open a popup with fancy markdown, use `open_floating_preview` instead
---
---@param bufnr integer
----@param contents table of lines to show in window
+---@param contents string[] of lines to show in window
---@param opts table with optional fields
--- - height of floating window
--- - width of floating window
@@ -1327,7 +1350,7 @@ function M.stylize_markdown(bufnr, contents, opts)
end
-- needs to run in the buffer for the regions to work
- api.nvim_buf_call(bufnr, function()
+ vim._with({ buf = bufnr }, function()
-- we need to apply lsp_markdown regions speperately, since otherwise
-- markdown regions can "bleed" through the other syntax regions
-- and mess up the formatting
@@ -1438,7 +1461,7 @@ end
--- Computes size of float needed to show contents (with optional wrapping)
---
---@param contents table of lines to show in window
----@param opts table with optional fields
+---@param opts? table with optional fields
--- - height of floating window
--- - width of floating window
--- - wrap_at character to wrap at for computing height
@@ -1630,10 +1653,9 @@ function M.open_floating_preview(contents, syntax, opts)
if do_stylize then
vim.wo[floating_winnr].conceallevel = 2
end
- -- disable folding
- vim.wo[floating_winnr].foldenable = false
- -- soft wrapping
- vim.wo[floating_winnr].wrap = opts.wrap
+ vim.wo[floating_winnr].foldenable = false -- Disable folding.
+ vim.wo[floating_winnr].wrap = opts.wrap -- Soft wrapping.
+ vim.wo[floating_winnr].breakindent = true -- Slightly better list presentation.
vim.bo[floating_bufnr].modifiable = false
vim.bo[floating_bufnr].bufhidden = 'wipe'
@@ -1670,7 +1692,7 @@ do --[[ References ]]
--- Shows a list of document highlights for a certain buffer.
---
---@param bufnr integer Buffer id
- ---@param references table List of `DocumentHighlight` objects to highlight
+ ---@param references lsp.DocumentHighlight[] objects to highlight
---@param offset_encoding string One of "utf-8", "utf-16", "utf-32".
---@see https://microsoft.github.io/language-server-protocol/specification/#textDocumentContentChangeEvent
function M.buf_highlight_references(bufnr, references, offset_encoding)
@@ -1721,7 +1743,9 @@ end)
---@inlinedoc
---@field filename string
---@field lnum integer 1-indexed line number
+---@field end_lnum integer 1-indexed end line number
---@field col integer 1-indexed column
+---@field end_col integer 1-indexed end column
---@field text string
---@field user_data lsp.Location|lsp.LocationLink
@@ -1748,7 +1772,7 @@ function M.locations_to_items(locations, offset_encoding)
end
local items = {}
- ---@type table<string, {start: lsp.Position, location: lsp.Location|lsp.LocationLink}[]>
+ ---@type table<string, {start: lsp.Position, end: lsp.Position, location: lsp.Location|lsp.LocationLink}[]>
local grouped = setmetatable({}, {
__index = function(t, k)
local v = {}
@@ -1760,7 +1784,7 @@ function M.locations_to_items(locations, offset_encoding)
-- locations may be Location or LocationLink
local uri = d.uri or d.targetUri
local range = d.range or d.targetSelectionRange
- table.insert(grouped[uri], { start = range.start, location = d })
+ table.insert(grouped[uri], { start = range.start, ['end'] = range['end'], location = d })
end
---@type string[]
@@ -1775,6 +1799,9 @@ function M.locations_to_items(locations, offset_encoding)
local line_numbers = {}
for _, temp in ipairs(rows) do
table.insert(line_numbers, temp.start.line)
+ if temp.start.line ~= temp['end'].line then
+ table.insert(line_numbers, temp['end'].line)
+ end
end
-- get all the lines for this uri
@@ -1782,13 +1809,20 @@ function M.locations_to_items(locations, offset_encoding)
for _, temp in ipairs(rows) do
local pos = temp.start
+ local end_pos = temp['end']
local row = pos.line
+ local end_row = end_pos.line
local line = lines[row] or ''
+ local end_line = lines[end_row] or ''
local col = M._str_byteindex_enc(line, pos.character, offset_encoding)
+ local end_col = M._str_byteindex_enc(end_line, end_pos.character, offset_encoding)
+
table.insert(items, {
filename = filename,
lnum = row + 1,
+ end_lnum = end_row + 1,
col = col + 1,
+ end_col = end_col + 1,
text = line,
user_data = temp.location,
})
@@ -1807,7 +1841,7 @@ end
--- Converts symbols to quickfix list items.
---
---@param symbols table DocumentSymbol[] or SymbolInformation[]
----@param bufnr integer
+---@param bufnr? integer
function M.symbols_to_items(symbols, bufnr)
local function _symbols_to_items(_symbols, _items, _bufnr)
for _, symbol in ipairs(_symbols) do
@@ -1874,7 +1908,7 @@ end
--- CAUTION: Modifies the input in-place!
---
---@deprecated
----@param lines table list of lines
+---@param lines string[] list of lines
---@return string filetype or "markdown" if it was unchanged.
function M.try_trim_markdown_code_blocks(lines)
vim.deprecate('vim.lsp.util.try_trim_markdown_code_blocks()', 'nil', '0.12')
@@ -1899,7 +1933,7 @@ function M.try_trim_markdown_code_blocks(lines)
end
---@param window integer|nil: window handle or 0 for current, defaults to current
----@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window`
+---@param offset_encoding? string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window`
local function make_position_param(window, offset_encoding)
window = window or 0
local buf = api.nvim_win_get_buf(window)
@@ -1911,7 +1945,7 @@ local function make_position_param(window, offset_encoding)
return { line = 0, character = 0 }
end
- col = _str_utfindex_enc(line, col, offset_encoding)
+ col = M._str_utfindex_enc(line, col, offset_encoding)
return { line = row, character = col }
end
@@ -1920,7 +1954,7 @@ end
---
---@param window integer|nil: window handle or 0 for current, defaults to current
---@param offset_encoding string|nil utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window`
----@return table `TextDocumentPositionParams` object
+---@return lsp.TextDocumentPositionParams
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams
function M.make_position_params(window, offset_encoding)
window = window or 0
@@ -1933,7 +1967,7 @@ function M.make_position_params(window, offset_encoding)
end
--- Utility function for getting the encoding of the first LSP client on the given buffer.
----@param bufnr (integer) buffer handle or 0 for current, defaults to current
+---@param bufnr integer buffer handle or 0 for current, defaults to current
---@return string encoding first client if there is one, nil otherwise
function M._get_offset_encoding(bufnr)
validate({
@@ -2034,15 +2068,16 @@ end
--- Creates a `TextDocumentIdentifier` object for the current buffer.
---
---@param bufnr integer|nil: Buffer handle, defaults to current
----@return table `TextDocumentIdentifier`
+---@return lsp.TextDocumentIdentifier
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentIdentifier
function M.make_text_document_params(bufnr)
return { uri = vim.uri_from_bufnr(bufnr or 0) }
end
--- Create the workspace params
----@param added table
----@param removed table
+---@param added lsp.WorkspaceFolder[]
+---@param removed lsp.WorkspaceFolder[]
+---@return lsp.WorkspaceFoldersChangeEvent
function M.make_workspace_params(added, removed)
return { event = { added = added, removed = removed } }
end
@@ -2050,8 +2085,8 @@ end
--- Returns indentation size.
---
---@see 'shiftwidth'
----@param bufnr (integer|nil): Buffer handle, defaults to current
----@return (integer) indentation size
+---@param bufnr integer|nil: Buffer handle, defaults to current
+---@return integer indentation size
function M.get_effective_tabstop(bufnr)
validate({ bufnr = { bufnr, 'n', true } })
local bo = bufnr and vim.bo[bufnr] or vim.bo
@@ -2061,7 +2096,7 @@ end
--- Creates a `DocumentFormattingParams` object for the current buffer and cursor position.
---
----@param options table|nil with valid `FormattingOptions` entries
+---@param options lsp.FormattingOptions|nil with valid `FormattingOptions` entries
---@return lsp.DocumentFormattingParams object
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting
function M.make_formatting_params(options)
@@ -2092,11 +2127,7 @@ function M.character_offset(buf, row, col, offset_encoding)
)
offset_encoding = vim.lsp.get_clients({ bufnr = buf })[1].offset_encoding
end
- -- If the col is past the EOL, use the line length.
- if col > #line then
- return _str_utfindex_enc(line, nil, offset_encoding)
- end
- return _str_utfindex_enc(line, col, offset_encoding)
+ return M._str_utfindex_enc(line, col, offset_encoding)
end
--- Helper function to return nested values in language server settings
@@ -2203,6 +2234,11 @@ end
M._get_line_byte_from_position = get_line_byte_from_position
---@nodoc
-M.buf_versions = {} ---@type table<integer,integer>
+---@type table<integer,integer>
+M.buf_versions = setmetatable({}, {
+ __index = function(t, bufnr)
+ return rawget(t, bufnr) or 0
+ end,
+})
return M
diff --git a/runtime/lua/vim/provider/health.lua b/runtime/lua/vim/provider/health.lua
index 63e0da448a..47c2080e3c 100644
--- a/runtime/lua/vim/provider/health.lua
+++ b/runtime/lua/vim/provider/health.lua
@@ -1,8 +1,114 @@
local health = vim.health
-local iswin = vim.uv.os_uname().sysname == 'Windows_NT'
+local iswin = vim.fn.has('win32') == 1
local M = {}
+local function cmd_ok(cmd)
+ local out = vim.fn.system(cmd)
+ return vim.v.shell_error == 0, out
+end
+
+-- Attempts to construct a shell command from an args list.
+-- Only for display, to help users debug a failed command.
+local function shellify(cmd)
+ if type(cmd) ~= 'table' then
+ return cmd
+ end
+ local escaped = {}
+ for i, v in ipairs(cmd) do
+ if v:match('[^A-Za-z_/.-]') then
+ escaped[i] = vim.fn.shellescape(v)
+ else
+ escaped[i] = v
+ end
+ end
+ return table.concat(escaped, ' ')
+end
+
+-- Handler for s:system() function.
+local function system_handler(self, _, data, event)
+ if event == 'stderr' then
+ if self.add_stderr_to_output then
+ self.output = self.output .. table.concat(data, '')
+ else
+ self.stderr = self.stderr .. table.concat(data, '')
+ end
+ elseif event == 'stdout' then
+ self.output = self.output .. table.concat(data, '')
+ end
+end
+
+--- @param cmd table List of command arguments to execute
+--- @param args? table Optional arguments:
+--- - stdin (string): Data to write to the job's stdin
+--- - stderr (boolean): Append stderr to stdout
+--- - ignore_error (boolean): If true, ignore error output
+--- - timeout (number): Number of seconds to wait before timing out (default 30)
+local function system(cmd, args)
+ args = args or {}
+ local stdin = args.stdin or ''
+ local stderr = vim.F.if_nil(args.stderr, false)
+ local ignore_error = vim.F.if_nil(args.ignore_error, false)
+
+ local shell_error_code = 0
+ local opts = {
+ add_stderr_to_output = stderr,
+ output = '',
+ stderr = '',
+ on_stdout = system_handler,
+ on_stderr = system_handler,
+ on_exit = function(_, data)
+ shell_error_code = data
+ end,
+ }
+ local jobid = vim.fn.jobstart(cmd, opts)
+
+ if jobid < 1 then
+ local message =
+ string.format('Command error (job=%d): %s (in %s)', jobid, shellify(cmd), vim.uv.cwd())
+ error(message)
+ return opts.output, 1
+ end
+
+ if stdin:find('^%s$') then
+ vim.fn.chansend(jobid, stdin)
+ end
+
+ local res = vim.fn.jobwait({ jobid }, vim.F.if_nil(args.timeout, 30) * 1000)
+ if res[1] == -1 then
+ error('Command timed out: ' .. shellify(cmd))
+ vim.fn.jobstop(jobid)
+ elseif shell_error_code ~= 0 and not ignore_error then
+ local emsg = string.format(
+ 'Command error (job=%d, exit code %d): %s (in %s)',
+ jobid,
+ shell_error_code,
+ shellify(cmd),
+ vim.uv.cwd()
+ )
+ if opts.output:find('%S') then
+ emsg = string.format('%s\noutput: %s', emsg, opts.output)
+ end
+ if opts.stderr:find('%S') then
+ emsg = string.format('%s\nstderr: %s', emsg, opts.stderr)
+ end
+ error(emsg)
+ end
+
+ return vim.trim(vim.fn.system(cmd)), shell_error_code
+end
+
+---@param provider string
+local function provider_disabled(provider)
+ local loaded_var = 'loaded_' .. provider .. '_provider'
+ local v = vim.g[loaded_var]
+ if v == 0 then
+ health.info('Disabled (' .. loaded_var .. '=' .. v .. ').')
+ return true
+ end
+ return false
+end
+
local function clipboard()
health.start('Clipboard (optional)')
@@ -10,7 +116,7 @@ local function clipboard()
os.getenv('TMUX')
and vim.fn.executable('tmux') == 1
and vim.fn.executable('pbpaste') == 1
- and not health._cmd_ok('pbpaste')
+ and not cmd_ok('pbpaste')
then
local tmux_version = string.match(vim.fn.system('tmux -V'), '%d+%.%d+')
local advice = {
@@ -20,9 +126,9 @@ local function clipboard()
health.error('pbcopy does not work with tmux version: ' .. tmux_version, advice)
end
- local clipboard_tool = vim.fn['provider#clipboard#Executable']()
+ local clipboard_tool = vim.fn['provider#clipboard#Executable']() ---@type string
if vim.g.clipboard ~= nil and clipboard_tool == '' then
- local error_message = vim.fn['provider#clipboard#Error']()
+ local error_message = vim.fn['provider#clipboard#Error']() ---@type string
health.error(
error_message,
"Use the example in :help g:clipboard as a template, or don't set g:clipboard at all."
@@ -40,7 +146,7 @@ end
local function node()
health.start('Node.js provider (optional)')
- if health._provider_disabled('node') then
+ if provider_disabled('node') then
return
end
@@ -60,7 +166,7 @@ local function node()
end
-- local node_v = vim.fn.split(system({'node', '-v'}), "\n")[1] or ''
- local ok, node_v = health._cmd_ok({ 'node', '-v' })
+ local ok, node_v = cmd_ok({ 'node', '-v' })
health.info('Node.js: ' .. node_v)
if not ok or vim.version.lt(node_v, '6.0.0') then
health.warn('Nvim node.js host does not support Node ' .. node_v)
@@ -73,7 +179,7 @@ local function node()
)
end
- local node_detect_table = vim.fn['provider#node#Detect']()
+ local node_detect_table = vim.fn['provider#node#Detect']() ---@type string[]
local host = node_detect_table[1]
if host:find('^%s*$') then
health.warn('Missing "neovim" npm (or yarn, pnpm) package.', {
@@ -97,7 +203,7 @@ local function node()
iswin and 'cmd /c ' .. manager .. ' info neovim --json' or manager .. ' info neovim --json'
)
local latest_npm
- ok, latest_npm = health._cmd_ok(vim.split(latest_npm_cmd, ' '))
+ ok, latest_npm = cmd_ok(vim.split(latest_npm_cmd, ' '))
if not ok or latest_npm:find('^%s$') then
health.error(
'Failed to run: ' .. latest_npm_cmd,
@@ -115,7 +221,7 @@ local function node()
local current_npm_cmd = { 'node', host, '--version' }
local current_npm
- ok, current_npm = health._cmd_ok(current_npm_cmd)
+ ok, current_npm = cmd_ok(current_npm_cmd)
if not ok then
health.error(
'Failed to run: ' .. table.concat(current_npm_cmd, ' '),
@@ -143,7 +249,7 @@ end
local function perl()
health.start('Perl provider (optional)')
- if health._provider_disabled('perl') then
+ if provider_disabled('perl') then
return
end
@@ -162,7 +268,7 @@ local function perl()
-- we cannot use cpanm that is on the path, as it may not be for the perl
-- set with g:perl_host_prog
- local ok = health._cmd_ok({ perl_exec, '-W', '-MApp::cpanminus', '-e', '' })
+ local ok = cmd_ok({ perl_exec, '-W', '-MApp::cpanminus', '-e', '' })
if not ok then
return { perl_exec, '"App::cpanminus" module is not installed' }
end
@@ -174,7 +280,7 @@ local function perl()
'my $app = App::cpanminus::script->new; $app->parse_options ("--info", "-q", "Neovim::Ext"); exit $app->doit',
}
local latest_cpan
- ok, latest_cpan = health._cmd_ok(latest_cpan_cmd)
+ ok, latest_cpan = cmd_ok(latest_cpan_cmd)
if not ok or latest_cpan:find('^%s*$') then
health.error(
'Failed to run: ' .. table.concat(latest_cpan_cmd, ' '),
@@ -184,7 +290,7 @@ local function perl()
elseif latest_cpan[1] == '!' then
local cpanm_errs = vim.split(latest_cpan, '!')
if cpanm_errs[1]:find("Can't write to ") then
- local advice = {}
+ local advice = {} ---@type string[]
for i = 2, #cpanm_errs do
advice[#advice + 1] = cpanm_errs[i]
end
@@ -197,7 +303,7 @@ local function perl()
return
end
end
- latest_cpan = vim.fn.matchstr(latest_cpan, [[\(\.\?\d\)\+]])
+ latest_cpan = tostring(vim.fn.matchstr(latest_cpan, [[\(\.\?\d\)\+]]))
if latest_cpan:find('^%s*$') then
health.error('Cannot parse version number from cpanm output: ' .. latest_cpan)
return
@@ -205,7 +311,7 @@ local function perl()
local current_cpan_cmd = { perl_exec, '-W', '-MNeovim::Ext', '-e', 'print $Neovim::Ext::VERSION' }
local current_cpan
- ok, current_cpan = health._cmd_ok(current_cpan_cmd)
+ ok, current_cpan = cmd_ok(current_cpan_cmd)
if not ok then
health.error(
'Failed to run: ' .. table.concat(current_cpan_cmd, ' '),
@@ -243,9 +349,11 @@ local function python_exepath(invocation)
return vim.fs.normalize(vim.trim(p.stdout))
end
--- Check if pyenv is available and a valid pyenv root can be found, then return
--- their respective paths. If either of those is invalid, return two empty
--- strings, effectively ignoring pyenv.
+--- Check if pyenv is available and a valid pyenv root can be found, then return
+--- their respective paths. If either of those is invalid, return two empty
+--- strings, effectively ignoring pyenv.
+---
+--- @return [string, string]
local function check_for_pyenv()
local pyenv_path = vim.fn.resolve(vim.fn.exepath('pyenv'))
@@ -258,7 +366,17 @@ local function check_for_pyenv()
local pyenv_root = vim.fn.resolve(os.getenv('PYENV_ROOT') or '')
if pyenv_root == '' then
- pyenv_root = vim.fn.system({ pyenv_path, 'root' })
+ local p = vim.system({ pyenv_path, 'root' }):wait()
+ if p.code ~= 0 then
+ local message = string.format(
+ 'pyenv: Failed to infer the root of pyenv by running `%s root` : %s. Ignoring pyenv for all following checks.',
+ pyenv_path,
+ p.stderr
+ )
+ health.warn(message)
+ return { '', '' }
+ end
+ pyenv_root = vim.trim(p.stdout)
health.info('pyenv: $PYENV_ROOT is not set. Infer from `pyenv root`.')
end
@@ -288,24 +406,29 @@ local function check_bin(bin)
return true
end
--- Fetch the contents of a URL.
+--- Fetch the contents of a URL.
+---
+--- @param url string
local function download(url)
local has_curl = vim.fn.executable('curl') == 1
if has_curl and vim.fn.system({ 'curl', '-V' }):find('Protocols:.*https') then
- local out, rc = health._system({ 'curl', '-sL', url }, { stderr = true, ignore_error = true })
+ local out, rc = system({ 'curl', '-sL', url }, { stderr = true, ignore_error = true })
if rc ~= 0 then
return 'curl error with ' .. url .. ': ' .. rc
else
return out
end
elseif vim.fn.executable('python') == 1 then
- local script = "try:\n\
- from urllib.request import urlopen\n\
- except ImportError:\n\
- from urllib2 import urlopen\n\
- response = urlopen('" .. url .. "')\n\
- print(response.read().decode('utf8'))\n"
- local out, rc = health._system({ 'python', '-c', script })
+ local script = ([[
+try:
+ from urllib.request import urlopen
+except ImportError:
+ from urllib2 import urlopen
+
+response = urlopen('%s')
+print(response.read().decode('utf8'))
+]]):format(url)
+ local out, rc = system({ 'python', '-c', script })
if out == '' and rc ~= 0 then
return 'python urllib.request error: ' .. rc
else
@@ -323,25 +446,24 @@ local function download(url)
return message
end
--- Get the latest Nvim Python client (pynvim) version from PyPI.
+--- Get the latest Nvim Python client (pynvim) version from PyPI.
local function latest_pypi_version()
local pypi_version = 'unable to get pypi response'
local pypi_response = download('https://pypi.python.org/pypi/pynvim/json')
if pypi_response ~= '' then
local pcall_ok, output = pcall(vim.fn.json_decode, pypi_response)
- local pypi_data
- if pcall_ok then
- pypi_data = output
- else
+ if not pcall_ok then
return 'error: ' .. pypi_response
end
+ local pypi_data = output
local pypi_element = pypi_data['info'] or {}
pypi_version = pypi_element['version'] or 'unable to parse'
end
return pypi_version
end
+--- @param s string
local function is_bad_response(s)
local lower = s:lower()
return vim.startswith(lower, 'unable')
@@ -349,20 +471,22 @@ local function is_bad_response(s)
or vim.startswith(lower, 'outdated')
end
--- Get version information using the specified interpreter. The interpreter is
--- used directly in case breaking changes were introduced since the last time
--- Nvim's Python client was updated.
---
--- Returns: {
--- {python executable version},
--- {current nvim version},
--- {current pypi nvim status},
--- {installed version status}
--- }
+--- Get version information using the specified interpreter. The interpreter is
+--- used directly in case breaking changes were introduced since the last time
+--- Nvim's Python client was updated.
+---
+--- @param python string
+---
+--- Returns: {
+--- {python executable version},
+--- {current nvim version},
+--- {current pypi nvim status},
+--- {installed version status}
+--- }
local function version_info(python)
local pypi_version = latest_pypi_version()
- local python_version, rc = health._system({
+ local python_version, rc = system({
python,
'-c',
'import sys; print(".".join(str(x) for x in sys.version_info[:3]))',
@@ -373,7 +497,7 @@ local function version_info(python)
end
local nvim_path
- nvim_path, rc = health._system({
+ nvim_path, rc = system({
python,
'-c',
'import sys; sys.path = [p for p in sys.path if p != ""]; import neovim; print(neovim.__file__)',
@@ -398,7 +522,7 @@ local function version_info(python)
-- Try to get neovim.VERSION (added in 0.1.11dev).
local nvim_version
- nvim_version, rc = health._system({
+ nvim_version, rc = system({
python,
'-c',
'from neovim import VERSION as v; print("{}.{}.{}{}".format(v.major, v.minor, v.patch, v.prerelease))',
@@ -406,9 +530,9 @@ local function version_info(python)
if rc ~= 0 or nvim_version == '' then
nvim_version = 'unable to find pynvim module version'
local base = vim.fs.basename(nvim_path)
- local metas = vim.fn.glob(base .. '-*/METADATA', 1, 1)
- vim.list_extend(metas, vim.fn.glob(base .. '-*/PKG-INFO', 1, 1))
- vim.list_extend(metas, vim.fn.glob(base .. '.egg-info/PKG-INFO', 1, 1))
+ local metas = vim.fn.glob(base .. '-*/METADATA', true, 1)
+ vim.list_extend(metas, vim.fn.glob(base .. '-*/PKG-INFO', true, 1))
+ vim.list_extend(metas, vim.fn.glob(base .. '.egg-info/PKG-INFO', true, 1))
metas = table.sort(metas, compare)
if metas and next(metas) ~= nil then
@@ -438,14 +562,13 @@ end
local function python()
health.start('Python 3 provider (optional)')
- local pyname = 'python3' ---@type string?
local python_exe = ''
local virtual_env = os.getenv('VIRTUAL_ENV')
local venv = virtual_env and vim.fn.resolve(virtual_env) or ''
- local host_prog_var = pyname .. '_host_prog'
- local python_multiple = {}
+ local host_prog_var = 'python3_host_prog'
+ local python_multiple = {} ---@type string[]
- if health._provider_disabled(pyname) then
+ if provider_disabled('python3') then
return
end
@@ -458,8 +581,7 @@ local function python()
health.info(message)
end
- local pythonx_warnings
- pyname, pythonx_warnings = vim.provider.python.detect_by_module('neovim')
+ local pyname, pythonx_warnings = vim.provider.python.detect_by_module('neovim')
if not pyname then
health.warn(
@@ -487,7 +609,7 @@ local function python()
end
if pyenv ~= '' then
- python_exe = health._system({ pyenv, 'which', pyname }, { stderr = true })
+ python_exe = system({ pyenv, 'which', pyname }, { stderr = true })
if python_exe == '' then
health.warn('pyenv could not find ' .. pyname .. '.')
end
@@ -547,12 +669,7 @@ local function python()
)
health.warn('pyenv is not set up optimally.', advice)
elseif venv ~= '' then
- local venv_root
- if pyenv_root ~= '' then
- venv_root = pyenv_root
- else
- venv_root = vim.fs.dirname(venv)
- end
+ local venv_root = pyenv_root ~= '' and pyenv_root or vim.fs.dirname(venv)
if vim.startswith(vim.fn.resolve(python_exe), venv_root .. '/') then
local advice = string.format(
@@ -637,9 +754,9 @@ local function python()
health.ok('no $VIRTUAL_ENV')
return
end
- local errors = {}
+ local errors = {} ---@type string[]
-- Keep hints as dict keys in order to discard duplicates.
- local hints = {}
+ local hints = {} ---@type table<string, boolean>
-- The virtualenv should contain some Python executables, and those
-- executables should be first both on Nvim's $PATH and the $PATH of
-- subshells launched from Nvim.
@@ -647,7 +764,7 @@ local function python()
local venv_bins = vim.fn.glob(string.format('%s/%s/python*', virtual_env, bin_dir), true, true)
venv_bins = vim.tbl_filter(function(v)
-- XXX: Remove irrelevant executables found in bin/.
- return not v:match('python%-config')
+ return not v:match('python.*%-config')
end, venv_bins)
if vim.tbl_count(venv_bins) > 0 then
for _, venv_bin in pairs(venv_bins) do
@@ -710,9 +827,7 @@ local function python()
health.info(msg)
health.info(
'Python version: '
- .. health._system(
- 'python -c "import platform, sys; sys.stdout.write(platform.python_version())"'
- )
+ .. system('python -c "import platform, sys; sys.stdout.write(platform.python_version())"')
)
health.ok('$VIRTUAL_ENV provides :!python.')
end
@@ -721,7 +836,7 @@ end
local function ruby()
health.start('Ruby provider (optional)')
- if health._provider_disabled('ruby') then
+ if provider_disabled('ruby') then
return
end
@@ -732,7 +847,7 @@ local function ruby()
)
return
end
- health.info('Ruby: ' .. health._system({ 'ruby', '-v' }))
+ health.info('Ruby: ' .. system({ 'ruby', '-v' }))
local host, _ = vim.provider.ruby.detect()
if (not host) or host:find('^%s*$') then
@@ -748,7 +863,7 @@ local function ruby()
health.info('Host: ' .. host)
local latest_gem_cmd = (iswin and 'cmd /c gem list -ra "^^neovim$"' or 'gem list -ra ^neovim$')
- local ok, latest_gem = health._cmd_ok(vim.split(latest_gem_cmd, ' '))
+ local ok, latest_gem = cmd_ok(vim.split(latest_gem_cmd, ' '))
if not ok or latest_gem:find('^%s*$') then
health.error(
'Failed to run: ' .. latest_gem_cmd,
@@ -761,7 +876,7 @@ local function ruby()
local current_gem_cmd = { host, '--version' }
local current_gem
- ok, current_gem = health._cmd_ok(current_gem_cmd)
+ ok, current_gem = cmd_ok(current_gem_cmd)
if not ok then
health.error(
'Failed to run: ' .. table.concat(current_gem_cmd, ' '),
diff --git a/runtime/lua/vim/secure.lua b/runtime/lua/vim/secure.lua
index 41a3d3ba25..266725cce2 100644
--- a/runtime/lua/vim/secure.lua
+++ b/runtime/lua/vim/secure.lua
@@ -41,6 +41,7 @@ end
--- trusted. The user's choice is persisted in a trust database at
--- $XDG_STATE_HOME/nvim/trust.
---
+---@since 11
---@see |:trust|
---
---@param path (string) Path to a file to read.
@@ -126,6 +127,7 @@ end
---
--- The trust database is located at |$XDG_STATE_HOME|/nvim/trust.
---
+---@since 11
---@param opts vim.trust.opts
---@return boolean success true if operation was successful
---@return string msg full path if operation was successful, else error message
diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua
index e9e4326057..4d06cdd77d 100644
--- a/runtime/lua/vim/shared.lua
+++ b/runtime/lua/vim/shared.lua
@@ -214,7 +214,7 @@ end
---@param t table<T, any> (table) Table
---@return T[] : List of keys
function vim.tbl_keys(t)
- vim.validate({ t = { t, 't' } })
+ vim.validate('t', t, 'table')
--- @cast t table<any,any>
local keys = {}
@@ -231,7 +231,7 @@ end
---@param t table<any, T> (table) Table
---@return T[] : List of values
function vim.tbl_values(t)
- vim.validate({ t = { t, 't' } })
+ vim.validate('t', t, 'table')
local values = {}
for _, v in
@@ -332,7 +332,7 @@ end
---@param value any Value to compare
---@return boolean `true` if `t` contains `value`
function vim.list_contains(t, value)
- vim.validate({ t = { t, 't' } })
+ vim.validate('t', t, 'table')
--- @cast t table<any,any>
for _, v in ipairs(t) do
@@ -350,41 +350,32 @@ end
---@param t table Table to check
---@return boolean `true` if `t` is empty
function vim.tbl_isempty(t)
- vim.validate({ t = { t, 't' } })
+ vim.validate('t', t, 'table')
return next(t) == nil
end
---- We only merge empty tables or tables that are not an array (indexed by integers)
+--- We only merge empty tables or tables that are not list-like (indexed by consecutive integers
+--- starting from 1)
local function can_merge(v)
- return type(v) == 'table' and (vim.tbl_isempty(v) or not vim.isarray(v))
+ return type(v) == 'table' and (vim.tbl_isempty(v) or not vim.islist(v))
end
-local function tbl_extend(behavior, deep_extend, ...)
- if behavior ~= 'error' and behavior ~= 'keep' and behavior ~= 'force' then
- error('invalid "behavior": ' .. tostring(behavior))
- end
-
- if select('#', ...) < 2 then
- error(
- 'wrong number of arguments (given '
- .. tostring(1 + select('#', ...))
- .. ', expected at least 3)'
- )
- end
-
+--- Recursive worker for tbl_extend
+--- @param behavior 'error'|'keep'|'force'
+--- @param deep_extend boolean
+--- @param ... table<any,any>
+local function tbl_extend_rec(behavior, deep_extend, ...)
local ret = {} --- @type table<any,any>
if vim._empty_dict_mt ~= nil and getmetatable(select(1, ...)) == vim._empty_dict_mt then
ret = vim.empty_dict()
end
for i = 1, select('#', ...) do
- local tbl = select(i, ...)
- vim.validate({ ['after the second argument'] = { tbl, 't' } })
- --- @cast tbl table<any,any>
+ local tbl = select(i, ...) --[[@as table<any,any>]]
if tbl then
for k, v in pairs(tbl) do
if deep_extend and can_merge(v) and can_merge(ret[k]) then
- ret[k] = tbl_extend(behavior, true, ret[k], v)
+ ret[k] = tbl_extend_rec(behavior, true, ret[k], v)
elseif behavior ~= 'force' and ret[k] ~= nil then
if behavior == 'error' then
error('key found in more than one map: ' .. k)
@@ -395,9 +386,31 @@ local function tbl_extend(behavior, deep_extend, ...)
end
end
end
+
return ret
end
+--- @param behavior 'error'|'keep'|'force'
+--- @param deep_extend boolean
+--- @param ... table<any,any>
+local function tbl_extend(behavior, deep_extend, ...)
+ if behavior ~= 'error' and behavior ~= 'keep' and behavior ~= 'force' then
+ error('invalid "behavior": ' .. tostring(behavior))
+ end
+
+ local nargs = select('#', ...)
+
+ if nargs < 2 then
+ error(('wrong number of arguments (given %d, expected at least 3)'):format(1 + nargs))
+ end
+
+ for i = 1, nargs do
+ vim.validate('after the second argument', select(i, ...), 'table')
+ end
+
+ return tbl_extend_rec(behavior, deep_extend, ...)
+end
+
--- Merges two or more tables.
---
---@see |extend()|
@@ -414,6 +427,11 @@ end
--- Merges recursively two or more tables.
---
+--- Only values that are empty tables or tables that are not |lua-list|s (indexed by consecutive
+--- integers starting from 1) are merged recursively. This is useful for merging nested tables
+--- like default and user configurations where lists should be treated as literals (i.e., are
+--- overwritten instead of merged).
+---
---@see |vim.tbl_extend()|
---
---@generic T1: table
@@ -580,7 +598,7 @@ end
---@return fun(table: table<K, V>, index?: K):K, V # |for-in| iterator over sorted keys and their values
---@return T
function vim.spairs(t)
- assert(type(t) == 'table', ('expected table, got %s'):format(type(t)))
+ vim.validate('t', t, 'table')
--- @cast t table<any,any>
-- collect the keys
@@ -691,7 +709,7 @@ end
---@param t table Table
---@return integer : Number of non-nil values in table
function vim.tbl_count(t)
- vim.validate({ t = { t, 't' } })
+ vim.validate('t', t, 'table')
--- @cast t table<any,any>
local count = 0
@@ -723,7 +741,7 @@ end
---@param s string String to trim
---@return string String with whitespace removed from its beginning and end
function vim.trim(s)
- vim.validate({ s = { s, 's' } })
+ vim.validate('s', s, 'string')
return s:match('^%s*(.*%S)') or ''
end
@@ -733,7 +751,7 @@ end
---@param s string String to escape
---@return string %-escaped pattern string
function vim.pesc(s)
- vim.validate({ s = { s, 's' } })
+ vim.validate('s', s, 'string')
return (s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1'))
end
@@ -743,7 +761,8 @@ end
---@param prefix string Prefix to match
---@return boolean `true` if `prefix` is a prefix of `s`
function vim.startswith(s, prefix)
- vim.validate({ s = { s, 's' }, prefix = { prefix, 's' } })
+ vim.validate('s', s, 'string')
+ vim.validate('prefix', prefix, 'string')
return s:sub(1, #prefix) == prefix
end
@@ -753,7 +772,8 @@ end
---@param suffix string Suffix to match
---@return boolean `true` if `suffix` is a suffix of `s`
function vim.endswith(s, suffix)
- vim.validate({ s = { s, 's' }, suffix = { suffix, 's' } })
+ vim.validate('s', s, 'string')
+ vim.validate('suffix', suffix, 'string')
return #suffix == 0 or s:sub(-#suffix) == suffix
end
@@ -787,7 +807,7 @@ do
}
--- @nodoc
- --- @class vim.validate.Spec {[1]: any, [2]: string|string[], [3]: boolean }
+ --- @class vim.validate.Spec [any, string|string[], boolean]
--- @field [1] any Argument value
--- @field [2] string|string[]|fun(v:any):boolean, string? Type name, or callable
--- @field [3]? boolean
@@ -877,8 +897,30 @@ do
return true
end
- --- Validates a parameter specification (types and values). Specs are evaluated in alphanumeric
- --- order, until the first failure.
+ --- Validate function arguments.
+ ---
+ --- This function has two valid forms:
+ ---
+ --- 1. vim.validate(name: str, value: any, type: string, optional?: bool)
+ --- 2. vim.validate(spec: table)
+ ---
+ --- Form 1 validates that argument {name} with value {value} has the type
+ --- {type}. {type} must be a value returned by |lua-type()|. If {optional} is
+ --- true, then {value} may be null. This form is significantly faster and
+ --- should be preferred for simple cases.
+ ---
+ --- Example:
+ ---
+ --- ```lua
+ --- function vim.startswith(s, prefix)
+ --- vim.validate('s', s, 'string')
+ --- vim.validate('prefix', prefix, 'string')
+ --- ...
+ --- end
+ --- ```
+ ---
+ --- Form 2 validates a parameter specification (types and values). Specs are
+ --- evaluated in alphanumeric order, until the first failure.
---
--- Usage example:
---
@@ -930,8 +972,32 @@ do
--- only if the argument is valid. Can optionally return an additional
--- informative error message as the second returned value.
--- - msg: (optional) error string if validation fails
- function vim.validate(opt)
- local ok, err_msg = is_valid(opt)
+ --- @overload fun(name: string, val: any, expected: string, optional?: boolean)
+ function vim.validate(opt, ...)
+ local ok = false
+ local err_msg ---@type string?
+ local narg = select('#', ...)
+ if narg == 0 then
+ ok, err_msg = is_valid(opt)
+ elseif narg >= 2 then
+ -- Overloaded signature for fast/simple cases
+ local name = opt --[[@as string]]
+ local v, expected, optional = ... ---@type string, string, boolean?
+ local actual = type(v)
+
+ ok = (actual == expected) or (v == nil and optional == true)
+ if not ok then
+ err_msg = ('%s: expected %s, got %s%s'):format(
+ name,
+ expected,
+ actual,
+ v and (' (%s)'):format(v) or ''
+ )
+ end
+ else
+ error('invalid arguments')
+ end
+
if not ok then
error(err_msg, 2)
end
@@ -949,7 +1015,7 @@ function vim.is_callable(f)
if m == nil then
return false
end
- return type(m.__call) == 'function'
+ return type(rawget(m, '__call')) == 'function'
end
--- Creates a table whose missing keys are provided by {createfn} (like Python's "defaultdict").
@@ -1091,4 +1157,165 @@ function vim._defer_require(root, mod)
})
end
+--- @nodoc
+--- @class vim.context.mods
+--- @field bo? table<string, any>
+--- @field buf? integer
+--- @field emsg_silent? boolean
+--- @field env? table<string, any>
+--- @field go? table<string, any>
+--- @field hide? boolean
+--- @field keepalt? boolean
+--- @field keepjumps? boolean
+--- @field keepmarks? boolean
+--- @field keeppatterns? boolean
+--- @field lockmarks? boolean
+--- @field noautocmd? boolean
+--- @field o? table<string, any>
+--- @field sandbox? boolean
+--- @field silent? boolean
+--- @field unsilent? boolean
+--- @field win? integer
+--- @field wo? table<string, any>
+
+--- @nodoc
+--- @class vim.context.state
+--- @field bo? table<string, any>
+--- @field env? table<string, any>
+--- @field go? table<string, any>
+--- @field wo? table<string, any>
+
+local scope_map = { buf = 'bo', global = 'go', win = 'wo' }
+local scope_order = { 'o', 'wo', 'bo', 'go', 'env' }
+local state_restore_order = { 'bo', 'wo', 'go', 'env' }
+
+--- Gets data about current state, enough to properly restore specified options/env/etc.
+--- @param context vim.context.mods
+--- @return vim.context.state
+local get_context_state = function(context)
+ local res = { bo = {}, env = {}, go = {}, wo = {} }
+
+ -- Use specific order from possibly most to least intrusive
+ for _, scope in ipairs(scope_order) do
+ for name, _ in pairs(context[scope] or {}) do
+ local sc = scope == 'o' and scope_map[vim.api.nvim_get_option_info2(name, {}).scope] or scope
+
+ -- Do not override already set state and fall back to `vim.NIL` for
+ -- state `nil` values (which still needs restoring later)
+ res[sc][name] = res[sc][name] or vim[sc][name] or vim.NIL
+
+ -- Always track global option value to properly restore later.
+ -- This matters for at least `o` and `wo` (which might set either/both
+ -- local and global option values).
+ if sc ~= 'env' then
+ res.go[name] = res.go[name] or vim.go[name]
+ end
+ end
+ end
+
+ return res
+end
+
+--- Executes function `f` with the given context specification.
+---
+--- Notes:
+--- - Context `{ buf = buf }` has no guarantees about current window when
+--- inside context.
+--- - Context `{ buf = buf, win = win }` is yet not allowed, but this seems
+--- to be an implementation detail.
+--- - There should be no way to revert currently set `context.sandbox = true`
+--- (like with nested `vim._with()` calls). Otherwise it kind of breaks the
+--- whole purpose of sandbox execution.
+--- - Saving and restoring option contexts (`bo`, `go`, `o`, `wo`) trigger
+--- `OptionSet` events. This is an implementation issue because not doing it
+--- seems to mean using either 'eventignore' option or extra nesting with
+--- `{ noautocmd = true }` (which itself is a wrapper for 'eventignore').
+--- As `{ go = { eventignore = '...' } }` is a valid context which should be
+--- properly set and restored, this is not a good approach.
+--- Not triggering `OptionSet` seems to be a good idea, though. So probably
+--- only moving context save and restore to lower level might resolve this.
+---
+--- @param context vim.context.mods
+--- @param f function
+--- @return any
+function vim._with(context, f)
+ vim.validate('context', context, 'table')
+ vim.validate('f', f, 'function')
+
+ vim.validate('context.bo', context.bo, 'table', true)
+ vim.validate('context.buf', context.buf, 'number', true)
+ vim.validate('context.emsg_silent', context.emsg_silent, 'boolean', true)
+ vim.validate('context.env', context.env, 'table', true)
+ vim.validate('context.go', context.go, 'table', true)
+ vim.validate('context.hide', context.hide, 'boolean', true)
+ vim.validate('context.keepalt', context.keepalt, 'boolean', true)
+ vim.validate('context.keepjumps', context.keepjumps, 'boolean', true)
+ vim.validate('context.keepmarks', context.keepmarks, 'boolean', true)
+ vim.validate('context.keeppatterns', context.keeppatterns, 'boolean', true)
+ vim.validate('context.lockmarks', context.lockmarks, 'boolean', true)
+ vim.validate('context.noautocmd', context.noautocmd, 'boolean', true)
+ vim.validate('context.o', context.o, 'table', true)
+ vim.validate('context.sandbox', context.sandbox, 'boolean', true)
+ vim.validate('context.silent', context.silent, 'boolean', true)
+ vim.validate('context.unsilent', context.unsilent, 'boolean', true)
+ vim.validate('context.win', context.win, 'number', true)
+ vim.validate('context.wo', context.wo, 'table', true)
+
+ -- Check buffer exists
+ if context.buf then
+ if not vim.api.nvim_buf_is_valid(context.buf) then
+ error('Invalid buffer id: ' .. context.buf)
+ end
+ end
+
+ -- Check window exists
+ if context.win then
+ if not vim.api.nvim_win_is_valid(context.win) then
+ error('Invalid window id: ' .. context.win)
+ end
+ -- TODO: Maybe allow it?
+ if context.buf and vim.api.nvim_win_get_buf(context.win) ~= context.buf then
+ error('Can not set both `buf` and `win` context.')
+ end
+ end
+
+ -- Decorate so that save-set-restore options is done in correct window-buffer
+ local callback = function()
+ -- Cache current values to be changed by context
+ -- Abort early in case of bad context value
+ local ok, state = pcall(get_context_state, context)
+ if not ok then
+ error(state, 0)
+ end
+
+ -- Apply some parts of the context in specific order
+ -- NOTE: triggers `OptionSet` event
+ for _, scope in ipairs(scope_order) do
+ for name, context_value in pairs(context[scope] or {}) do
+ vim[scope][name] = context_value
+ end
+ end
+
+ -- Execute
+ local res = { pcall(f) }
+
+ -- Restore relevant cached values in specific order, global scope last
+ -- NOTE: triggers `OptionSet` event
+ for _, scope in ipairs(state_restore_order) do
+ for name, cached_value in pairs(state[scope]) do
+ vim[scope][name] = cached_value
+ end
+ end
+
+ -- Return
+ if not res[1] then
+ error(res[2], 0)
+ end
+ table.remove(res, 1)
+ return unpack(res, 1, table.maxn(res))
+ end
+
+ return vim._with_c(context, callback)
+end
+
return vim
diff --git a/runtime/lua/vim/snippet.lua b/runtime/lua/vim/snippet.lua
index 3d8f73f362..af7e3c6d33 100644
--- a/runtime/lua/vim/snippet.lua
+++ b/runtime/lua/vim/snippet.lua
@@ -2,6 +2,8 @@ local G = vim.lsp._snippet_grammar
local snippet_group = vim.api.nvim_create_augroup('vim/snippet', {})
local snippet_ns = vim.api.nvim_create_namespace('vim/snippet')
local hl_group = 'SnippetTabstop'
+local jump_forward_key = '<tab>'
+local jump_backward_key = '<s-tab>'
--- Returns the 0-based cursor position.
---
@@ -182,6 +184,8 @@ end
--- @field extmark_id integer
--- @field tabstops table<integer, vim.snippet.Tabstop[]>
--- @field current_tabstop vim.snippet.Tabstop
+--- @field tab_keymaps { i: table<string, any>?, s: table<string, any>? }
+--- @field shift_tab_keymaps { i: table<string, any>?, s: table<string, any>? }
local Session = {}
--- Creates a new snippet session in the current buffer.
@@ -197,6 +201,8 @@ function Session.new(bufnr, snippet_extmark, tabstop_data)
extmark_id = snippet_extmark,
tabstops = {},
current_tabstop = Tabstop.new(0, bufnr, { 0, 0, 0, 0 }),
+ tab_keymaps = { i = nil, s = nil },
+ shift_tab_keymaps = { i = nil, s = nil },
}, { __index = Session })
-- Create the tabstops.
@@ -207,9 +213,64 @@ function Session.new(bufnr, snippet_extmark, tabstop_data)
end
end
+ self:set_keymaps()
+
return self
end
+--- Sets the snippet navigation keymaps.
+---
+--- @package
+function Session:set_keymaps()
+ local function maparg(key, mode)
+ local map = vim.fn.maparg(key, mode, false, true) --[[ @as table ]]
+ if not vim.tbl_isempty(map) and map.buffer == 1 then
+ return map
+ else
+ return nil
+ end
+ end
+
+ local function set(jump_key, direction)
+ vim.keymap.set({ 'i', 's' }, jump_key, function()
+ return vim.snippet.active({ direction = direction })
+ and '<cmd>lua vim.snippet.jump(' .. direction .. ')<cr>'
+ or jump_key
+ end, { expr = true, silent = true, buffer = self.bufnr })
+ end
+
+ self.tab_keymaps = {
+ i = maparg(jump_forward_key, 'i'),
+ s = maparg(jump_forward_key, 's'),
+ }
+ self.shift_tab_keymaps = {
+ i = maparg(jump_backward_key, 'i'),
+ s = maparg(jump_backward_key, 's'),
+ }
+ set(jump_forward_key, 1)
+ set(jump_backward_key, -1)
+end
+
+--- Restores/deletes the keymaps used for snippet navigation.
+---
+--- @package
+function Session:restore_keymaps()
+ local function restore(keymap, lhs, mode)
+ if keymap then
+ vim._with({ buf = self.bufnr }, function()
+ vim.fn.mapset(keymap)
+ end)
+ else
+ vim.api.nvim_buf_del_keymap(self.bufnr, mode, lhs)
+ end
+ end
+
+ restore(self.tab_keymaps.i, jump_forward_key, 'i')
+ restore(self.tab_keymaps.s, jump_forward_key, 's')
+ restore(self.shift_tab_keymaps.i, jump_backward_key, 'i')
+ restore(self.shift_tab_keymaps.s, jump_backward_key, 's')
+end
+
--- Returns the destination tabstop index when jumping in the given direction.
---
--- @package
@@ -315,7 +376,7 @@ local function select_tabstop(tabstop)
move_cursor_to(range[1] + 1, range[2] + 1)
feedkeys('v')
move_cursor_to(range[3] + 1, range[4])
- feedkeys('o<c-g>')
+ feedkeys('o<c-g><c-r>_')
end
end
@@ -395,6 +456,15 @@ local function setup_autocmds(bufnr)
end
end,
})
+
+ vim.api.nvim_create_autocmd('BufLeave', {
+ group = snippet_group,
+ desc = 'Stop the snippet session when leaving the buffer',
+ buffer = bufnr,
+ callback = function()
+ M.stop()
+ end,
+ })
end
--- Expands the given snippet text.
@@ -444,7 +514,7 @@ function M.expand(input)
local snippet_lines = text_to_lines(snippet_text)
-- Get the base indentation based on the current line and the last line of the snippet.
if #snippet_lines > 0 then
- base_indent = base_indent .. (snippet_lines[#snippet_lines]:match('(^%s*)%S') or '') --- @type string
+ base_indent = base_indent .. (snippet_lines[#snippet_lines]:match('(^%s+)%S') or '') --- @type string
end
local shiftwidth = vim.fn.shiftwidth()
@@ -539,7 +609,7 @@ end
--- ```lua
--- vim.keymap.set({ 'i', 's' }, '<Tab>', function()
--- if vim.snippet.active({ direction = 1 }) then
---- return '<cmd>lua vim.snippet.jump(1)<cr>'
+--- return '<Cmd>lua vim.snippet.jump(1)<CR>'
--- else
--- return '<Tab>'
--- end
@@ -591,7 +661,7 @@ end
--- ```lua
--- vim.keymap.set({ 'i', 's' }, '<Tab>', function()
--- if vim.snippet.active({ direction = 1 }) then
---- return '<cmd>lua vim.snippet.jump(1)<cr>'
+--- return '<Cmd>lua vim.snippet.jump(1)<CR>'
--- else
--- return '<Tab>'
--- end
@@ -619,6 +689,8 @@ function M.stop()
return
end
+ M._session:restore_keymaps()
+
vim.api.nvim_clear_autocmds({ group = snippet_group, buffer = M._session.bufnr })
vim.api.nvim_buf_clear_namespace(M._session.bufnr, snippet_ns, 0, -1)
diff --git a/runtime/lua/vim/text.lua b/runtime/lua/vim/text.lua
index bc90d490aa..d45c8021c6 100644
--- a/runtime/lua/vim/text.lua
+++ b/runtime/lua/vim/text.lua
@@ -7,10 +7,9 @@ local M = {}
--- @param str string String to encode
--- @return string : Hex encoded string
function M.hexencode(str)
- local bytes = { str:byte(1, #str) }
local enc = {} ---@type string[]
- for i = 1, #bytes do
- enc[i] = string.format('%02X', bytes[i])
+ for i = 1, #str do
+ enc[i] = string.format('%02X', str:byte(i, i + 1))
end
return table.concat(enc)
end
@@ -18,15 +17,19 @@ end
--- Hex decode a string.
---
--- @param enc string String to decode
---- @return string : Decoded string
+--- @return string? : Decoded string
+--- @return string? : Error message, if any
function M.hexdecode(enc)
- assert(#enc % 2 == 0, 'string must have an even number of hex characters')
+ if #enc % 2 ~= 0 then
+ return nil, 'string must have an even number of hex characters'
+ end
+
local str = {} ---@type string[]
for i = 1, #enc, 2 do
local n = assert(tonumber(enc:sub(i, i + 1), 16))
str[#str + 1] = string.char(n)
end
- return table.concat(str)
+ return table.concat(str), nil
end
return M
diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua
index db544c1ab1..ed7d31e1f7 100644
--- a/runtime/lua/vim/treesitter.lua
+++ b/runtime/lua/vim/treesitter.lua
@@ -76,36 +76,48 @@ end
---
--- If needed, this will create the parser.
---
+--- If no parser can be created, an error is thrown. Set `opts.error = false` to suppress this and
+--- return nil (and an error message) instead. WARNING: This behavior will become default in Nvim
+--- 0.12 and the option will be removed.
+---
---@param bufnr (integer|nil) Buffer the parser should be tied to (default: current buffer)
---@param lang (string|nil) Language of this parser (default: from buffer filetype)
---@param opts (table|nil) Options to pass to the created language tree
---
----@return vim.treesitter.LanguageTree object to use for parsing
+---@return vim.treesitter.LanguageTree? object to use for parsing
+---@return string? error message, if applicable
function M.get_parser(bufnr, lang, opts)
opts = opts or {}
+ local should_error = opts.error == nil or opts.error
if bufnr == nil or bufnr == 0 then
bufnr = api.nvim_get_current_buf()
end
if not valid_lang(lang) then
- lang = M.language.get_lang(vim.bo[bufnr].filetype) or vim.bo[bufnr].filetype
+ lang = M.language.get_lang(vim.bo[bufnr].filetype)
end
if not valid_lang(lang) then
if not parsers[bufnr] then
- error(
- string.format(
- 'There is no parser available for buffer %d and one could not be'
- .. ' created because lang could not be determined. Either pass lang'
- .. ' or set the buffer filetype',
- bufnr
- )
- )
+ local err_msg =
+ string.format('Parser not found for buffer %s: language could not be determined', bufnr)
+ if should_error then
+ error(err_msg)
+ end
+ return nil, err_msg
end
elseif parsers[bufnr] == nil or parsers[bufnr]:lang() ~= lang then
- assert(lang, 'lang should be valid')
- parsers[bufnr] = M._create_parser(bufnr, lang, opts)
+ local parser = vim.F.npcall(M._create_parser, bufnr, lang, opts)
+ if not parser then
+ local err_msg =
+ string.format('Parser could not be created for buffer %s and language "%s"', bufnr, lang)
+ if should_error then
+ error(err_msg)
+ end
+ return nil, err_msg
+ end
+ parsers[bufnr] = parser
end
parsers[bufnr]:register_cbs(opts.buf_attach_cbs)
@@ -140,16 +152,8 @@ function M.is_ancestor(dest, source)
return false
end
- local current = source ---@type TSNode?
- while current ~= nil do
- if current == dest then
- return true
- end
-
- current = current:parent()
- end
-
- return false
+ -- child_containing_descendant returns nil if dest is a direct parent
+ return source:parent() == dest or dest:child_containing_descendant(source) ~= nil
end
--- Returns the node's range or an unpacked range table
@@ -257,7 +261,7 @@ end
---@param row integer Position row
---@param col integer Position column
---
----@return {capture: string, lang: string, metadata: table}[]
+---@return {capture: string, lang: string, metadata: vim.treesitter.query.TSMetadata}[]
function M.get_captures_at_pos(bufnr, row, col)
if bufnr == 0 then
bufnr = api.nvim_get_current_buf()
@@ -335,13 +339,16 @@ end
---
--- 0-indexed (row, col) tuple. Defaults to cursor position in the
--- current window. Required if {bufnr} is not the current buffer
---- @field pos { [1]: integer, [2]: integer }?
+--- @field pos [integer, integer]?
---
--- Parser language. (default: from buffer filetype)
--- @field lang string?
---
--- Ignore injected languages (default true)
--- @field ignore_injections boolean?
+---
+--- Include anonymous nodes (default false)
+--- @field include_anonymous boolean?
--- Returns the smallest named node at the given position
---
@@ -383,11 +390,14 @@ function M.get_node(opts)
local ts_range = { row, col, row, col }
- local root_lang_tree = M.get_parser(bufnr, opts.lang)
+ local root_lang_tree = M.get_parser(bufnr, opts.lang, { error = false })
if not root_lang_tree then
return
end
+ if opts.include_anonymous then
+ return root_lang_tree:node_for_range(ts_range, opts)
+ end
return root_lang_tree:named_node_for_range(ts_range, opts)
end
@@ -413,7 +423,7 @@ end
---@param lang (string|nil) Language of the parser (default: from buffer filetype)
function M.start(bufnr, lang)
bufnr = bufnr or api.nvim_get_current_buf()
- local parser = M.get_parser(bufnr, lang)
+ local parser = assert(M.get_parser(bufnr, lang, { error = false }))
M.highlighter.new(parser)
end
@@ -437,6 +447,7 @@ end
---
--- Can also be shown with `:InspectTree`. [:InspectTree]()
---
+---@since 11
---@param opts table|nil Optional options table with the following possible keys:
--- - lang (string|nil): The language of the source buffer. If omitted, detect
--- from the filetype of the source buffer.
@@ -460,6 +471,7 @@ end
--- vim.wo.foldexpr = 'v:lua.vim.treesitter.foldexpr()'
--- ```
---
+---@since 11
---@param lnum integer|nil Line number to calculate fold level for
---@return string
function M.foldexpr(lnum)
diff --git a/runtime/lua/vim/treesitter/_fold.lua b/runtime/lua/vim/treesitter/_fold.lua
index eecf1ad6b1..7237d2e7d4 100644
--- a/runtime/lua/vim/treesitter/_fold.lua
+++ b/runtime/lua/vim/treesitter/_fold.lua
@@ -87,7 +87,7 @@ end
---@param srow integer
---@param erow integer 0-indexed, exclusive
function FoldInfo:add_range(srow, erow)
- list_insert(self.levels, srow + 1, erow, '=')
+ list_insert(self.levels, srow + 1, erow, -1)
list_insert(self.levels0, srow + 1, erow, -1)
end
@@ -114,7 +114,7 @@ local function compute_folds_levels(bufnr, info, srow, erow, parse_injections)
srow = srow or 0
erow = erow or api.nvim_buf_line_count(bufnr)
- local parser = ts.get_parser(bufnr)
+ local parser = assert(ts.get_parser(bufnr, nil, { error = false }))
parser:parse(parse_injections and { srow, erow } or nil)
@@ -131,24 +131,18 @@ 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])
local start, _, stop, stop_col = Range.unpack4(range)
- for i = 2, #nodes, 1 do
- local node_range = ts.get_range(nodes[i], bufnr, metadata[id])
- local node_start, _, node_stop, node_stop_col = Range.unpack4(node_range)
- if node_start < start then
- start = node_start
- end
- if node_stop > stop then
- stop = node_stop
- stop_col = node_stop_col
- end
+ if #nodes > 1 then
+ -- assumes nodes are ordered by range
+ local end_range = ts.get_range(nodes[#nodes], bufnr, metadata[id])
+ local _, _, end_stop, end_stop_col = Range.unpack4(end_range)
+ stop = end_stop
+ stop_col = end_stop_col
end
if stop_col == 0 then
@@ -268,6 +262,15 @@ end
---@package
function FoldInfo:do_foldupdate(bufnr)
+ -- InsertLeave is not executed when <C-C> is used for exiting the insert mode, leaving
+ -- do_foldupdate untouched. If another execution of foldupdate consumes foldupdate_range, the
+ -- InsertLeave do_foldupdate gets nil foldupdate_range. In that case, skip the update. This is
+ -- correct because the update that consumed the range must have incorporated the range that
+ -- InsertLeave meant to update.
+ if not self.foldupdate_range then
+ return
+ end
+
local srow, erow = self.foldupdate_range[1], self.foldupdate_range[2]
self.foldupdate_range = nil
for _, win in ipairs(vim.fn.win_findbuf(bufnr)) do
@@ -383,14 +386,13 @@ local function on_bytes(bufnr, foldinfo, start_row, start_col, old_row, old_col,
end
end
----@package
---@param lnum integer|nil
---@return string
function M.foldexpr(lnum)
lnum = lnum or vim.v.lnum
local bufnr = api.nvim_get_current_buf()
- local parser = vim.F.npcall(ts.get_parser, bufnr)
+ local parser = ts.get_parser(bufnr, nil, { error = false })
if not parser then
return '0'
end
diff --git a/runtime/lua/vim/treesitter/_meta.lua b/runtime/lua/vim/treesitter/_meta.lua
deleted file mode 100644
index 177699a207..0000000000
--- a/runtime/lua/vim/treesitter/_meta.lua
+++ /dev/null
@@ -1,113 +0,0 @@
----@meta
-error('Cannot require a meta file')
-
----@class TSNode: userdata
----@field id fun(self: TSNode): string
----@field tree fun(self: TSNode): TSTree
----@field range fun(self: TSNode, include_bytes: false?): integer, integer, integer, integer
----@field range fun(self: TSNode, include_bytes: true): integer, integer, integer, integer, integer, integer
----@field start fun(self: TSNode): integer, integer, integer
----@field end_ fun(self: TSNode): integer, integer, integer
----@field type fun(self: TSNode): string
----@field symbol fun(self: TSNode): integer
----@field named fun(self: TSNode): boolean
----@field missing fun(self: TSNode): boolean
----@field extra fun(self: TSNode): boolean
----@field child_count fun(self: TSNode): integer
----@field named_child_count fun(self: TSNode): integer
----@field child fun(self: TSNode, index: integer): TSNode?
----@field named_child fun(self: TSNode, index: integer): TSNode?
----@field descendant_for_range fun(self: TSNode, start_row: integer, start_col: integer, end_row: integer, end_col: integer): TSNode?
----@field named_descendant_for_range fun(self: TSNode, start_row: integer, start_col: integer, end_row: integer, end_col: integer): TSNode?
----@field parent fun(self: TSNode): TSNode?
----@field child_containing_descendant fun(self: TSNode, descendant: TSNode): TSNode?
----@field next_sibling fun(self: TSNode): TSNode?
----@field prev_sibling fun(self: TSNode): TSNode?
----@field next_named_sibling fun(self: TSNode): TSNode?
----@field prev_named_sibling fun(self: TSNode): TSNode?
----@field named_children fun(self: TSNode): TSNode[]
----@field has_changes fun(self: TSNode): boolean
----@field has_error fun(self: TSNode): boolean
----@field sexpr fun(self: TSNode): string
----@field equal fun(self: TSNode, other: TSNode): boolean
----@field iter_children fun(self: TSNode): fun(): TSNode, string
----@field field fun(self: TSNode, name: string): TSNode[]
----@field byte_length fun(self: TSNode): integer
-local TSNode = {}
-
----@alias TSLoggerCallback fun(logtype: 'parse'|'lex', msg: string)
-
----@class TSParser: userdata
----@field parse fun(self: TSParser, tree: TSTree?, source: integer|string, include_bytes: boolean): TSTree, (Range4|Range6)[]
----@field reset fun(self: TSParser)
----@field included_ranges fun(self: TSParser, include_bytes: boolean?): integer[]
----@field set_included_ranges fun(self: TSParser, ranges: (Range6|TSNode)[])
----@field set_timeout fun(self: TSParser, timeout: integer)
----@field timeout fun(self: TSParser): integer
----@field _set_logger fun(self: TSParser, lex: boolean, parse: boolean, cb: TSLoggerCallback)
----@field _logger fun(self: TSParser): TSLoggerCallback
-
----@class TSTree: userdata
----@field root fun(self: TSTree): TSNode
----@field edit fun(self: TSTree, _: integer, _: integer, _: integer, _: integer, _: integer, _: integer, _: integer, _: integer, _:integer)
----@field copy fun(self: TSTree): TSTree
----@field included_ranges fun(self: TSTree, include_bytes: true): Range6[]
----@field included_ranges fun(self: TSTree, include_bytes: false): Range4[]
-
----@class TSQuery: userdata
----@field inspect fun(self: TSQuery): TSQueryInfo
-
----@class (exact) TSQueryInfo
----@field captures string[]
----@field patterns table<integer, (integer|string)[][]>
-
---- @param lang string
-vim._ts_inspect_language = function(lang) end
-
----@return integer
-vim._ts_get_language_version = function() end
-
---- @param path string
---- @param lang string
---- @param symbol_name? string
-vim._ts_add_language = function(path, lang, symbol_name) end
-
----@return integer
-vim._ts_get_minimum_language_version = function() end
-
----@param lang string Language to use for the query
----@param query string Query string in s-expr syntax
----@return TSQuery
-vim._ts_parse_query = function(lang, query) end
-
----@param lang string
----@return TSParser
-vim._create_ts_parser = function(lang) end
-
---- @class TSQueryMatch: userdata
---- @field captures fun(self: TSQueryMatch): table<integer,TSNode[]>
-local TSQueryMatch = {}
-
---- @return integer match_id
---- @return integer pattern_index
-function TSQueryMatch:info() end
-
---- @class TSQueryCursor: userdata
---- @field remove_match fun(self: TSQueryCursor, id: integer)
-local TSQueryCursor = {}
-
---- @return integer capture
---- @return TSNode captured_node
---- @return TSQueryMatch match
-function TSQueryCursor:next_capture() end
-
---- @return TSQueryMatch match
-function TSQueryCursor:next_match() end
-
---- @param node TSNode
---- @param query TSQuery
---- @param start integer?
---- @param stop integer?
---- @param opts? { max_start_depth?: integer, match_limit?: integer}
---- @return TSQueryCursor
-function vim._create_ts_querycursor(node, query, start, stop, opts) end
diff --git a/runtime/lua/vim/treesitter/_meta/misc.lua b/runtime/lua/vim/treesitter/_meta/misc.lua
new file mode 100644
index 0000000000..33701ef254
--- /dev/null
+++ b/runtime/lua/vim/treesitter/_meta/misc.lua
@@ -0,0 +1,78 @@
+---@meta
+-- luacheck: no unused args
+error('Cannot require a meta file')
+
+---@alias TSLoggerCallback fun(logtype: 'parse'|'lex', msg: string)
+
+---@class TSParser: userdata
+---@field parse fun(self: TSParser, tree: TSTree?, source: integer|string, include_bytes: boolean): TSTree, (Range4|Range6)[]
+---@field reset fun(self: TSParser)
+---@field included_ranges fun(self: TSParser, include_bytes: boolean?): integer[]
+---@field set_included_ranges fun(self: TSParser, ranges: (Range6|TSNode)[])
+---@field set_timeout fun(self: TSParser, timeout: integer)
+---@field timeout fun(self: TSParser): integer
+---@field _set_logger fun(self: TSParser, lex: boolean, parse: boolean, cb: TSLoggerCallback)
+---@field _logger fun(self: TSParser): TSLoggerCallback
+
+---@class TSQuery: userdata
+---@field inspect fun(self: TSQuery): TSQueryInfo
+
+---@class (exact) TSQueryInfo
+---@field captures string[]
+---@field patterns table<integer, (integer|string)[][]>
+
+--- @param lang string
+--- @return table
+vim._ts_inspect_language = function(lang) end
+
+---@return integer
+vim._ts_get_language_version = function() end
+
+--- @param path string
+--- @param lang string
+--- @param symbol_name? string
+vim._ts_add_language_from_object = function(path, lang, symbol_name) end
+
+--- @param path string
+--- @param lang string
+vim._ts_add_language_from_wasm = function(path, lang) end
+
+---@return integer
+vim._ts_get_minimum_language_version = function() end
+
+---@param lang string Language to use for the query
+---@param query string Query string in s-expr syntax
+---@return TSQuery
+vim._ts_parse_query = function(lang, query) end
+
+---@param lang string
+---@return TSParser
+vim._create_ts_parser = function(lang) end
+
+--- @class TSQueryMatch: userdata
+--- @field captures fun(self: TSQueryMatch): table<integer,TSNode[]>
+local TSQueryMatch = {} -- luacheck: no unused
+
+--- @return integer match_id
+--- @return integer pattern_index
+function TSQueryMatch:info() end
+
+--- @class TSQueryCursor: userdata
+--- @field remove_match fun(self: TSQueryCursor, id: integer)
+local TSQueryCursor = {} -- luacheck: no unused
+
+--- @return integer capture
+--- @return TSNode captured_node
+--- @return TSQueryMatch match
+function TSQueryCursor:next_capture() end
+
+--- @return TSQueryMatch match
+function TSQueryCursor:next_match() end
+
+--- @param node TSNode
+--- @param query TSQuery
+--- @param start integer?
+--- @param stop integer?
+--- @param opts? { max_start_depth?: integer, match_limit?: integer}
+--- @return TSQueryCursor
+function vim._create_ts_querycursor(node, query, start, stop, opts) end
diff --git a/runtime/lua/vim/treesitter/_meta/tsnode.lua b/runtime/lua/vim/treesitter/_meta/tsnode.lua
new file mode 100644
index 0000000000..acc9f8d24e
--- /dev/null
+++ b/runtime/lua/vim/treesitter/_meta/tsnode.lua
@@ -0,0 +1,185 @@
+---@meta
+-- luacheck: no unused args
+error('Cannot require a meta file')
+
+--- @brief A "treesitter node" represents one specific element of the parsed contents of a buffer,
+--- which can be captured by a |Query| for, e.g., highlighting. It is a |userdata| reference to an
+--- object held by the treesitter library.
+---
+--- An instance `TSNode` of a treesitter node supports the following methods.
+
+---@nodoc
+---@class TSNode: userdata
+---@field named_children fun(self: TSNode): TSNode[]
+---@field __has_ancestor fun(self: TSNode, node_types: string[]): boolean
+local TSNode = {} -- luacheck: no unused
+
+--- Get the node's immediate parent.
+--- Prefer |TSNode:child_containing_descendant()|
+--- for iterating over the node's ancestors.
+--- @return TSNode?
+function TSNode:parent() end
+
+--- Get the node's next sibling.
+--- @return TSNode?
+function TSNode:next_sibling() end
+
+--- Get the node's previous sibling.
+--- @return TSNode?
+function TSNode:prev_sibling() end
+
+--- Get the node's next named sibling.
+--- @return TSNode?
+function TSNode:next_named_sibling() end
+
+--- Get the node's previous named sibling.
+--- @return TSNode?
+function TSNode:prev_named_sibling() end
+
+--- Iterates over all the direct children of {TSNode}, regardless of whether
+--- they are named or not.
+--- Returns the child node plus the eventual field name corresponding to this
+--- child node.
+--- @return fun(): TSNode, string
+function TSNode:iter_children() end
+
+--- Returns a table of the nodes corresponding to the {name} field.
+--- @param name string
+--- @return TSNode[]
+function TSNode:field(name) end
+
+--- Get the node's number of children.
+--- @return integer
+function TSNode:child_count() end
+
+--- Get the node's child at the given {index}, where zero represents the first
+--- child.
+--- @param index integer
+--- @return TSNode?
+function TSNode:child(index) end
+
+--- Get the node's number of named children.
+--- @return integer
+function TSNode:named_child_count() end
+
+--- Get the node's named child at the given {index}, where zero represents the
+--- first named child.
+--- @param index integer
+--- @return TSNode?
+function TSNode:named_child(index) end
+
+--- Get the node's child that contains {descendant}.
+--- @param descendant TSNode
+--- @return TSNode?
+function TSNode:child_containing_descendant(descendant) end
+
+--- Get the node's start position. Return three values: the row, column and
+--- total byte count (all zero-based).
+--- @return integer, integer, integer
+function TSNode:start() end
+
+--- Get the node's end position. Return three values: the row, column and
+--- total byte count (all zero-based).
+--- @return integer, integer, integer
+function TSNode:end_() end
+
+--- Get the range of the node.
+---
+--- Return four or six values:
+---
+--- - start row
+--- - start column
+--- - start byte (if {include_bytes} is `true`)
+--- - end row
+--- - end column
+--- - end byte (if {include_bytes} is `true`)
+--- @param include_bytes boolean?
+function TSNode:range(include_bytes) end
+
+--- @nodoc
+--- @param include_bytes false?
+--- @return integer, integer, integer, integer
+function TSNode:range(include_bytes) end
+
+--- @nodoc
+--- @param include_bytes true
+--- @return integer, integer, integer, integer, integer, integer
+function TSNode:range(include_bytes) end
+
+--- Get the node's type as a string.
+--- @return string
+function TSNode:type() end
+
+--- Get the node's type as a numerical id.
+--- @return integer
+function TSNode:symbol() end
+
+--- Check if the node is named. Named nodes correspond to named rules in the
+--- grammar, whereas anonymous nodes correspond to string literals in the
+--- grammar.
+--- @return boolean
+function TSNode:named() end
+
+--- Check if the node is missing. Missing nodes are inserted by the parser in
+--- order to recover from certain kinds of syntax errors.
+--- @return boolean
+function TSNode:missing() end
+
+--- Check if the node is extra. Extra nodes represent things like comments,
+--- which are not required by the grammar but can appear anywhere.
+--- @return boolean
+function TSNode:extra() end
+
+--- Check if a syntax node has been edited.
+--- @return boolean
+function TSNode:has_changes() end
+
+--- Check if the node is a syntax error or contains any syntax errors.
+--- @return boolean
+function TSNode:has_error() end
+
+--- Get an S-expression representing the node as a string.
+--- @return string
+function TSNode:sexpr() end
+
+--- Get a unique identifier for the node inside its own tree.
+---
+--- No guarantees are made about this identifier's internal representation,
+--- except for being a primitive Lua type with value equality (so not a
+--- table). Presently it is a (non-printable) string.
+---
+--- Note: The `id` is not guaranteed to be unique for nodes from different
+--- trees.
+--- @return string
+function TSNode:id() end
+
+--- Get the |TSTree| of the node.
+--- @return TSTree
+function TSNode:tree() end
+
+--- Get the smallest node within this node that spans the given range of (row,
+--- column) positions
+--- @param start_row integer
+--- @param start_col integer
+--- @param end_row integer
+--- @param end_col integer
+--- @return TSNode?
+function TSNode:descendant_for_range(start_row, start_col, end_row, end_col) end
+
+--- Get the smallest named node within this node that spans the given range of
+--- (row, column) positions
+--- @param start_row integer
+--- @param start_col integer
+--- @param end_row integer
+--- @param end_col integer
+--- @return TSNode?
+function TSNode:named_descendant_for_range(start_row, start_col, end_row, end_col) end
+
+--- Check if {node} refers to the same node within the same tree.
+--- @param node TSNode
+--- @return boolean
+function TSNode:equal(node) end
+
+--- Return the number of bytes spanned by this node.
+--- @return integer
+function TSNode:byte_length() end
diff --git a/runtime/lua/vim/treesitter/_meta/tstree.lua b/runtime/lua/vim/treesitter/_meta/tstree.lua
new file mode 100644
index 0000000000..24cb60040e
--- /dev/null
+++ b/runtime/lua/vim/treesitter/_meta/tstree.lua
@@ -0,0 +1,44 @@
+---@meta
+-- luacheck: no unused args
+error('Cannot require a meta file')
+
+--- @brief A "treesitter tree" represents the parsed contents of a buffer, which can be
+--- used to perform further analysis. It is a |userdata| reference to an object
+--- held by the treesitter library.
+---
+--- An instance `TSTree` of a treesitter tree supports the following methods.
+
+---@nodoc
+---@class TSTree: userdata
+local TSTree = {} -- luacheck: no unused
+
+--- Return the root node of this tree.
+---@return TSNode
+function TSTree:root() end
+
+-- stylua: ignore
+---@param start_byte integer
+---@param end_byte_old integer
+---@param end_byte_new integer
+---@param start_row integer
+---@param start_col integer
+---@param end_row_old integer
+---@param end_col_old integer
+---@param end_row_new integer
+---@param end_col_new integer
+---@nodoc
+function TSTree:edit(start_byte, end_byte_old, end_byte_new, start_row, start_col, end_row_old, end_col_old, end_row_new, end_col_new) end
+
+--- Returns a copy of the `TSTree`.
+---@return TSTree
+function TSTree:copy() end
+
+---@param include_bytes true
+---@return Range6[]
+---@nodoc
+function TSTree:included_ranges(include_bytes) end
+
+---@param include_bytes false
+---@return Range4[]
+---@nodoc
+function TSTree:included_ranges(include_bytes) end
diff --git a/runtime/lua/vim/treesitter/_query_linter.lua b/runtime/lua/vim/treesitter/_query_linter.lua
index 12b4cbc7b9..c5e4b86e1e 100644
--- a/runtime/lua/vim/treesitter/_query_linter.lua
+++ b/runtime/lua/vim/treesitter/_query_linter.lua
@@ -40,7 +40,8 @@ end
local function guess_query_lang(buf)
local filename = api.nvim_buf_get_name(buf)
if filename ~= '' then
- return vim.F.npcall(vim.fn.fnamemodify, filename, ':p:h:t')
+ local resolved_filename = vim.F.npcall(vim.fn.fnamemodify, filename, ':p:h:t')
+ return resolved_filename and vim.treesitter.language.get_lang(resolved_filename)
end
end
@@ -64,7 +65,7 @@ local function normalize_opts(buf, opts)
end
local lint_query = [[;; query
- (program [(named_node) (list) (grouping)] @toplevel)
+ (program [(named_node) (anonymous_node) (list) (grouping)] @toplevel)
(named_node
name: _ @node.named)
(anonymous_node
@@ -170,17 +171,17 @@ function M.lint(buf, opts)
--- @type (table|nil)
local parser_info = vim.F.npcall(vim.treesitter.language.inspect, lang)
+ local lang_context = {
+ lang = lang,
+ parser_info = parser_info,
+ is_first_lang = i == 1,
+ }
- local parser = vim.treesitter.get_parser(buf)
+ local parser = assert(vim.treesitter.get_parser(buf, nil, { error = false }))
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
- local lang_context = {
- lang = lang,
- parser_info = parser_info,
- is_first_lang = i == 1,
- }
+ for _, match, _ in query:iter_matches(tree:root(), buf, 0, -1) do
lint_match(buf, match, query, lang_context, diagnostics)
end
end
@@ -240,7 +241,7 @@ function M.omnifunc(findstart, base)
end
end
for _, s in pairs(parser_info.symbols) do
- local text = s[2] and s[1] or '"' .. s[1]:gsub([[\]], [[\\]]) .. '"' ---@type string
+ local text = s[2] and s[1] or string.format('%q', s[1]):gsub('\n', 'n') ---@type string
if text:find(base, 1, true) then
table.insert(items, text)
end
diff --git a/runtime/lua/vim/treesitter/_range.lua b/runtime/lua/vim/treesitter/_range.lua
index 8d727c3c52..82ab8517aa 100644
--- a/runtime/lua/vim/treesitter/_range.lua
+++ b/runtime/lua/vim/treesitter/_range.lua
@@ -3,16 +3,19 @@ local api = vim.api
local M = {}
---@class Range2
+---@inlinedoc
---@field [1] integer start row
---@field [2] integer end row
---@class Range4
+---@inlinedoc
---@field [1] integer start row
---@field [2] integer start column
---@field [3] integer end row
---@field [4] integer end column
---@class Range6
+---@inlinedoc
---@field [1] integer start row
---@field [2] integer start column
---@field [3] integer start bytes
@@ -150,6 +153,7 @@ function M.contains(r1, r2)
return true
end
+--- @private
--- @param source integer|string
--- @param index integer
--- @return integer
diff --git a/runtime/lua/vim/treesitter/dev.lua b/runtime/lua/vim/treesitter/dev.lua
index 5c91f101c0..90c3720b80 100644
--- a/runtime/lua/vim/treesitter/dev.lua
+++ b/runtime/lua/vim/treesitter/dev.lua
@@ -76,10 +76,14 @@ end
---
---@package
function TSTreeView:new(bufnr, lang)
- local ok, parser = pcall(vim.treesitter.get_parser, bufnr or 0, lang)
- if not ok then
- local err = parser --[[ @as string ]]
- return nil, 'No parser available for the given buffer:\n' .. err
+ local parser = vim.treesitter.get_parser(bufnr or 0, lang, { error = false })
+ if not parser then
+ return nil,
+ string.format(
+ 'Failed to create TSTreeView for buffer %s: no parser for lang "%s"',
+ bufnr,
+ lang
+ )
end
-- For each child tree (injected language), find the root of the tree and locate the node within
@@ -154,7 +158,8 @@ end
---@param w integer
---@param b integer
-local function set_dev_properties(w, b)
+---@param opts nil|{ indent?: integer }
+local function set_dev_options(w, b, opts)
vim.wo[w].scrolloff = 5
vim.wo[w].wrap = false
vim.wo[w].foldmethod = 'expr'
@@ -165,6 +170,12 @@ local function set_dev_properties(w, b)
vim.bo[b].buftype = 'nofile'
vim.bo[b].bufhidden = 'wipe'
vim.bo[b].filetype = 'query'
+ vim.bo[b].swapfile = false
+
+ opts = opts or {}
+ if opts.indent then
+ vim.bo[b].shiftwidth = opts.indent
+ end
end
--- Updates the cursor position in the inspector to match the node under the cursor.
@@ -174,7 +185,7 @@ end
--- @param source_buf integer
--- @param inspect_buf integer
--- @param inspect_win integer
---- @param pos? { [1]: integer, [2]: integer }
+--- @param pos? [integer, integer]
local function set_inspector_cursor(treeview, lang, source_buf, inspect_buf, inspect_win, pos)
api.nvim_buf_clear_namespace(inspect_buf, treeview.ns, 0, -1)
@@ -183,6 +194,7 @@ local function set_inspector_cursor(treeview, lang, source_buf, inspect_buf, ins
lang = lang,
pos = pos,
ignore_injections = false,
+ include_anonymous = treeview.opts.anon,
})
if not cursor_node then
return
@@ -220,14 +232,13 @@ function TSTreeView:draw(bufnr)
local text ---@type string
if item.node:named() then
- if item.field then
- text = string.format('%s: (%s', item.field, item.node:type())
- else
- text = string.format('(%s', item.node:type())
- end
+ text = string.format('(%s', item.node:type())
else
text = string.format('%q', item.node:type()):gsub('\n', 'n')
end
+ if item.field then
+ text = string.format('%s: %s', item.field, text)
+ end
local next = self:get(i + 1)
if not next or next.depth <= item.depth then
@@ -325,7 +336,10 @@ function M.inspect_tree(opts)
opts = opts or {}
+ -- source buffer
local buf = api.nvim_get_current_buf()
+
+ -- window id for source buffer
local win = api.nvim_get_current_win()
local treeview = assert(TSTreeView:new(buf, opts.lang))
@@ -334,12 +348,14 @@ function M.inspect_tree(opts)
close_win(vim.b[buf].dev_inspect)
end
+ -- window id for tree buffer
local w = opts.winid
if not w then
vim.cmd(opts.command or '60vnew')
w = api.nvim_get_current_win()
end
+ -- tree buffer
local b = opts.bufnr
if b then
api.nvim_win_set_buf(w, b)
@@ -350,7 +366,7 @@ function M.inspect_tree(opts)
vim.b[buf].dev_inspect = w
vim.b[b].dev_base = win -- base window handle
vim.b[b].disable_query_linter = true
- set_dev_properties(w, b)
+ set_dev_options(w, b, { indent = treeview.opts.indent })
local title --- @type string?
local opts_title = opts.title
@@ -375,6 +391,12 @@ function M.inspect_tree(opts)
callback = function()
local row = api.nvim_win_get_cursor(w)[1]
local lnum, col = treeview:get(row).node:start()
+
+ -- update source window if original was closed
+ if not api.nvim_win_is_valid(win) then
+ win = vim.fn.win_findbuf(buf)[1]
+ end
+
api.nvim_set_current_win(win)
api.nvim_win_set_cursor(win, { lnum + 1, col })
end,
@@ -432,6 +454,7 @@ function M.inspect_tree(opts)
return true
end
+ w = api.nvim_get_current_win()
api.nvim_buf_clear_namespace(buf, treeview.ns, 0, -1)
local row = api.nvim_win_get_cursor(w)[1]
local lnum, col, end_lnum, end_col = treeview:get(row).node:range()
@@ -441,6 +464,11 @@ function M.inspect_tree(opts)
hl_group = 'Visual',
})
+ -- update source window if original was closed
+ if not api.nvim_win_is_valid(win) then
+ win = vim.fn.win_findbuf(buf)[1]
+ end
+
local topline, botline = vim.fn.line('w0', win), vim.fn.line('w$', win)
-- Move the cursor if highlighted range is completely out of view
@@ -506,7 +534,10 @@ function M.inspect_tree(opts)
buffer = buf,
once = true,
callback = function()
- close_win(w)
+ -- close all tree windows
+ for _, window in pairs(vim.fn.win_findbuf(b)) do
+ close_win(window)
+ end
end,
})
end
@@ -519,7 +550,7 @@ local edit_ns = api.nvim_create_namespace('treesitter/dev-edit')
local function update_editor_highlights(query_win, base_win, lang)
local base_buf = api.nvim_win_get_buf(base_win)
local query_buf = api.nvim_win_get_buf(query_win)
- local parser = vim.treesitter.get_parser(base_buf, lang)
+ local parser = assert(vim.treesitter.get_parser(base_buf, lang, { error = false }))
api.nvim_buf_clear_namespace(base_buf, edit_ns, 0, -1)
local query_content = table.concat(api.nvim_buf_get_lines(query_buf, 0, -1, false), '\n')
@@ -554,6 +585,8 @@ end
--- @private
--- @param lang? string language to open the query editor for.
+--- @return boolean? `true` on success, `nil` on failure
+--- @return string? error message, if applicable
function M.edit_query(lang)
local buf = api.nvim_get_current_buf()
local win = api.nvim_get_current_win()
@@ -576,9 +609,10 @@ function M.edit_query(lang)
end
vim.cmd(cmd)
- local ok, parser = pcall(vim.treesitter.get_parser, buf, lang)
- if not ok then
- return nil, 'No parser available for the given buffer'
+ local parser = vim.treesitter.get_parser(buf, lang, { error = false })
+ if not parser then
+ return nil,
+ string.format('Failed to show query editor for buffer %s: no parser for lang "%s"', buf, lang)
end
lang = parser:lang()
@@ -587,7 +621,7 @@ function M.edit_query(lang)
vim.b[buf].dev_edit = query_win
vim.bo[query_buf].omnifunc = 'v:lua.vim.treesitter.query.omnifunc'
- set_dev_properties(query_win, query_buf)
+ set_dev_options(query_win, query_buf)
-- Note that omnifunc guesses the language based on the containing folder,
-- so we add the parser's language to the buffer's name so that omnifunc
@@ -652,6 +686,8 @@ function M.edit_query(lang)
})
vim.cmd('normal! G')
vim.cmd.startinsert()
+
+ return true
end
return M
diff --git a/runtime/lua/vim/treesitter/health.lua b/runtime/lua/vim/treesitter/health.lua
index ed3616ef46..637f9ea543 100644
--- a/runtime/lua/vim/treesitter/health.lua
+++ b/runtime/lua/vim/treesitter/health.lua
@@ -28,6 +28,9 @@ function M.check()
)
end
end
+
+ local can_wasm = vim._ts_add_language_from_wasm ~= nil
+ health.info(string.format('Can load WASM parsers: %s', tostring(can_wasm)))
end
return M
diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua
index d2f986b874..a94c408f4e 100644
--- a/runtime/lua/vim/treesitter/highlighter.lua
+++ b/runtime/lua/vim/treesitter/highlighter.lua
@@ -47,7 +47,7 @@ function TSHighlighterQuery:get_hl_from_capture(capture)
return self.hl_cache[capture]
end
----@package
+---@nodoc
function TSHighlighterQuery:query()
return self._query
end
@@ -75,7 +75,7 @@ local TSHighlighter = {
TSHighlighter.__index = TSHighlighter
----@package
+---@nodoc
---
--- Creates a highlighter for `tree`.
---
@@ -139,11 +139,14 @@ function TSHighlighter.new(tree, opts)
-- but use synload.vim rather than syntax.vim to not enable
-- syntax FileType autocmds. Later on we should integrate with the
-- `:syntax` and `set syntax=...` machinery properly.
+ -- Still need to ensure that syntaxset augroup exists, so that calling :destroy()
+ -- immediately afterwards will not error.
if vim.g.syntax_on ~= 1 then
vim.cmd.runtime({ 'syntax/synload.vim', bang = true })
+ vim.api.nvim_create_augroup('syntaxset', { clear = false })
end
- api.nvim_buf_call(self.bufnr, function()
+ vim._with({ buf = self.bufnr }, function()
vim.opt_local.spelloptions:append('noplainbuffer')
end)
@@ -232,7 +235,7 @@ function TSHighlighter:on_changedtree(changes)
end
--- Gets the query used for @param lang
----@package
+---@nodoc
---@param lang string Language used by the highlighter.
---@return vim.treesitter.highlighter.Query
function TSHighlighter:get_query(lang)
@@ -377,11 +380,15 @@ function TSHighlighter._on_spell_nav(_, _, buf, srow, _, erow, _)
return
end
+ -- Do not affect potentially populated highlight state. Here we just want a temporary
+ -- empty state so the C code can detect whether the region should be spell checked.
+ local highlight_states = self._highlight_states
self:prepare_highlight_states(srow, erow)
for row = srow, erow do
on_line_impl(self, buf, row, true)
end
+ self._highlight_states = highlight_states
end
---@private
diff --git a/runtime/lua/vim/treesitter/language.lua b/runtime/lua/vim/treesitter/language.lua
index d0a74daa6c..9f7807e036 100644
--- a/runtime/lua/vim/treesitter/language.lua
+++ b/runtime/lua/vim/treesitter/language.lua
@@ -7,11 +7,15 @@ local ft_to_lang = {
help = 'vimdoc',
}
---- Get the filetypes associated with the parser named {lang}.
+--- Returns the filetypes for which a parser named {lang} is used.
+---
+--- The list includes {lang} itself plus all filetypes registered via
+--- |vim.treesitter.language.register()|.
+---
--- @param lang string Name of parser
--- @return string[] filetypes
function M.get_filetypes(lang)
- local r = {} ---@type string[]
+ local r = { lang } ---@type string[]
for ft, p in pairs(ft_to_lang) do
if p == lang then
r[#r + 1] = ft
@@ -20,6 +24,12 @@ function M.get_filetypes(lang)
return r
end
+--- Returns the language name to be used when loading a parser for {filetype}.
+---
+--- If no language has been explicitly registered via |vim.treesitter.language.register()|,
+--- default to {filetype}. For composite filetypes like `html.glimmer`, only the main filetype is
+--- returned.
+---
--- @param filetype string
--- @return string|nil
function M.get_lang(filetype)
@@ -29,9 +39,9 @@ function M.get_lang(filetype)
if ft_to_lang[filetype] then
return ft_to_lang[filetype]
end
- -- support subfiletypes like html.glimmer
+ -- for subfiletypes like html.glimmer use only "main" filetype
filetype = vim.split(filetype, '.', { plain = true })[1]
- return ft_to_lang[filetype]
+ return ft_to_lang[filetype] or filetype
end
---@deprecated
@@ -52,17 +62,27 @@ function M.require_language(lang, path, silent, symbol_name)
return installed
end
- M.add(lang, opts)
- return true
+ return M.add(lang, opts)
+end
+
+--- Load wasm or native parser (wrapper)
+--- todo(clason): move to C
+---
+---@param path string Path of parser library
+---@param lang string Language name
+---@param symbol_name? string Internal symbol name for the language to load (default lang)
+---@return boolean? True if parser is loaded
+local function loadparser(path, lang, symbol_name)
+ if vim.endswith(path, '.wasm') then
+ return vim._ts_add_language_from_wasm and vim._ts_add_language_from_wasm(path, lang)
+ else
+ return vim._ts_add_language_from_object(path, lang, symbol_name)
+ end
end
---@class vim.treesitter.language.add.Opts
---@inlinedoc
---
----Default filetype the parser should be associated with.
----(Default: {lang})
----@field filetype? string|string[]
----
---Optional path the parser is located at
---@field path? string
---
@@ -71,46 +91,52 @@ end
--- Load parser with name {lang}
---
---- Parsers are searched in the `parser` runtime directory, or the provided {path}
+--- Parsers are searched in the `parser` runtime directory, or the provided {path}.
+--- Can be used to check for available parsers before enabling treesitter features, e.g.,
+--- ```lua
+--- if vim.treesitter.language.add('markdown') then
+--- vim.treesitter.start(bufnr, 'markdown')
+--- end
+--- ```
---
---@param lang string Name of the parser (alphanumerical and `_` only)
---@param opts? vim.treesitter.language.add.Opts Options:
+---@return boolean? True if parser is loaded
+---@return string? Error if parser cannot be loaded
function M.add(lang, opts)
opts = opts or {}
local path = opts.path
- local filetype = opts.filetype or lang
local symbol_name = opts.symbol_name
vim.validate({
lang = { lang, 'string' },
path = { path, 'string', true },
symbol_name = { symbol_name, 'string', true },
- filetype = { filetype, { 'string', 'table' }, true },
})
-- parser names are assumed to be lowercase (consistent behavior on case-insensitive file systems)
lang = lang:lower()
if vim._ts_has_language(lang) then
- M.register(lang, filetype)
- return
+ return true
end
if path == nil then
+ -- allow only safe language names when looking for libraries to load
if not (lang and lang:match('[%w_]+') == lang) then
- error("'" .. lang .. "' is not a valid language name")
+ return nil, string.format('Invalid language name "%s"', lang)
end
local fname = 'parser/' .. lang .. '.*'
local paths = api.nvim_get_runtime_file(fname, false)
if #paths == 0 then
- error("no parser for '" .. lang .. "' language, see :help treesitter-parsers")
+ return nil, string.format('No parser for language "%s"', lang)
end
path = paths[1]
end
- vim._ts_add_language(path, lang, symbol_name)
- M.register(lang, filetype)
+ return loadparser(path, lang, symbol_name) or nil,
+ string.format('Cannot load parser %s for language "%s"', path, lang)
end
--- @param x string|string[]
diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua
index b0812123b9..fd68c2b910 100644
--- a/runtime/lua/vim/treesitter/languagetree.lua
+++ b/runtime/lua/vim/treesitter/languagetree.lua
@@ -98,9 +98,9 @@ local LanguageTree = {}
LanguageTree.__index = LanguageTree
---- @package
+--- @nodoc
---
---- |LanguageTree| contains a tree of parsers: the root treesitter parser for {lang} and any
+--- LanguageTree contains a tree of parsers: the root treesitter parser for {lang} and any
--- "injected" language parsers, which themselves may inject other languages, recursively.
---
---@param source (integer|string) Buffer or text string to parse
@@ -108,7 +108,7 @@ LanguageTree.__index = LanguageTree
---@param opts vim.treesitter.LanguageTree.new.Opts?
---@return vim.treesitter.LanguageTree parser object
function LanguageTree.new(source, lang, opts)
- language.add(lang)
+ assert(language.add(lang))
opts = opts or {}
if source == 0 then
@@ -638,6 +638,8 @@ end
---Gets the set of included regions managed by this LanguageTree. This can be different from the
---regions set by injection query, because a partial |LanguageTree:parse()| drops the regions
---outside the requested range.
+---Each list represents a range in the form of
+---{ {start_row}, {start_col}, {start_bytes}, {end_row}, {end_col}, {end_bytes} }.
---@return table<integer, Range6[]>
function LanguageTree:included_regions()
if self._regions then
@@ -732,7 +734,7 @@ local function add_injection(t, tree_index, pattern, lang, combined, ranges)
table.insert(t[tree_index][lang][pattern].regions, ranges)
end
--- TODO(clason): replace by refactored `ts.has_parser` API (without registering)
+-- TODO(clason): replace by refactored `ts.has_parser` API (without side effects)
--- The result of this function is cached to prevent nvim_get_runtime_file from being
--- called too often
--- @param lang string parser name
@@ -831,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
@@ -951,7 +947,7 @@ function LanguageTree:_edit(
end
end
----@package
+---@nodoc
---@param bufnr integer
---@param changed_tick integer
---@param start_row integer
@@ -1023,12 +1019,12 @@ function LanguageTree:_on_bytes(
)
end
----@package
+---@nodoc
function LanguageTree:_on_reload()
self:invalidate(true)
end
----@package
+---@nodoc
function LanguageTree:_on_detach(...)
self:invalidate(true)
self:_do_callback('detach', ...)
@@ -1087,7 +1083,7 @@ end
--- Determines whether {range} is contained in the |LanguageTree|.
---
----@param range Range4 `{ start_line, start_col, end_line, end_col }`
+---@param range Range4
---@return boolean
function LanguageTree:contains(range)
for _, tree in pairs(self._trees) do
@@ -1108,7 +1104,7 @@ end
--- Gets the tree that contains {range}.
---
----@param range Range4 `{ start_line, start_col, end_line, end_col }`
+---@param range Range4
---@param opts? vim.treesitter.LanguageTree.tree_for_range.Opts
---@return TSTree?
function LanguageTree:tree_for_range(range, opts)
@@ -1133,9 +1129,21 @@ function LanguageTree:tree_for_range(range, opts)
return nil
end
+--- Gets the smallest node that contains {range}.
+---
+---@param range Range4
+---@param opts? vim.treesitter.LanguageTree.tree_for_range.Opts
+---@return TSNode?
+function LanguageTree:node_for_range(range, opts)
+ local tree = self:tree_for_range(range, opts)
+ if tree then
+ return tree:root():descendant_for_range(unpack(range))
+ end
+end
+
--- Gets the smallest named node that contains {range}.
---
----@param range Range4 `{ start_line, start_col, end_line, end_col }`
+---@param range Range4
---@param opts? vim.treesitter.LanguageTree.tree_for_range.Opts
---@return TSNode?
function LanguageTree:named_node_for_range(range, opts)
@@ -1147,7 +1155,7 @@ end
--- Gets the appropriate language that contains {range}.
---
----@param range Range4 `{ start_line, start_col, end_line, end_col }`
+---@param range Range4
---@return vim.treesitter.LanguageTree tree Managing {range}
function LanguageTree:language_for_range(range)
for _, child in pairs(self._children) do
diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua
index ef5c2143a7..4614967799 100644
--- a/runtime/lua/vim/treesitter/query.lua
+++ b/runtime/lua/vim/treesitter/query.lua
@@ -247,8 +247,7 @@ end)
---
---@see [vim.treesitter.query.get()]
M.parse = memoize('concat-2', function(lang, query)
- language.add(lang)
-
+ assert(language.add(lang))
local ts_query = vim._ts_parse_query(lang, query)
return Query.new(lang, ts_query)
end)
@@ -487,8 +486,8 @@ predicate_handlers['any-vim-match?'] = predicate_handlers['any-match?']
---@class vim.treesitter.query.TSMetadata
---@field range? Range
---@field conceal? string
----@field [integer] vim.treesitter.query.TSMetadata
----@field [string] integer|string
+---@field [integer]? vim.treesitter.query.TSMetadata
+---@field [string]? integer|string
---@alias TSDirective fun(match: table<integer,TSNode[]>, _, _, predicate: (string|integer)[], metadata: vim.treesitter.query.TSMetadata)
@@ -620,16 +619,16 @@ 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
---
---@param name string Name of the predicate, without leading #
----@param handler fun(match: table<integer,TSNode[]>, pattern: integer, source: integer|string, predicate: any[], metadata: table)
+---@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 +641,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[]>
@@ -667,7 +666,7 @@ end
--- metadata table `metadata[capture_id].key = value`
---
---@param name string Name of the directive, without leading #
----@param handler fun(match: table<integer,TSNode[]>, pattern: integer, source: integer|string, predicate: any[], metadata: table)
+---@param handler fun(match: table<integer,TSNode[]>, pattern: integer, source: integer|string, predicate: any[], metadata: vim.treesitter.query.TSMetadata)
--- - match: A table mapping capture IDs to a list of captured nodes
--- - pattern: the index of the matching pattern in the query file
--- - predicate: list of strings containing the full directive being called, e.g.
@@ -894,16 +893,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,11 +918,11 @@ 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[]>, table): pattern id, match, metadata
+---@return (fun(): integer, table<integer, TSNode[]>, vim.treesitter.query.TSMetadata): pattern id, match, metadata
function Query:iter_matches(node, source, start, stop, opts)
opts = opts or {}
opts.match_limit = opts.match_limit or 256
@@ -960,10 +953,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]
@@ -1034,7 +1027,7 @@ end
---
--- @param lang? string language to open the query editor for. If omitted, inferred from the current buffer's filetype.
function M.edit(lang)
- vim.treesitter.dev.edit_query(lang)
+ assert(vim.treesitter.dev.edit_query(lang))
end
return M
diff --git a/runtime/lua/vim/ui.lua b/runtime/lua/vim/ui.lua
index 99b9b78e2a..532decf5e9 100644
--- a/runtime/lua/vim/ui.lua
+++ b/runtime/lua/vim/ui.lua
@@ -117,6 +117,8 @@ end
--- -- Asynchronous.
--- vim.ui.open("https://neovim.io/")
--- vim.ui.open("~/path/to/file")
+--- -- Use the "osurl" command to handle the path or URL.
+--- vim.ui.open("gh#neovim/neovim!29490", { cmd = { 'osurl' } })
--- -- Synchronous (wait until the process exits).
--- local cmd, err = vim.ui.open("$VIMRUNTIME")
--- if cmd then
@@ -125,23 +127,29 @@ end
--- ```
---
---@param path string Path or URL to open
+---@param opt? { cmd?: string[] } Options
+--- - cmd string[]|nil Command used to open the path or URL.
---
---@return vim.SystemObj|nil # Command object, or nil if not found.
---@return nil|string # Error message on failure, or nil on success.
---
---@see |vim.system()|
-function M.open(path)
+function M.open(path, opt)
vim.validate({
path = { path, 'string' },
})
local is_uri = path:match('%w+:')
if not is_uri then
- path = vim.fn.expand(path)
+ path = vim.fs.normalize(path)
end
- local cmd --- @type string[]
+ opt = opt or {}
+ local cmd ---@type string[]
+ local job_opt = { text = true, detach = true } --- @type vim.SystemOpts
- if vim.fn.has('mac') == 1 then
+ if opt.cmd then
+ cmd = vim.list_extend(opt.cmd --[[@as string[] ]], { path })
+ elseif vim.fn.has('mac') == 1 then
cmd = { 'open', path }
elseif vim.fn.has('win32') == 1 then
if vim.fn.executable('rundll32') == 1 then
@@ -149,37 +157,79 @@ function M.open(path)
else
return nil, 'vim.ui.open: rundll32 not found'
end
+ elseif vim.fn.executable('xdg-open') == 1 then
+ cmd = { 'xdg-open', path }
+ job_opt.stdout = false
+ job_opt.stderr = false
elseif vim.fn.executable('wslview') == 1 then
cmd = { 'wslview', path }
elseif vim.fn.executable('explorer.exe') == 1 then
cmd = { 'explorer.exe', path }
- elseif vim.fn.executable('xdg-open') == 1 then
- cmd = { 'xdg-open', path }
else
return nil, 'vim.ui.open: no handler found (tried: wslview, explorer.exe, xdg-open)'
end
- return vim.system(cmd, { text = true, detach = true }), nil
+ return vim.system(cmd, job_opt), nil
end
---- Gets the URL at cursor, if any.
-function M._get_url()
- if vim.bo.filetype == 'markdown' then
- local range = vim.api.nvim_win_get_cursor(0)
- vim.treesitter.get_parser():parse(range)
- -- marking the node as `markdown_inline` is required. Setting it to `markdown` does not
- -- work.
- local current_node = vim.treesitter.get_node { lang = 'markdown_inline' }
- while current_node do
- local type = current_node:type()
- if type == 'inline_link' or type == 'image' then
- local child = assert(current_node:named_child(1))
- return vim.treesitter.get_node_text(child, 0)
+--- Returns all URLs at cursor, if any.
+--- @return string[]
+function M._get_urls()
+ local urls = {} ---@type string[]
+
+ local bufnr = vim.api.nvim_get_current_buf()
+ local cursor = vim.api.nvim_win_get_cursor(0)
+ local row = cursor[1] - 1
+ local col = cursor[2]
+ local extmarks = vim.api.nvim_buf_get_extmarks(bufnr, -1, { row, col }, { row, col }, {
+ details = true,
+ type = 'highlight',
+ overlap = true,
+ })
+ for _, v in ipairs(extmarks) do
+ local details = v[4]
+ if details and details.url then
+ urls[#urls + 1] = details.url
+ end
+ end
+
+ local highlighter = vim.treesitter.highlighter.active[bufnr]
+ if highlighter then
+ local range = { row, col, row, col }
+ local ltree = highlighter.tree:language_for_range(range)
+ local lang = ltree:lang()
+ local query = vim.treesitter.query.get(lang, 'highlights')
+ if query then
+ 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 = metadata[url] })
+ end
+ end
+ end
+ end
+ end
end
- current_node = current_node:parent()
end
end
- return vim.fn.expand('<cfile>')
+
+ if #urls == 0 then
+ -- If all else fails, use the filename under the cursor
+ table.insert(
+ urls,
+ vim._with({ go = { isfname = vim.o.isfname .. ',@-@' } }, function()
+ return vim.fn.expand('<cfile>')
+ end)
+ )
+ end
+
+ return urls
end
return M
diff --git a/runtime/lua/vim/version.lua b/runtime/lua/vim/version.lua
index 0b149700b5..d64ef98d2d 100644
--- a/runtime/lua/vim/version.lua
+++ b/runtime/lua/vim/version.lua
@@ -174,6 +174,10 @@ function M._version(version, strict) -- Adapted from https://github.com/folke/la
version = version:match('%d[^ ]*')
end
+ if version == nil then
+ return nil
+ end
+
local prerel = version:match('%-([^+]*)')
local prerel_strict = version:match('%-([0-9A-Za-z-]*)')
if
@@ -272,6 +276,7 @@ end
--- ```
---
--- @see # https://github.com/npm/node-semver#ranges
+--- @since 11
---
--- @param spec string Version range "spec"
--- @return vim.VersionRange?
@@ -371,6 +376,7 @@ end
--- ```
---
--- @note Per semver, build metadata is ignored when comparing two otherwise-equivalent versions.
+--- @since 11
---
---@param v1 vim.Version|number[]|string Version object.
---@param v2 vim.Version|number[]|string Version to compare with `v1`.
@@ -388,6 +394,7 @@ function M.cmp(v1, v2)
end
---Returns `true` if the given versions are equal. See |vim.version.cmp()| for usage.
+---@since 11
---@param v1 vim.Version|number[]|string
---@param v2 vim.Version|number[]|string
---@return boolean
@@ -396,6 +403,7 @@ function M.eq(v1, v2)
end
---Returns `true` if `v1 <= v2`. See |vim.version.cmp()| for usage.
+---@since 12
---@param v1 vim.Version|number[]|string
---@param v2 vim.Version|number[]|string
---@return boolean
@@ -404,6 +412,7 @@ function M.le(v1, v2)
end
---Returns `true` if `v1 < v2`. See |vim.version.cmp()| for usage.
+---@since 11
---@param v1 vim.Version|number[]|string
---@param v2 vim.Version|number[]|string
---@return boolean
@@ -412,6 +421,7 @@ function M.lt(v1, v2)
end
---Returns `true` if `v1 >= v2`. See |vim.version.cmp()| for usage.
+---@since 12
---@param v1 vim.Version|number[]|string
---@param v2 vim.Version|number[]|string
---@return boolean
@@ -420,6 +430,7 @@ function M.ge(v1, v2)
end
---Returns `true` if `v1 > v2`. See |vim.version.cmp()| for usage.
+---@since 11
---@param v1 vim.Version|number[]|string
---@param v2 vim.Version|number[]|string
---@return boolean
@@ -434,7 +445,8 @@ end
--- { major = 1, minor = 0, patch = 1, prerelease = "rc1", build = "build.2" }
--- ```
---
---- @see # https://semver.org/spec/v2.0.0.html
+---@see # https://semver.org/spec/v2.0.0.html
+---@since 11
---
---@param version string Version string to parse.
---@param opts table|nil Optional keyword arguments:
diff --git a/runtime/lua/vim/vimhelp.lua b/runtime/lua/vim/vimhelp.lua
index 4af6866d48..5579cc0174 100644
--- a/runtime/lua/vim/vimhelp.lua
+++ b/runtime/lua/vim/vimhelp.lua
@@ -30,4 +30,42 @@ function M.highlight_groups(patterns)
vim.fn.setpos('.', save_cursor)
end
+--- Show a table of contents for the help buffer in a loclist
+function M.show_toc()
+ local bufnr = vim.api.nvim_get_current_buf()
+ local parser = assert(vim.treesitter.get_parser(bufnr, 'vimdoc', { error = false }))
+ local query = vim.treesitter.query.parse(
+ parser:lang(),
+ [[
+ (h1 (heading) @h1)
+ (h2 (heading) @h2)
+ (h3 (heading) @h3)
+ (column_heading (heading) @h4)
+ ]]
+ )
+ local root = parser:parse()[1]:root()
+ local headings = {}
+ for id, node, _, _ in query:iter_captures(root, bufnr) do
+ local text = vim.treesitter.get_node_text(node, bufnr)
+ local capture = query.captures[id]
+ local row, col = node:start()
+ -- only column_headings at col 1 are headings, otherwise it's code examples
+ local is_code = (capture == 'h4' and col > 0)
+ -- ignore tabular material
+ local is_table = (capture == 'h4' and (text:find('\t') or text:find(' ')))
+ -- ignore tag-only headings
+ local is_tag = node:child_count() == 1 and node:child(0):type() == 'tag'
+ if not (is_code or is_table or is_tag) then
+ table.insert(headings, {
+ bufnr = bufnr,
+ lnum = row + 1,
+ text = (capture == 'h3' or capture == 'h4') and '  ' .. text or text,
+ })
+ end
+ end
+ vim.fn.setloclist(0, headings, ' ')
+ vim.fn.setloclist(0, {}, 'a', { title = 'Help TOC' })
+ vim.cmd.lopen()
+end
+
return M