diff options
Diffstat (limited to 'runtime/lua/vim')
-rw-r--r-- | runtime/lua/vim/_defaults.lua | 483 | ||||
-rw-r--r-- | runtime/lua/vim/_inspector.lua | 6 | ||||
-rw-r--r-- | runtime/lua/vim/_meta/base64.lua | 4 | ||||
-rw-r--r-- | runtime/lua/vim/_meta/builtin.lua | 25 | ||||
-rw-r--r-- | runtime/lua/vim/_meta/diff.lua | 85 | ||||
-rw-r--r-- | runtime/lua/vim/_meta/re.lua | 4 | ||||
-rw-r--r-- | runtime/lua/vim/_meta/spell.lua | 6 | ||||
-rw-r--r-- | runtime/lua/vim/_meta/vimfn.lua | 4 | ||||
-rw-r--r-- | runtime/lua/vim/iter.lua | 100 | ||||
-rw-r--r-- | runtime/lua/vim/lsp.lua | 100 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/client.lua | 14 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/inlay_hint.lua | 32 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/rpc.lua | 25 | ||||
-rw-r--r-- | runtime/lua/vim/treesitter.lua | 2 | ||||
-rw-r--r-- | runtime/lua/vim/treesitter/_fold.lua | 2 |
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, |