aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/lua/vim')
-rw-r--r--runtime/lua/vim/_defaults.lua483
-rw-r--r--runtime/lua/vim/_inspector.lua6
-rw-r--r--runtime/lua/vim/_meta/base64.lua4
-rw-r--r--runtime/lua/vim/_meta/builtin.lua25
-rw-r--r--runtime/lua/vim/_meta/diff.lua85
-rw-r--r--runtime/lua/vim/_meta/re.lua4
-rw-r--r--runtime/lua/vim/_meta/spell.lua6
-rw-r--r--runtime/lua/vim/_meta/vimfn.lua4
-rw-r--r--runtime/lua/vim/iter.lua100
-rw-r--r--runtime/lua/vim/lsp.lua100
-rw-r--r--runtime/lua/vim/lsp/client.lua14
-rw-r--r--runtime/lua/vim/lsp/inlay_hint.lua32
-rw-r--r--runtime/lua/vim/lsp/rpc.lua25
-rw-r--r--runtime/lua/vim/treesitter.lua2
-rw-r--r--runtime/lua/vim/treesitter/_fold.lua2
15 files changed, 488 insertions, 404 deletions
diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua
index 419a29a5c6..98dfbf3225 100644
--- a/runtime/lua/vim/_defaults.lua
+++ b/runtime/lua/vim/_defaults.lua
@@ -127,7 +127,9 @@ do
end, { desc = gx_desc })
end
- --- Default maps for built-in commenting
+ --- Default maps for built-in commenting.
+ ---
+ --- See |gc-default| and |gcc-default|.
do
local operator_rhs = function()
return require('vim._comment').operator()
@@ -144,6 +146,60 @@ do
end
vim.keymap.set({ 'o' }, 'gc', textobject_rhs, { desc = 'Comment textobject' })
end
+
+ --- Default maps for LSP functions.
+ ---
+ --- These are mapped unconditionally to avoid confusion. If no server is attached, or if a server
+ --- does not support a capability, an error message is displayed rather than exhibiting different
+ --- behavior.
+ ---
+ --- See |gr-default|, |crn|, |crr|, |i_CTRL-S|.
+ do
+ vim.keymap.set('n', 'crn', function()
+ vim.lsp.buf.rename()
+ end, { desc = 'vim.lsp.buf.rename()' })
+
+ vim.keymap.set({ 'n', 'v' }, 'crr', function()
+ vim.lsp.buf.code_action()
+ end, { desc = 'vim.lsp.buf.code_action()' })
+
+ vim.keymap.set('n', 'gr', function()
+ vim.lsp.buf.references()
+ end, { desc = 'vim.lsp.buf.references()' })
+
+ vim.keymap.set('i', '<C-S>', function()
+ vim.lsp.buf.signature_help()
+ end, { desc = 'vim.lsp.buf.signature_help()' })
+ end
+
+ --- Map [d and ]d to move to the previous/next diagnostic. Map <C-W>d to open a floating window
+ --- for the diagnostic under the cursor.
+ ---
+ --- 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 with the highest severity',
+ })
+
+ vim.keymap.set('n', '[d', function()
+ vim.diagnostic.goto_prev({ float = false })
+ end, {
+ desc = 'Jump to the previous diagnostic with the highest severity',
+ })
+
+ vim.keymap.set('n', '<C-W>d', function()
+ vim.diagnostic.open_float({ border = 'rounded' })
+ end, {
+ desc = 'Open a floating window showing diagnostics under the cursor',
+ })
+
+ vim.keymap.set('n', '<C-W><C-D>', '<C-W>d', {
+ remap = true,
+ desc = 'Open a floating window showing diagnostics under the cursor',
+ })
+ end
end
--- Default menus
@@ -196,6 +252,7 @@ do
group = nvim_terminal_augroup,
desc = 'Respond to OSC foreground/background color requests',
callback = function(args)
+ --- @type integer
local channel = vim.bo[args.buf].channel
if channel == 0 then
return
@@ -242,229 +299,140 @@ do
vim.notify(('W325: Ignoring swapfile from Nvim process %d'):format(info.pid))
end,
})
-end
--- Only do the following when the TUI is attached
-local tty = nil
-for _, ui in ipairs(vim.api.nvim_list_uis()) do
- if ui.chan == 1 and ui.stdout_tty then
- tty = ui
- break
- end
-end
-
-if tty then
- local group = vim.api.nvim_create_augroup('nvim_tty', {})
-
- --- Set an option after startup (so that OptionSet is fired), but only if not
- --- already set by the user.
- ---
- --- @param option string Option name
- --- @param value any Option value
- local function setoption(option, value)
- if vim.api.nvim_get_option_info2(option, {}).was_set then
- -- Don't do anything if option is already set
- return
- end
-
- -- Wait until Nvim is finished starting to set the option to ensure the
- -- OptionSet event fires.
- if vim.v.vim_did_enter == 1 then
- vim.o[option] = value
- else
- vim.api.nvim_create_autocmd('VimEnter', {
- group = group,
- once = true,
- nested = true,
- callback = function()
- setoption(option, value)
- end,
- })
+ -- Only do the following when the TUI is attached
+ local tty = nil
+ for _, ui in ipairs(vim.api.nvim_list_uis()) do
+ if ui.chan == 1 and ui.stdout_tty then
+ tty = ui
+ break
end
end
- --- Guess value of 'background' based on terminal color.
- ---
- --- We write Operating System Command (OSC) 11 to the terminal to request the
- --- terminal's background color. We then wait for a response. If the response
- --- matches `rgba:RRRR/GGGG/BBBB/AAAA` where R, G, B, and A are hex digits, then
- --- compute the luminance[1] of the RGB color and classify it as light/dark
- --- accordingly. Note that the color components may have anywhere from one to
- --- four hex digits, and require scaling accordingly as values out of 4, 8, 12,
- --- or 16 bits. Also note the A(lpha) component is optional, and is parsed but
- --- ignored in the calculations.
- ---
- --- [1] https://en.wikipedia.org/wiki/Luma_%28video%29
- do
- --- Parse a string of hex characters as a color.
- ---
- --- The string can contain 1 to 4 hex characters. The returned value is
- --- between 0.0 and 1.0 (inclusive) representing the intensity of the color.
- ---
- --- For instance, if only a single hex char "a" is used, then this function
- --- returns 0.625 (10 / 16), while a value of "aa" would return 0.664 (170 /
- --- 256).
+ if tty then
+ local group = vim.api.nvim_create_augroup('nvim_tty', {})
+
+ --- Set an option after startup (so that OptionSet is fired), but only if not
+ --- already set by the user.
---
- --- @param c string Color as a string of hex chars
- --- @return number? Intensity of the color
- local function parsecolor(c)
- if #c == 0 or #c > 4 then
- return nil
+ --- @param option string Option name
+ --- @param value any Option value
+ local function setoption(option, value)
+ if vim.api.nvim_get_option_info2(option, {}).was_set then
+ -- Don't do anything if option is already set
+ return
end
- local val = tonumber(c, 16)
- if not val then
- return nil
+ -- Wait until Nvim is finished starting to set the option to ensure the
+ -- OptionSet event fires.
+ if vim.v.vim_did_enter == 1 then
+ --- @diagnostic disable-next-line:no-unknown
+ vim.o[option] = value
+ else
+ vim.api.nvim_create_autocmd('VimEnter', {
+ group = group,
+ once = true,
+ nested = true,
+ callback = function()
+ setoption(option, value)
+ end,
+ })
end
-
- local max = tonumber(string.rep('f', #c), 16)
- return val / max
end
- --- Parse an OSC 11 response
- ---
- --- Either of the two formats below are accepted:
- ---
- --- OSC 11 ; rgb:<red>/<green>/<blue>
- ---
- --- or
- ---
- --- OSC 11 ; rgba:<red>/<green>/<blue>/<alpha>
+ --- Guess value of 'background' based on terminal color.
---
- --- where
+ --- We write Operating System Command (OSC) 11 to the terminal to request the
+ --- terminal's background color. We then wait for a response. If the response
+ --- matches `rgba:RRRR/GGGG/BBBB/AAAA` where R, G, B, and A are hex digits, then
+ --- compute the luminance[1] of the RGB color and classify it as light/dark
+ --- accordingly. Note that the color components may have anywhere from one to
+ --- four hex digits, and require scaling accordingly as values out of 4, 8, 12,
+ --- or 16 bits. Also note the A(lpha) component is optional, and is parsed but
+ --- ignored in the calculations.
---
- --- <red>, <green>, <blue>, <alpha> := h | hh | hhh | hhhh
- ---
- --- The alpha component is ignored, if present.
- ---
- --- @param resp string OSC 11 response
- --- @return string? Red component
- --- @return string? Green component
- --- @return string? Blue component
- local function parseosc11(resp)
- local r, g, b
- r, g, b = resp:match('^\027%]11;rgb:(%x+)/(%x+)/(%x+)$')
- if not r and not g and not b then
- local a
- r, g, b, a = resp:match('^\027%]11;rgba:(%x+)/(%x+)/(%x+)/(%x+)$')
- if not a or #a > 4 then
- return nil, nil, nil
+ --- [1] https://en.wikipedia.org/wiki/Luma_%28video%29
+ do
+ --- Parse a string of hex characters as a color.
+ ---
+ --- The string can contain 1 to 4 hex characters. The returned value is
+ --- between 0.0 and 1.0 (inclusive) representing the intensity of the color.
+ ---
+ --- For instance, if only a single hex char "a" is used, then this function
+ --- returns 0.625 (10 / 16), while a value of "aa" would return 0.664 (170 /
+ --- 256).
+ ---
+ --- @param c string Color as a string of hex chars
+ --- @return number? Intensity of the color
+ local function parsecolor(c)
+ if #c == 0 or #c > 4 then
+ return nil
end
- end
-
- if r and g and b and #r <= 4 and #g <= 4 and #b <= 4 then
- return r, g, b
- end
-
- return nil, nil, nil
- end
-
- local timer = assert(vim.uv.new_timer())
-
- local id = vim.api.nvim_create_autocmd('TermResponse', {
- group = group,
- nested = true,
- callback = function(args)
- local resp = args.data ---@type string
- local r, g, b = parseosc11(resp)
- if r and g and b then
- local rr = parsecolor(r)
- local gg = parsecolor(g)
- local bb = parsecolor(b)
-
- if rr and gg and bb then
- local luminance = (0.299 * rr) + (0.587 * gg) + (0.114 * bb)
- local bg = luminance < 0.5 and 'dark' or 'light'
- setoption('background', bg)
- end
- return true
+ local val = tonumber(c, 16)
+ if not val then
+ return nil
end
- end,
- })
-
- io.stdout:write('\027]11;?\007')
- timer:start(1000, 0, function()
- -- Delete the autocommand if no response was received
- vim.schedule(function()
- -- Suppress error if autocommand has already been deleted
- pcall(vim.api.nvim_del_autocmd, id)
- end)
-
- if not timer:is_closing() then
- timer:close()
+ local max = tonumber(string.rep('f', #c), 16)
+ return val / max
end
- end)
- end
- --- If the TUI (term_has_truecolor) was able to determine that the host
- --- terminal supports truecolor, enable 'termguicolors'. Otherwise, query the
- --- terminal (using both XTGETTCAP and SGR + DECRQSS). If the terminal's
- --- 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
- setoption('termguicolors', true)
- else
- local caps = {} ---@type table<string, boolean>
- require('vim.termcap').query({ 'Tc', 'RGB', 'setrgbf', 'setrgbb' }, function(cap, found)
- if not found then
- return
+ --- Parse an OSC 11 response
+ ---
+ --- Either of the two formats below are accepted:
+ ---
+ --- OSC 11 ; rgb:<red>/<green>/<blue>
+ ---
+ --- or
+ ---
+ --- OSC 11 ; rgba:<red>/<green>/<blue>/<alpha>
+ ---
+ --- where
+ ---
+ --- <red>, <green>, <blue>, <alpha> := h | hh | hhh | hhhh
+ ---
+ --- The alpha component is ignored, if present.
+ ---
+ --- @param resp string OSC 11 response
+ --- @return string? Red component
+ --- @return string? Green component
+ --- @return string? Blue component
+ local function parseosc11(resp)
+ local r, g, b
+ r, g, b = resp:match('^\027%]11;rgb:(%x+)/(%x+)/(%x+)$')
+ if not r and not g and not b then
+ local a
+ r, g, b, a = resp:match('^\027%]11;rgba:(%x+)/(%x+)/(%x+)/(%x+)$')
+ if not a or #a > 4 then
+ return nil, nil, nil
+ end
end
- caps[cap] = true
- if caps.Tc or caps.RGB or (caps.setrgbf and caps.setrgbb) then
- setoption('termguicolors', true)
+ if r and g and b and #r <= 4 and #g <= 4 and #b <= 4 then
+ return r, g, b
end
- end)
- local timer = assert(vim.uv.new_timer())
+ return nil, nil, nil
+ end
- -- Arbitrary colors to set in the SGR sequence
- local r = 1
- local g = 2
- local b = 3
+ local timer = assert(vim.uv.new_timer())
local id = vim.api.nvim_create_autocmd('TermResponse', {
group = group,
nested = true,
callback = function(args)
local resp = args.data ---@type string
- local decrqss = resp:match('^\027P1%$r([%d;:]+)m$')
-
- if decrqss then
- -- The DECRQSS SGR response first contains attributes separated by
- -- semicolons, followed by the SGR itself with parameters separated
- -- by colons. Some terminals include "0" in the attribute list
- -- unconditionally; others do not. Our SGR sequence did not set any
- -- attributes, so there should be no attributes in the list.
- local attrs = vim.split(decrqss, ';')
- if #attrs ~= 1 and (#attrs ~= 2 or attrs[1] ~= '0') then
- return false
- end
-
- -- The returned SGR sequence should begin with 48:2
- local sgr = attrs[#attrs]:match('^48:2:([%d:]+)$')
- if not sgr then
- return false
- end
-
- -- The remaining elements of the SGR sequence should be the 3 colors
- -- we set. Some terminals also include an additional parameter
- -- (which can even be empty!), so handle those cases as well
- local params = vim.split(sgr, ':')
- if #params ~= 3 and (#params ~= 4 or (params[1] ~= '' and params[1] ~= '1')) then
- return true
- end
-
- if
- tonumber(params[#params - 2]) == r
- and tonumber(params[#params - 1]) == g
- and tonumber(params[#params]) == b
- then
- setoption('termguicolors', true)
+ local r, g, b = parseosc11(resp)
+ if r and g and b then
+ local rr = parsecolor(r)
+ local gg = parsecolor(g)
+ local bb = parsecolor(b)
+
+ if rr and gg and bb then
+ local luminance = (0.299 * rr) + (0.587 * gg) + (0.114 * bb)
+ local bg = luminance < 0.5 and 'dark' or 'light'
+ setoption('background', bg)
end
return true
@@ -472,16 +440,7 @@ if tty then
end,
})
- -- Write SGR followed by DECRQSS. This sets the background color then
- -- immediately asks the terminal what the background color is. If the
- -- terminal responds to the DECRQSS with the same SGR sequence that we
- -- sent then the terminal supports truecolor.
- local decrqss = '\027P$qm\027\\'
- if os.getenv('TMUX') then
- decrqss = string.format('\027Ptmux;%s\027\\', decrqss:gsub('\027', '\027\027'))
- end
- -- Reset attributes first, as other code may have set attributes.
- io.stdout:write(string.format('\027[0m\027[48;2;%d;%d;%dm%s', r, g, b, decrqss))
+ io.stdout:write('\027]11;?\007')
timer:start(1000, 0, function()
-- Delete the autocommand if no response was received
@@ -495,13 +454,115 @@ if tty then
end
end)
end
+
+ --- If the TUI (term_has_truecolor) was able to determine that the host
+ --- terminal supports truecolor, enable 'termguicolors'. Otherwise, query the
+ --- terminal (using both XTGETTCAP and SGR + DECRQSS). If the terminal's
+ --- 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
+ setoption('termguicolors', true)
+ else
+ local caps = {} ---@type table<string, boolean>
+ require('vim.termcap').query({ 'Tc', 'RGB', 'setrgbf', 'setrgbb' }, function(cap, found)
+ if not found then
+ return
+ end
+
+ caps[cap] = true
+ if caps.Tc or caps.RGB or (caps.setrgbf and caps.setrgbb) then
+ setoption('termguicolors', true)
+ end
+ end)
+
+ local timer = assert(vim.uv.new_timer())
+
+ -- Arbitrary colors to set in the SGR sequence
+ local r = 1
+ local g = 2
+ local b = 3
+
+ local id = vim.api.nvim_create_autocmd('TermResponse', {
+ group = group,
+ nested = true,
+ callback = function(args)
+ local resp = args.data ---@type string
+ local decrqss = resp:match('^\027P1%$r([%d;:]+)m$')
+
+ if decrqss then
+ -- The DECRQSS SGR response first contains attributes separated by
+ -- semicolons, followed by the SGR itself with parameters separated
+ -- by colons. Some terminals include "0" in the attribute list
+ -- unconditionally; others do not. Our SGR sequence did not set any
+ -- attributes, so there should be no attributes in the list.
+ local attrs = vim.split(decrqss, ';')
+ if #attrs ~= 1 and (#attrs ~= 2 or attrs[1] ~= '0') then
+ return false
+ end
+
+ -- The returned SGR sequence should begin with 48:2
+ local sgr = attrs[#attrs]:match('^48:2:([%d:]+)$')
+ if not sgr then
+ return false
+ end
+
+ -- The remaining elements of the SGR sequence should be the 3 colors
+ -- we set. Some terminals also include an additional parameter
+ -- (which can even be empty!), so handle those cases as well
+ local params = vim.split(sgr, ':')
+ if #params ~= 3 and (#params ~= 4 or (params[1] ~= '' and params[1] ~= '1')) then
+ return true
+ end
+
+ if
+ tonumber(params[#params - 2]) == r
+ and tonumber(params[#params - 1]) == g
+ and tonumber(params[#params]) == b
+ then
+ setoption('termguicolors', true)
+ end
+
+ return true
+ end
+ end,
+ })
+
+ -- Write SGR followed by DECRQSS. This sets the background color then
+ -- immediately asks the terminal what the background color is. If the
+ -- terminal responds to the DECRQSS with the same SGR sequence that we
+ -- sent then the terminal supports truecolor.
+ local decrqss = '\027P$qm\027\\'
+ if os.getenv('TMUX') then
+ decrqss = string.format('\027Ptmux;%s\027\\', decrqss:gsub('\027', '\027\027'))
+ end
+ -- Reset attributes first, as other code may have set attributes.
+ io.stdout:write(string.format('\027[0m\027[48;2;%d;%d;%dm%s', r, g, b, decrqss))
+
+ timer:start(1000, 0, function()
+ -- Delete the autocommand if no response was received
+ vim.schedule(function()
+ -- Suppress error if autocommand has already been deleted
+ pcall(vim.api.nvim_del_autocmd, id)
+ end)
+
+ if not timer:is_closing() then
+ timer:close()
+ end
+ end)
+ end
+ end
end
end
---- Default 'grepprg' to ripgrep if available.
-if vim.fn.executable('rg') == 1 then
- -- Match :grep default, otherwise rg searches cwd by default
- -- Use -uuu to make ripgrep not do its default filtering
- vim.o.grepprg = 'rg --vimgrep -uuu $* ' .. (vim.fn.has('unix') == 1 and '/dev/null' or 'nul')
- vim.o.grepformat = '%f:%l:%c:%m'
+--- Default options
+do
+ --- Default 'grepprg' to ripgrep if available.
+ if vim.fn.executable('rg') == 1 then
+ -- Match :grep default, otherwise rg searches cwd by default
+ -- Use -uuu to make ripgrep not do its default filtering
+ vim.o.grepprg = 'rg --vimgrep -uuu $* ' .. (vim.fn.has('unix') == 1 and '/dev/null' or 'nul')
+ vim.o.grepformat = '%f:%l:%c:%m'
+ end
end
diff --git a/runtime/lua/vim/_inspector.lua b/runtime/lua/vim/_inspector.lua
index afbd6211cd..f5d1640c82 100644
--- a/runtime/lua/vim/_inspector.lua
+++ b/runtime/lua/vim/_inspector.lua
@@ -55,8 +55,8 @@ function vim.inspect_pos(bufnr, row, col, filter)
bufnr = bufnr == 0 and vim.api.nvim_get_current_buf() or bufnr
local results = {
- treesitter = {},
- syntax = {},
+ treesitter = {}, --- @type table[]
+ syntax = {}, --- @type table[]
extmarks = {},
semantic_tokens = {},
buffer = bufnr,
@@ -93,7 +93,7 @@ function vim.inspect_pos(bufnr, row, col, filter)
end
-- namespace id -> name map
- local nsmap = {}
+ local nsmap = {} --- @type table<integer,string>
for name, id in pairs(vim.api.nvim_get_namespaces()) do
nsmap[id] = name
end
diff --git a/runtime/lua/vim/_meta/base64.lua b/runtime/lua/vim/_meta/base64.lua
index f25b4af234..8ba59e1703 100644
--- a/runtime/lua/vim/_meta/base64.lua
+++ b/runtime/lua/vim/_meta/base64.lua
@@ -3,11 +3,11 @@
--- Encode {str} using Base64.
---
--- @param str string String to encode
---- @return string Encoded string
+--- @return string : Encoded string
function vim.base64.encode(str) end
--- Decode a Base64 encoded string.
---
--- @param str string Base64 encoded string
---- @return string Decoded string
+--- @return string : Decoded string
function vim.base64.decode(str) end
diff --git a/runtime/lua/vim/_meta/builtin.lua b/runtime/lua/vim/_meta/builtin.lua
index 20b6d9dabe..75737bd040 100644
--- a/runtime/lua/vim/_meta/builtin.lua
+++ b/runtime/lua/vim/_meta/builtin.lua
@@ -119,15 +119,15 @@ function vim.stricmp(a, b) end
--- An {index} in the middle of a UTF-16 sequence is rounded upwards to
--- the end of that sequence.
--- @param str string
---- @param index number
---- @param use_utf16? any
+--- @param index integer
+--- @param use_utf16? boolean
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.
---
--- Embedded NUL bytes are treated as terminating the string.
--- @param str string
---- @return table
+--- @return integer[]
function vim.str_utf_pos(str) end
--- Gets the distance (in bytes) from the starting byte of the codepoint (character) that {index}
@@ -148,8 +148,8 @@ function vim.str_utf_pos(str) end
--- ```
---
--- @param str string
---- @param index number
---- @return number
+--- @param index integer
+--- @return integer
function vim.str_utf_start(str, index) end
--- Gets the distance (in bytes) from the last byte of the codepoint (character) that {index} points
@@ -168,8 +168,8 @@ function vim.str_utf_start(str, index) end
--- ```
---
--- @param str string
---- @param index number
---- @return number
+--- @param index integer
+--- @return integer
function vim.str_utf_end(str, index) end
--- Convert byte index to UTF-32 and UTF-16 indices. If {index} is not
@@ -180,7 +180,7 @@ function vim.str_utf_end(str, index) end
--- {index} in the middle of a UTF-8 sequence is rounded upwards to the end of
--- that sequence.
--- @param str string
---- @param index? number
+--- @param index? integer
--- @return integer UTF-32 index
--- @return integer UTF-16 index
function vim.str_utfindex(str, index) end
@@ -193,15 +193,14 @@ function vim.str_utfindex(str, index) end
--- can accept, see ":Man 3 iconv".
---
--- @param str string Text to convert
---- @param from number Encoding of {str}
---- @param to number Target encoding
---- @param opts? table<string,any>
---- @return string|nil Converted string if conversion succeeds, `nil` otherwise.
+--- @param from string Encoding of {str}
+--- @param to string Target encoding
+--- @return string? : Converted string if conversion succeeds, `nil` otherwise.
function vim.iconv(str, from, to, opts) end
--- Schedules {fn} to be invoked soon by the main event-loop. Useful
--- to avoid |textlock| or other temporary restrictions.
---- @param fn function
+--- @param fn fun()
function vim.schedule(fn) end
--- Wait for {time} in milliseconds until {callback} returns `true`.
diff --git a/runtime/lua/vim/_meta/diff.lua b/runtime/lua/vim/_meta/diff.lua
index f265139448..617bc87f59 100644
--- a/runtime/lua/vim/_meta/diff.lua
+++ b/runtime/lua/vim/_meta/diff.lua
@@ -1,5 +1,46 @@
---@meta
+--- Optional parameters:
+--- @class vim.diff.Opts
+--- @inlinedoc
+---
+--- Invoked for each hunk in the diff. Return a negative number
+--- to cancel the callback for any remaining hunks.
+--- Arguments:
+--- - `start_a` (`integer`): Start line of hunk in {a}.
+--- - `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
+---
+--- 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'
+---
+--- 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
+---
+--- Diff algorithm to use. Values:
+--- - `myers`: the default algorithm
+--- - `minimal`: spend extra time to generate the smallest possible diff
+--- - `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.
+
-- luacheck: no unused args
--- Run diff on strings {a} and {b}. Any indices returned by this function,
@@ -24,47 +65,7 @@
---
---@param a string First string to compare
---@param b string Second string to compare
----@param opts table<string,any> Optional parameters:
---- - `on_hunk` (callback):
---- Invoked for each hunk in the diff. Return a negative number
---- to cancel the callback for any remaining hunks.
---- Args:
---- - `start_a` (integer): Start line of hunk in {a}.
---- - `count_a` (integer): Hunk size in {a}.
---- - `start_b` (integer): Start line of hunk in {b}.
---- - `count_b` (integer): Hunk size in {b}.
---- - `result_type` (string): Form of the returned diff:
---- - "unified": (default) String in unified format.
---- - "indices": Array of hunk locations.
---- Note: This option is ignored if `on_hunk` is used.
---- - `linematch` (boolean|integer): 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.
---- - `algorithm` (string):
---- Diff algorithm to use. Values:
---- - "myers" the default algorithm
---- - "minimal" spend extra time to generate the
---- smallest possible diff
---- - "patience" patience diff algorithm
---- - "histogram" histogram diff algorithm
---- - `ctxlen` (integer): Context length
---- - `interhunkctxlen` (integer):
---- Inter hunk context length
---- - `ignore_whitespace` (boolean):
---- Ignore whitespace
---- - `ignore_whitespace_change` (boolean):
---- Ignore whitespace change
---- - `ignore_whitespace_change_at_eol` (boolean)
---- Ignore whitespace change at end-of-line.
---- - `ignore_cr_at_eol` (boolean)
---- Ignore carriage return at end-of-line
---- - `ignore_blank_lines` (boolean)
---- Ignore blank lines
---- - `indent_heuristic` (boolean):
---- Use the indent heuristic for the internal
---- diff library.
----
----@return string|table|nil
+---@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/re.lua b/runtime/lua/vim/_meta/re.lua
index 14c94c7824..9721e6f8c8 100644
--- a/runtime/lua/vim/_meta/re.lua
+++ b/runtime/lua/vim/_meta/re.lua
@@ -30,8 +30,8 @@ function vim.re.compile(string, defs) end
--- @param subject string
--- @param pattern vim.lpeg.Pattern|string
--- @param init? integer
---- @return integer|nil the index where the occurrence starts, nil if no match
---- @return integer|nil the index where the occurrence ends, nil if no match
+--- @return integer|nil : the index where the occurrence starts, nil if no match
+--- @return integer|nil : the index where the occurrence ends, nil if no match
function vim.re.find(subject, pattern, init) end
--- Does a global substitution, replacing all occurrences of {pattern} in the given {subject} by
diff --git a/runtime/lua/vim/_meta/spell.lua b/runtime/lua/vim/_meta/spell.lua
index 57f2180895..c636db3b53 100644
--- a/runtime/lua/vim/_meta/spell.lua
+++ b/runtime/lua/vim/_meta/spell.lua
@@ -3,11 +3,11 @@
-- luacheck: no unused args
--- Check {str} for spelling errors. Similar to the Vimscript function
---- |spellbadword()|.
+--- [spellbadword()].
---
--- Note: The behaviour of this function is dependent on: 'spelllang',
--- 'spellfile', 'spellcapcheck' and 'spelloptions' which can all be local to
---- the buffer. Consider calling this with |nvim_buf_call()|.
+--- the buffer. Consider calling this with [nvim_buf_call()].
---
--- Example:
---
@@ -20,7 +20,7 @@
--- ```
---
--- @param str string
---- @return {[1]: string, [2]: string, [3]: string}[]
+--- @return {[1]: string, [2]: 'bad'|'rare'|'local'|'caps', [3]: 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 d010673d24..02d5eaf575 100644
--- a/runtime/lua/vim/_meta/vimfn.lua
+++ b/runtime/lua/vim/_meta/vimfn.lua
@@ -9752,6 +9752,10 @@ function vim.fn.synIDtrans(synID) end
--- synconcealed(lnum, 5) [1, 'X', 2]
--- synconcealed(lnum, 6) [0, '', 0]
---
+--- Note: Doesn't consider |matchadd()| highlighting items,
+--- since syntax and matching highlighting are two different
+--- mechanisms |syntax-vs-match|.
+---
--- @param lnum integer
--- @param col integer
--- @return {[1]: integer, [2]: string, [3]: integer}
diff --git a/runtime/lua/vim/iter.lua b/runtime/lua/vim/iter.lua
index 302463b136..04137e18d4 100644
--- a/runtime/lua/vim/iter.lua
+++ b/runtime/lua/vim/iter.lua
@@ -630,7 +630,7 @@ function Iter:find(f)
return unpack(result)
end
---- Gets the first value in a |list-iterator| that satisfies a predicate, starting from the end.
+--- Gets the first value satisfying a predicate, from the end of a |list-iterator|.
---
--- Advances the iterator. Returns nil and drains the iterator if no value is found.
---
@@ -717,19 +717,19 @@ end
---
--- ```lua
--- local it = vim.iter({1, 2, 3, 4})
---- it:nextback()
+--- it:pop()
--- -- 4
---- it:nextback()
+--- it:pop()
--- -- 3
--- ```
---
---@return any
-function Iter:nextback()
- error('nextback() requires a list-like table')
+function Iter:pop()
+ error('pop() requires a list-like table')
end
--- @nodoc
-function ListIter:nextback()
+function ListIter:pop()
if self._head ~= self._tail then
local inc = self._head < self._tail and 1 or -1
self._tail = self._tail - inc
@@ -739,27 +739,27 @@ end
--- Gets the last value of a |list-iterator| without consuming it.
---
---- See also |Iter:last()|.
----
--- Example:
---
--- ```lua
--- local it = vim.iter({1, 2, 3, 4})
---- it:peekback()
+--- it:rpeek()
--- -- 4
---- it:peekback()
+--- it:rpeek()
--- -- 4
---- it:nextback()
+--- it:pop()
--- -- 4
--- ```
---
+---@see Iter.last
+---
---@return any
-function Iter:peekback()
- error('peekback() requires a list-like table')
+function Iter:rpeek()
+ error('rpeek() requires a list-like table')
end
---@nodoc
-function ListIter:peekback()
+function ListIter:rpeek()
if self._head ~= self._tail then
local inc = self._head < self._tail and 1 or -1
return self._table[self._tail - inc]
@@ -797,27 +797,27 @@ function ListIter:skip(n)
return self
end
---- Skips `n` values backwards from the end of a |list-iterator| pipeline.
+--- Discards `n` values from the end of a |list-iterator| pipeline.
---
--- Example:
---
--- ```lua
---- local it = vim.iter({ 1, 2, 3, 4, 5 }):skipback(2)
+--- local it = vim.iter({ 1, 2, 3, 4, 5 }):rskip(2)
--- it:next()
--- -- 1
---- it:nextback()
+--- it:pop()
--- -- 3
--- ```
---
---@param n number Number of values to skip.
---@return Iter
---@diagnostic disable-next-line: unused-local
-function Iter:skipback(n) -- luacheck: no unused args
- error('skipback() requires a list-like table')
+function Iter:rskip(n) -- luacheck: no unused args
+ error('rskip() requires a list-like table')
end
---@private
-function ListIter:skipback(n)
+function ListIter:rskip(n)
local inc = self._head < self._tail and n or -n
self._tail = self._tail - inc
if (inc > 0 and self._head > self._tail) or (inc < 0 and self._head < self._tail) then
@@ -828,51 +828,37 @@ end
--- Gets the nth value of an iterator (and advances to it).
---
+--- If `n` is negative, offsets from the end of a |list-iterator|.
+---
--- Example:
---
--- ```lua
----
--- local it = vim.iter({ 3, 6, 9, 12 })
--- it:nth(2)
--- -- 6
--- it:nth(2)
--- -- 12
---
---- ```
----
----@param n number The index of the value to return.
----@return any
-function Iter:nth(n)
- if n > 0 then
- return self:skip(n - 1):next()
- end
-end
-
---- Gets the nth value from the end of a |list-iterator| (and advances to it).
----
---- Example:
----
---- ```lua
----
---- local it = vim.iter({ 3, 6, 9, 12 })
---- it:nthback(2)
+--- local it2 = vim.iter({ 3, 6, 9, 12 })
+--- it2:nth(-2)
--- -- 9
---- it:nthback(2)
+--- it2:nth(-2)
--- -- 3
----
--- ```
---
----@param n number The index of the value to return.
+---@param n number Index of the value to return. May be negative if the source is a |list-iterator|.
---@return any
-function Iter:nthback(n)
+function Iter:nth(n)
if n > 0 then
- return self:skipback(n - 1):nextback()
+ return self:skip(n - 1):next()
+ elseif n < 0 then
+ return self:rskip(math.abs(n) - 1):pop()
end
end
--- Sets the start and end of a |list-iterator| pipeline.
---
---- Equivalent to `:skip(first - 1):skipback(len - last + 1)`.
+--- Equivalent to `:skip(first - 1):rskip(len - last + 1)`.
---
---@param first number
---@param last number
@@ -884,7 +870,7 @@ end
---@private
function ListIter:slice(first, last)
- return self:skip(math.max(0, first - 1)):skipback(math.max(0, self._tail - last - 1))
+ return self:skip(math.max(0, first - 1)):rskip(math.max(0, self._tail - last - 1))
end
--- Returns true if any of the items in the iterator match the given predicate.
@@ -950,6 +936,8 @@ end
---
--- ```
---
+---@see Iter.rpeek
+---
---@return any
function Iter:last()
local last = self:next()
@@ -1016,6 +1004,26 @@ function ListIter:enumerate()
return self
end
+---@deprecated
+function Iter:nextback()
+ error('Iter:nextback() was renamed to Iter:pop()')
+end
+
+---@deprecated
+function Iter:peekback()
+ error('Iter:peekback() was renamed to Iter:rpeek()')
+end
+
+---@deprecated
+function Iter:skipback()
+ error('Iter:skipback() was renamed to Iter:rskip()')
+end
+
+---@deprecated
+function Iter:nthback()
+ error('Iter:nthback() was removed, use Iter:nth() with negative index')
+end
+
--- Creates a new Iter object from a table or other |iterable|.
---
---@param src table|function Table or iterator to drain values from
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 8403aa0ee6..c6d6c8a0cd 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -195,10 +195,13 @@ 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: table): 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
+---
+--- Suppress error reporting if the LSP server fails to start (default false).
+--- @field silent? boolean
--- Create a new LSP client and start a language server or reuses an already
--- running client if one is found matching `name` and `root_dir`.
@@ -246,19 +249,25 @@ function lsp.start(config, opts)
for _, client in pairs(all_clients) do
if reuse_client(client, config) then
- lsp.buf_attach_client(bufnr, client.id)
- return client.id
+ if lsp.buf_attach_client(bufnr, client.id) then
+ return client.id
+ end
end
end
- local client_id = lsp.start_client(config)
+ local client_id, err = lsp.start_client(config)
+ if err then
+ if not opts.silent then
+ vim.notify(err, vim.log.levels.WARN)
+ end
+ return nil
+ end
- if not client_id then
- return -- lsp.start_client will have printed an error
+ if client_id and lsp.buf_attach_client(bufnr, client_id) then
+ return client_id
end
- lsp.buf_attach_client(bufnr, client_id)
- return client_id
+ return nil
end
--- Consumes the latest progress messages from all clients and formats them as a string.
@@ -339,7 +348,7 @@ function lsp._set_defaults(client, bufnr)
and is_empty_or_default(bufnr, 'keywordprg')
and vim.fn.maparg('K', 'n', false, false) == ''
then
- vim.keymap.set('n', 'K', vim.lsp.buf.hover, { buffer = bufnr })
+ vim.keymap.set('n', 'K', vim.lsp.buf.hover, { buffer = bufnr, desc = 'vim.lsp.buf.hover()' })
end
end)
if client.supports_method(ms.textDocument_diagnostic) then
@@ -420,16 +429,18 @@ end
--- Starts and initializes a client with the given configuration.
--- @param config vim.lsp.ClientConfig Configuration for the server.
---- @return integer|nil client_id |vim.lsp.get_client_by_id()| Note: client may not be
---- fully initialized. Use `on_init` to do any actions once
---- the client has been initialized.
+--- @return integer? client_id |vim.lsp.get_client_by_id()| Note: client may not be
+--- fully initialized. Use `on_init` to do any actions once
+--- the client has been initialized.
+--- @return string? # Error message, if any
function lsp.start_client(config)
- local client = require('vim.lsp.client').create(config)
-
- if not client then
- return
+ local ok, res = pcall(require('vim.lsp.client').create, config)
+ if not ok then
+ return nil, res --[[@as string]]
end
+ local client = assert(res)
+
--- @diagnostic disable-next-line: invisible
table.insert(client._on_exit_cbs, on_client_exit)
@@ -437,31 +448,7 @@ function lsp.start_client(config)
client:initialize()
- return client.id
-end
-
---- Notify all attached clients that a buffer has changed.
----@param _ integer
----@param bufnr integer
----@param changedtick integer
----@param firstline integer
----@param lastline integer
----@param new_lastline integer
----@return true?
-local function text_document_did_change_handler(
- _,
- bufnr,
- changedtick,
- firstline,
- lastline,
- new_lastline
-)
- -- Detach (nvim_buf_attach) via returning True to on_lines if no clients are attached
- if #lsp.get_clients({ bufnr = bufnr }) == 0 then
- return true
- end
- util.buf_versions[bufnr] = changedtick
- changetracking.send_changes(bufnr, firstline, lastline, new_lastline)
+ return client.id, nil
end
---Buffer lifecycle handler for textDocument/didSave
@@ -505,11 +492,18 @@ local function text_document_did_save_handler(bufnr)
end
end
+--- @type table<integer,true>
+local attached_buffers = {}
+
--- @param bufnr integer
---- @param client_id integer
-local function buf_attach(bufnr, client_id)
+local function buf_attach(bufnr)
+ if attached_buffers[bufnr] then
+ return
+ end
+ attached_buffers[bufnr] = true
+
local uri = vim.uri_from_bufnr(bufnr)
- local augroup = ('lsp_c_%d_b_%d_save'):format(client_id, bufnr)
+ local augroup = ('lsp_b_%d_save'):format(bufnr)
local group = api.nvim_create_augroup(augroup, { clear = true })
api.nvim_create_autocmd('BufWritePre', {
group = group,
@@ -548,7 +542,14 @@ local function buf_attach(bufnr, client_id)
})
-- First time, so attach and set up stuff.
api.nvim_buf_attach(bufnr, false, {
- on_lines = text_document_did_change_handler,
+ on_lines = function(_, _, changedtick, firstline, lastline, new_lastline)
+ if #lsp.get_clients({ bufnr = bufnr }) == 0 then
+ return true -- detach
+ end
+ util.buf_versions[bufnr] = changedtick
+ changetracking.send_changes(bufnr, firstline, lastline, new_lastline)
+ end,
+
on_reload = function()
local params = { textDocument = { uri = uri } }
for _, client in ipairs(lsp.get_clients({ bufnr = bufnr })) do
@@ -559,6 +560,7 @@ local function buf_attach(bufnr, client_id)
client:_text_document_did_open_handler(bufnr)
end
end,
+
on_detach = function()
local params = { textDocument = { uri = uri } }
for _, client in ipairs(lsp.get_clients({ bufnr = bufnr })) do
@@ -571,7 +573,9 @@ local function buf_attach(bufnr, client_id)
client.attached_buffers[bufnr] = nil
end
util.buf_versions[bufnr] = nil
+ attached_buffers[bufnr] = nil
end,
+
-- TODO if we know all of the potential clients ahead of time, then we
-- could conditionally set this.
-- utf_sizes = size_index > 1;
@@ -597,16 +601,14 @@ function lsp.buf_attach_client(bufnr, client_id)
log.warn(string.format('buf_attach_client called on unloaded buffer (id: %d): ', bufnr))
return false
end
- -- This is our first time attaching to this buffer.
- if #lsp.get_clients({ bufnr = bufnr, _uninitialized = true }) == 0 then
- buf_attach(bufnr, client_id)
- end
local client = lsp.get_client_by_id(client_id)
if not client then
return false
end
+ buf_attach(bufnr)
+
if client.attached_buffers[bufnr] then
return true
end
diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua
index 98b3cfc762..448f986cd3 100644
--- a/runtime/lua/vim/lsp/client.lua
+++ b/runtime/lua/vim/lsp/client.lua
@@ -37,7 +37,7 @@ local validate = vim.validate
--- `is_closing` and `terminate`.
--- See |vim.lsp.rpc.request()|, |vim.lsp.rpc.notify()|.
--- For TCP there is a builtin RPC client factory: |vim.lsp.rpc.connect()|
---- @field cmd string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient?
+--- @field cmd string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient
---
--- Directory to launch the `cmd` process. Not related to `root_dir`.
--- (default: cwd)
@@ -506,25 +506,17 @@ function Client.create(config)
}
-- Start the RPC client.
- local rpc --- @type vim.lsp.rpc.PublicClient?
local config_cmd = config.cmd
if type(config_cmd) == 'function' then
- rpc = config_cmd(dispatchers)
+ self.rpc = config_cmd(dispatchers)
else
- rpc = lsp.rpc.start(config_cmd, dispatchers, {
+ self.rpc = lsp.rpc.start(config_cmd, dispatchers, {
cwd = config.cmd_cwd,
env = config.cmd_env,
detached = config.detached,
})
end
- -- Return nil if the rpc client fails to start
- if not rpc then
- return
- end
-
- self.rpc = rpc
-
setmetatable(self, Client)
return self
diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua
index 37b330f5f6..3d8b54ee3d 100644
--- a/runtime/lua/vim/lsp/inlay_hint.lua
+++ b/runtime/lua/vim/lsp/inlay_hint.lua
@@ -41,13 +41,13 @@ function M.on_inlayhint(err, result, ctx, _)
bufstate.client_hints = vim.defaulttable()
bufstate.version = ctx.version
end
- local hints_by_client = bufstate.client_hints
+ local client_hints = bufstate.client_hints
local client = assert(vim.lsp.get_client_by_id(client_id))
- local new_hints_by_lnum = vim.defaulttable()
+ local new_lnum_hints = vim.defaulttable()
local num_unprocessed = #result
if num_unprocessed == 0 then
- hints_by_client[client_id] = {}
+ client_hints[client_id] = {}
bufstate.version = ctx.version
api.nvim__buf_redraw_range(bufnr, 0, -1)
return
@@ -73,10 +73,10 @@ function M.on_inlayhint(err, result, ctx, _)
for _, hint in ipairs(result) do
local lnum = hint.position.line
hint.position.character = pos_to_byte(hint.position)
- table.insert(new_hints_by_lnum[lnum], hint)
+ table.insert(new_lnum_hints[lnum], hint)
end
- hints_by_client[client_id] = new_hints_by_lnum
+ client_hints[client_id] = new_lnum_hints
bufstate.version = ctx.version
api.nvim__buf_redraw_range(bufnr, 0, -1)
end
@@ -175,19 +175,19 @@ function M.get(filter)
end
--- @type vim.lsp.inlay_hint.get.ret[]
- local hints = {}
+ local result = {}
for _, client in pairs(clients) do
- local hints_by_lnum = bufstate.client_hints[client.id]
- if hints_by_lnum then
+ local lnum_hints = bufstate.client_hints[client.id]
+ if lnum_hints then
for lnum = range.start.line, range['end'].line do
- local line_hints = hints_by_lnum[lnum] or {}
- for _, hint in pairs(line_hints) do
+ local hints = lnum_hints[lnum] or {}
+ for _, hint in pairs(hints) do
local line, char = hint.position.line, hint.position.character
if
(line > range.start.line or char >= range.start.character)
and (line < range['end'].line or char <= range['end'].character)
then
- table.insert(hints, {
+ table.insert(result, {
bufnr = bufnr,
client_id = client.id,
inlay_hint = hint,
@@ -197,7 +197,7 @@ function M.get(filter)
end
end
end
- return hints
+ return result
end
--- Clear inlay hints
@@ -315,14 +315,14 @@ api.nvim_set_decoration_provider(namespace, {
if not bufstate.client_hints then
return
end
- local hints_by_client = assert(bufstate.client_hints)
+ local client_hints = assert(bufstate.client_hints)
for lnum = topline, botline do
if bufstate.applied[lnum] ~= bufstate.version then
api.nvim_buf_clear_namespace(bufnr, namespace, lnum, lnum + 1)
- for _, hints_by_lnum in pairs(hints_by_client) do
- local line_hints = hints_by_lnum[lnum] or {}
- for _, hint in pairs(line_hints) do
+ for _, lnum_hints in pairs(client_hints) do
+ local hints = lnum_hints[lnum] or {}
+ for _, hint in pairs(hints) do
local text = ''
local label = hint.label
if type(label) == 'string' then
diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua
index 6748b32ec0..3c63a12da2 100644
--- a/runtime/lua/vim/lsp/rpc.lua
+++ b/runtime/lua/vim/lsp/rpc.lua
@@ -645,9 +645,23 @@ function M.connect(host_or_path, port)
or assert(uv.new_tcp(), 'Could not create new TCP socket')
)
local closing = false
+ -- Connect returns a PublicClient synchronously so the caller
+ -- can immediately send messages before the connection is established
+ -- -> Need to buffer them until that happens
+ local connected = false
+ -- size should be enough because the client can't really do anything until initialization is done
+ -- which required a response from the server - implying the connection got established
+ local msgbuf = vim.ringbuf(10)
local transport = {
write = function(msg)
- handle:write(msg)
+ if connected then
+ local _, err = handle:write(msg)
+ if err and not closing then
+ log.error('Error on handle:write: %q', err)
+ end
+ else
+ msgbuf:push(msg)
+ end
end,
is_closing = function()
return closing
@@ -679,6 +693,10 @@ function M.connect(host_or_path, port)
handle:read_start(M.create_read_loop(handle_body, transport.terminate, function(read_err)
client:on_error(M.client_errors.READ_ERROR, read_err)
end))
+ connected = true
+ for msg in msgbuf do
+ handle:write(msg)
+ end
end
if port == nil then
handle:connect(host_or_path, on_connect)
@@ -704,7 +722,7 @@ end
--- @param cmd string[] Command to start the LSP server.
--- @param dispatchers? vim.lsp.rpc.Dispatchers
--- @param extra_spawn_params? vim.lsp.rpc.ExtraSpawnParams
---- @return vim.lsp.rpc.PublicClient? : Client RPC object, with these methods:
+--- @return vim.lsp.rpc.PublicClient : Client RPC object, with these methods:
--- - `notify()` |vim.lsp.rpc.notify()|
--- - `request()` |vim.lsp.rpc.request()|
--- - `is_closing()` returns a boolean indicating if the RPC is closing.
@@ -779,8 +797,7 @@ function M.start(cmd, dispatchers, extra_spawn_params)
end
local msg =
string.format('Spawning language server with cmd: `%s` failed%s', vim.inspect(cmd), sfx)
- vim.notify(msg, vim.log.levels.WARN)
- return nil
+ error(msg)
end
sysobj = sysobj_or_err --[[@as vim.SystemObj]]
diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua
index a09619f369..4f4762547a 100644
--- a/runtime/lua/vim/treesitter.lua
+++ b/runtime/lua/vim/treesitter.lua
@@ -257,7 +257,7 @@ end
---@param row integer Position row
---@param col integer Position column
---
----@return table[] List of captures `{ capture = "name", metadata = { ... } }`
+---@return {capture: string, lang: string, metadata: table}[]
function M.get_captures_at_pos(bufnr, row, col)
if bufnr == 0 then
bufnr = api.nvim_get_current_buf()
diff --git a/runtime/lua/vim/treesitter/_fold.lua b/runtime/lua/vim/treesitter/_fold.lua
index d511bef7a5..09d3f3368f 100644
--- a/runtime/lua/vim/treesitter/_fold.lua
+++ b/runtime/lua/vim/treesitter/_fold.lua
@@ -258,7 +258,7 @@ function FoldInfo:foldupdate(bufnr, srow, erow)
self.foldupdate_range = { srow, erow }
end
- if api.nvim_get_mode().mode == 'i' then
+ if api.nvim_get_mode().mode:match('^i') then
-- foldUpdate() is guarded in insert mode. So update folds on InsertLeave
if #(api.nvim_get_autocmds({
group = group,