From f449a38f6a47bee30f0d4e291d8234d1ac8288a7 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 9 Oct 2024 08:14:18 +0800 Subject: vim-patch:9.1.0770: current command line completion is a bit limited (#30728) Problem: current command completion is a bit limited Solution: Add the shellcmdline completion type and getmdcomplpat() function (Ruslan Russkikh). closes: vim/vim#15823 https://github.com/vim/vim/commit/0407d621bbad020b840ffbbbd25ba023bbc05edd Co-authored-by: Ruslan Russkikh --- runtime/lua/vim/_meta/vimfn.lua | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index 3f6deba092..1e1e87fca8 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -2879,12 +2879,22 @@ function vim.fn.getcharsearch() end --- @return string function vim.fn.getcharstr(expr) end +--- Return completion pattern of the current command-line. +--- Only works when the command line is being edited, thus +--- requires use of |c_CTRL-\_e| or |c_CTRL-R_=|. +--- Also see |getcmdtype()|, |setcmdpos()|, |getcmdline()|, +--- |getcmdprompt()|, |getcmdcompltype()| and |setcmdline()|. +--- Returns an empty string when completion is not defined. +--- +--- @return string +function vim.fn.getcmdcomplpat() end + --- Return the type of the current command-line completion. --- Only works when the command line is being edited, thus --- requires use of |c_CTRL-\_e| or |c_CTRL-R_=|. --- See |:command-completion| for the return string. --- Also see |getcmdtype()|, |setcmdpos()|, |getcmdline()|, ---- |getcmdprompt()| and |setcmdline()|. +--- |getcmdprompt()|, |getcmdcomplpat()| and |setcmdline()|. --- Returns an empty string when completion is not defined. --- --- @return string -- cgit From 0264870c0aa50fd4e34fb8a6b3c3c3cd88a22db7 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 9 Oct 2024 09:44:15 +0200 Subject: vim-patch:9.1.0769: filetype: MLIR files are not recognized Problem: filetype: MLIR files are not recognized Solution: Detect '*.mlir' files as mlir filetype, include a mlir filetype plugin (Wu, Zhenyu) closes: vim/vim#15826 https://github.com/vim/vim/commit/347d43bd33519ab537f77d1a8fa8ab8f9196bcb9 Co-authored-by: Wu, Zhenyu --- runtime/lua/vim/filetype.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index d3910e26eb..3d3b2a2ea1 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -752,6 +752,7 @@ local extension = { mib = 'mib', mix = 'mix', mixal = 'mix', + mlir = 'mlir', mm = detect.mm, nb = 'mma', wl = 'mma', -- cgit From 8ef3dd3afa20024f439d8b7a69f316069c1909d3 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 9 Oct 2024 19:31:14 +0800 Subject: docs(lua): "vim.bo" is always equivalent to :setlocal (#30733) vim.bo :lua vim.bo.textwidth = 80 :setglobal textwidth? textwidth=0 :setlocal :setlocal textwidth=80 :setglobal textwidth? textwidth=0 :set :set textwidth=80 :setglobal textwidth? textwidth=80 --- runtime/lua/vim/_options.lua | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_options.lua b/runtime/lua/vim/_options.lua index a61fa61256..77d7054626 100644 --- a/runtime/lua/vim/_options.lua +++ b/runtime/lua/vim/_options.lua @@ -274,11 +274,9 @@ vim.go = setmetatable({}, { }) --- Get or set buffer-scoped |options| for the buffer with number {bufnr}. ---- If {bufnr} is omitted then the current buffer is used. +--- Like `:setlocal`. If {bufnr} is omitted then the current buffer is used. --- Invalid {bufnr} or key is an error. --- ---- Note: this is equivalent to `:setlocal` for |global-local| options and `:set` otherwise. ---- --- Example: --- --- ```lua -- cgit From 8450752f46e1482bf34b7f05e484cca740f61075 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 9 Oct 2024 08:15:14 +0800 Subject: vim-patch:9.1.0771: completion attribute hl_group is confusing Problem: Currently completion attribute hl_group is combined with all items, which is redundant and confusing with kind_hlgroup Solution: Renamed to abbr_hlgroup and combine it only with the abbr item (glepnir). closes: vim/vim#15818 https://github.com/vim/vim/commit/0fe17f8ffbd2588ecd2bf42dced556897bc64f89 Co-authored-by: glepnir --- runtime/lua/vim/lsp/completion.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/completion.lua b/runtime/lua/vim/lsp/completion.lua index 71ea2df100..e74f993356 100644 --- a/runtime/lua/vim/lsp/completion.lua +++ b/runtime/lua/vim/lsp/completion.lua @@ -272,7 +272,7 @@ function M._lsp_to_complete_items(result, prefix, client_id) icase = 1, dup = 1, empty = 1, - hl_group = hl_group, + abbr_hlgroup = hl_group, user_data = { nvim = { lsp = { -- cgit From b3109084c2c3675aa886bf16ff15f50025f30096 Mon Sep 17 00:00:00 2001 From: Tomasz N Date: Thu, 10 Oct 2024 11:40:03 +0200 Subject: fix(lsp): fix cursor position after snippet expansion (#30659) Problem: on `CompleteDone` cursor can jump to the end of line instead of the end of the completed word. Solution: remove only inserted word for snippet expansion instead of everything until eol. Fixes #30656 Co-authored-by: Mathias Fussenegger Co-authored-by: Justin M. Keyes --- runtime/lua/vim/lsp/completion.lua | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/completion.lua b/runtime/lua/vim/lsp/completion.lua index e74f993356..14fad987e0 100644 --- a/runtime/lua/vim/lsp/completion.lua +++ b/runtime/lua/vim/lsp/completion.lua @@ -113,12 +113,11 @@ local function parse_snippet(input) end --- @param item lsp.CompletionItem ---- @param suffix? string -local function apply_snippet(item, suffix) +local function apply_snippet(item) if item.textEdit then - vim.snippet.expand(item.textEdit.newText .. suffix) + vim.snippet.expand(item.textEdit.newText) elseif item.insertText then - vim.snippet.expand(item.insertText .. suffix) + vim.snippet.expand(item.insertText) end end @@ -539,15 +538,12 @@ local function on_complete_done() -- Remove the already inserted word. local start_char = cursor_col - #completed_item.word - local line = api.nvim_buf_get_lines(bufnr, cursor_row, cursor_row + 1, true)[1] - api.nvim_buf_set_text(bufnr, cursor_row, start_char, cursor_row, #line, { '' }) - return line:sub(cursor_col + 1) + api.nvim_buf_set_text(bufnr, cursor_row, start_char, cursor_row, cursor_col, { '' }) end - --- @param suffix? string - local function apply_snippet_and_command(suffix) + local function apply_snippet_and_command() if expand_snippet then - apply_snippet(completion_item, suffix) + apply_snippet(completion_item) end local command = completion_item.command @@ -565,9 +561,9 @@ local function on_complete_done() end if completion_item.additionalTextEdits and next(completion_item.additionalTextEdits) then - local suffix = clear_word() + clear_word() lsp.util.apply_text_edits(completion_item.additionalTextEdits, bufnr, offset_encoding) - apply_snippet_and_command(suffix) + apply_snippet_and_command() elseif resolve_provider and type(completion_item) == 'table' then local changedtick = vim.b[bufnr].changedtick @@ -577,7 +573,7 @@ local function on_complete_done() return end - local suffix = clear_word() + clear_word() if err then vim.notify_once(err.message, vim.log.levels.WARN) elseif result and result.additionalTextEdits then @@ -587,11 +583,11 @@ local function on_complete_done() end end - apply_snippet_and_command(suffix) + apply_snippet_and_command() end, bufnr) else - local suffix = clear_word() - apply_snippet_and_command(suffix) + clear_word() + apply_snippet_and_command() end end -- cgit From 6f1601a1b94e6ea724d8436600c64760525d1d2b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 11 Oct 2024 06:48:45 +0800 Subject: vim-patch:9.1.0774: "shellcmdline" doesn't work with getcompletion() (#30750) Problem: "shellcmdline" doesn't work with getcompletion(). Solution: Use set_context_for_wildcard_arg() (zeertzjq). closes: vim/vim#15834 https://github.com/vim/vim/commit/85f36d61e09b12d6f1c60201129823371daa4a84 --- runtime/lua/vim/_meta/vimfn.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index 1e1e87fca8..81e7070ac0 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -3008,6 +3008,7 @@ function vim.fn.getcmdwintype() end --- runtime |:runtime| completion --- scriptnames sourced script names |:scriptnames| --- shellcmd Shell command +--- shellcmdline Shell command line with filename arguments --- sign |:sign| suboptions --- syntax syntax file names |'syntax'| --- syntime |:syntime| suboptions -- cgit From 79c036747a6cde1e38adcd19ed89b9295b2af1e3 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Fri, 11 Oct 2024 08:31:06 +0200 Subject: vim-patch:9.1.0773: filetype: some Apache files are not recognized Problem: filetype: some Apache files are not recognized Solution: Detect more config files from the Apache source distribution as filetype apache (nisbet-hubbard) closes: vim/vim#15810 https://github.com/vim/vim/commit/e58e9015cc431b79d88af4daec838519a8e296d6 Co-authored-by: nisbet-hubbard <87453615+nisbet-hubbard@users.noreply.github.com> --- runtime/lua/vim/filetype.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 3d3b2a2ea1..445281deca 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -2185,6 +2185,8 @@ local pattern = { ['^apache%.conf'] = detect_apache_dotconf, ['^apache2%.conf'] = detect_apache_dotconf, ['^httpd%.conf'] = detect_apache_dotconf, + ['^httpd%-.*%.conf'] = detect_apache_dotconf, + ['^proxy%-html%.conf'] = detect_apache_dotconf, ['^srm%.conf'] = detect_apache_dotconf, ['asterisk/.*%.conf'] = starsetf('asterisk'), ['asterisk.*/.*voicemail%.conf'] = starsetf('asteriskvm'), -- cgit From c3cb702ac7a5d6a1515129c80ce143e3bdf5d739 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 11 Oct 2024 00:50:09 -0700 Subject: fix(lsp): set 'smoothscroll' in docs hover #30748 --- runtime/lua/vim/lsp/util.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 882ec22ca6..ebc3f7c55e 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1656,6 +1656,7 @@ function M.open_floating_preview(contents, syntax, opts) vim.wo[floating_winnr].foldenable = false -- Disable folding. vim.wo[floating_winnr].wrap = opts.wrap -- Soft wrapping. vim.wo[floating_winnr].breakindent = true -- Slightly better list presentation. + vim.wo[floating_winnr].smoothscroll = true -- Scroll by screen-line instead of buffer-line. vim.bo[floating_bufnr].modifiable = false vim.bo[floating_bufnr].bufhidden = 'wipe' -- cgit From 267c7525f738cdd6024c39da758e885c026ffaaa Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Wed, 2 Oct 2024 10:34:14 -0700 Subject: feat(treesitter): introduce child_with_descendant() This commit also marks `child_containing_descendant()` as deprecated (per upstream's documentation), and uses `child_with_descendant()` in its place. Minimum required tree-sitter version will now be `0.24`. --- runtime/lua/vim/treesitter.lua | 3 +-- runtime/lua/vim/treesitter/_meta/tsnode.lua | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index ed7d31e1f7..4727c0d61d 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -152,8 +152,7 @@ function M.is_ancestor(dest, source) return false end - -- child_containing_descendant returns nil if dest is a direct parent - return source:parent() == dest or dest:child_containing_descendant(source) ~= nil + return dest:child_with_descendant(source) ~= nil end --- Returns the node's range or an unpacked range table diff --git a/runtime/lua/vim/treesitter/_meta/tsnode.lua b/runtime/lua/vim/treesitter/_meta/tsnode.lua index acc9f8d24e..d982b6a505 100644 --- a/runtime/lua/vim/treesitter/_meta/tsnode.lua +++ b/runtime/lua/vim/treesitter/_meta/tsnode.lua @@ -15,7 +15,7 @@ error('Cannot require a meta file') local TSNode = {} -- luacheck: no unused --- Get the node's immediate parent. ---- Prefer |TSNode:child_containing_descendant()| +--- Prefer |TSNode:child_with_descendant()| --- for iterating over the node's ancestors. --- @return TSNode? function TSNode:parent() end @@ -71,8 +71,24 @@ function TSNode:named_child(index) end --- Get the node's child that contains {descendant}. --- @param descendant TSNode --- @return TSNode? +--- @deprecated function TSNode:child_containing_descendant(descendant) end +--- Get the node's child that contains {descendant} (includes {descendant}). +--- +--- For example, with the following node hierarchy: +--- +--- ``` +--- a -> b -> c +--- +--- a:child_with_descendant(c) == b +--- a:child_with_descendant(b) == b +--- a:child_with_descendant(a) == nil +--- ``` +--- @param descendant TSNode +--- @return TSNode? +function TSNode:child_with_descendant(descendant) end + --- Get the node's start position. Return three values: the row, column and --- total byte count (all zero-based). --- @return integer, integer, integer -- cgit From d3193afc2559e7d84ed2d76664a650dc03b4c6ef Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Thu, 19 Sep 2024 13:08:22 -0700 Subject: fix(treesitter): remove duplicate symbol names in language.inspect() **Problems:** - `vim.treesitter.language.inspect()` returns duplicate symbol names, sometimes up to 6 of one kind in the case of `markdown` - The list-like `symbols` table can have holes and is thus not even a valid msgpack table anyway, mentioned in a test **Solution:** Return symbols as a map, rather than a list, where field names are the names of the symbol. The boolean value associated with the field encodes whether or not the symbol is named. Note that anonymous nodes are surrounded with double quotes (`"`) to prevent potential collisions with named counterparts that have the same identifier. --- runtime/lua/vim/treesitter/_query_linter.lua | 8 ++++++-- runtime/lua/vim/treesitter/language.lua | 7 ++++++- 2 files changed, 12 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/_query_linter.lua b/runtime/lua/vim/treesitter/_query_linter.lua index c5e4b86e1e..a825505378 100644 --- a/runtime/lua/vim/treesitter/_query_linter.lua +++ b/runtime/lua/vim/treesitter/_query_linter.lua @@ -240,8 +240,12 @@ function M.omnifunc(findstart, base) table.insert(items, text) end end - for _, s in pairs(parser_info.symbols) do - local text = s[2] and s[1] or string.format('%q', s[1]):gsub('\n', 'n') ---@type string + for text, named in + pairs(parser_info.symbols --[[@as table]]) + do + if not named then + text = string.format('%q', text:sub(2, -2)):gsub('\n', 'n') ---@type string + end if text:find(base, 1, true) then table.insert(items, text) end diff --git a/runtime/lua/vim/treesitter/language.lua b/runtime/lua/vim/treesitter/language.lua index 9f7807e036..aa1d38df97 100644 --- a/runtime/lua/vim/treesitter/language.lua +++ b/runtime/lua/vim/treesitter/language.lua @@ -170,7 +170,12 @@ end --- Inspects the provided language. --- ---- Inspecting provides some useful information on the language like node names, ... +--- Inspecting provides some useful information on the language like node and field names, ABI +--- version, and whether the language came from a WASM module. +--- +--- Node names are returned in a table mapping each node name to a `boolean` indicating whether or +--- not the node is named (i.e., not anonymous). Anonymous nodes are surrounded with double quotes +--- (`"`). --- ---@param lang string Language ---@return table -- cgit From 26e765f905cbf1df18cfc825bad9b1b982f517ea Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Fri, 11 Oct 2024 11:56:21 -0500 Subject: feat(defaults): map gri to vim.lsp.buf.implementation() (#30764) Continuing the default LSP maps under the "gr" prefix. Mnemonic: "i" for "implementation". --- runtime/lua/vim/_defaults.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua index 6cad1dbca9..8e850f4cd3 100644 --- a/runtime/lua/vim/_defaults.lua +++ b/runtime/lua/vim/_defaults.lua @@ -157,7 +157,7 @@ do --- client is attached. If no client is attached, or if a server does not support a capability, an --- error message is displayed rather than exhibiting different behavior. --- - --- See |grr|, |grn|, |gra|, |i_CTRL-S|. + --- See |grr|, |grn|, |gra|, |gri|, |i_CTRL-S|. do vim.keymap.set('n', 'grn', function() vim.lsp.buf.rename() @@ -171,6 +171,10 @@ do vim.lsp.buf.references() end, { desc = 'vim.lsp.buf.references()' }) + vim.keymap.set('n', 'gri', function() + vim.lsp.buf.implementation() + end, { desc = 'vim.lsp.buf.implementation()' }) + vim.keymap.set('i', '', function() vim.lsp.buf.signature_help() end, { desc = 'vim.lsp.buf.signature_help()' }) -- cgit From 0e42c81c7fd429529d89458349c7cdde254d5406 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 12 Oct 2024 08:07:05 +0800 Subject: fix(lua): avoid recursive vim.on_key() callback (#30753) --- runtime/lua/vim/_editor.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 2e829578a7..17a87dd9fd 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -658,6 +658,8 @@ local on_key_cbs = {} --- @type table --- and cannot be toggled dynamically. --- ---@note {fn} will be removed on error. +---@note {fn} won't be invoked recursively, i.e. if {fn} itself consumes input, +--- it won't be invoked for those keys. ---@note {fn} will not be cleared by |nvim_buf_clear_namespace()| --- ---@param fn fun(key: string, typed: string)? -- cgit From 45f8f957c05ca18ea86b8b6bb6c0a903c9140b7b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 12 Oct 2024 12:20:06 +0800 Subject: docs(lua): clarify when on_key "typed" will be empty (#30774) --- runtime/lua/vim/_editor.lua | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 17a87dd9fd..ce269f087d 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -662,15 +662,18 @@ local on_key_cbs = {} --- @type table --- it won't be invoked for those keys. ---@note {fn} will not be cleared by |nvim_buf_clear_namespace()| --- ----@param fn fun(key: string, typed: string)? ---- Function invoked on every key press. |i_CTRL-V| ---- {key} is the key after mappings have been applied, and ---- {typed} is the key(s) before mappings are applied, which ---- may be empty if {key} is produced by non-typed keys. ---- When {fn} is nil and {ns_id} is specified, the callback ---- associated with namespace {ns_id} is removed. +---@param fn fun(key: string, typed: string)? Function invoked for every input key, +--- after mappings have been applied but before further processing. Arguments +--- {key} and {typed} are raw keycodes, where {key} is the key after mappings +--- are applied, and {typed} is the key(s) before mappings are applied. +--- {typed} may be empty if {key} is produced by non-typed key(s) or by the +--- same typed key(s) that produced a previous {key}. +--- When {fn} is `nil` and {ns_id} is specified, the callback associated with +--- namespace {ns_id} is removed. ---@param ns_id integer? Namespace ID. If nil or 0, generates and returns a ---- new |nvim_create_namespace()| id. +--- new |nvim_create_namespace()| id. +--- +---@see |keytrans()| --- ---@return integer Namespace id associated with {fn}. Or count of all callbacks ---if on_key() is called without arguments. -- cgit From 06625f9b3af22a2971cbd21274138c9653f2f723 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 12 Oct 2024 17:28:56 +0200 Subject: vim-patch:9.1.0777: filetype: Some upstream php files are not recognized Problem: filetype: Some upstream php files are not recognized Solution: Detect more config files from the PHP source distribution as filetype ini (nisbet-hubbard). closes: vim/vim#15840 https://github.com/vim/vim/commit/367499c5c39057bca267716d9aad20554d4d83fd Co-authored-by: nisbet-hubbard <87453615+nisbet-hubbard@users.noreply.github.com> --- runtime/lua/vim/filetype.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 445281deca..25e4dd33f7 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -2296,6 +2296,8 @@ local pattern = { ['^crontab%.'] = starsetf('crontab'), ['^cvs%d+$'] = 'cvs', ['^php%.ini%-'] = 'dosini', + ['^php%-fpm%.conf'] = 'dosini', + ['^www%.conf'] = 'dosini', ['^drac%.'] = starsetf('dracula'), ['/dtrace/.*%.d$'] = 'dtrace', ['esmtprc$'] = 'esmtprc', -- cgit From 2d24558c285198d2e725c83a8c4ad4d90797c950 Mon Sep 17 00:00:00 2001 From: glepnir Date: Sun, 13 Oct 2024 18:44:05 +0800 Subject: docs: update autotrigger description of vim.lsp.compleiton.BufferOpts (#30796) Currently the behavior of autotrigger is not well documented. --- runtime/lua/vim/lsp/completion.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/completion.lua b/runtime/lua/vim/lsp/completion.lua index 14fad987e0..3df506d23a 100644 --- a/runtime/lua/vim/lsp/completion.lua +++ b/runtime/lua/vim/lsp/completion.lua @@ -592,7 +592,7 @@ local function on_complete_done() end --- @class vim.lsp.completion.BufferOpts ---- @field autotrigger? boolean Whether to trigger completion automatically. Default: false +--- @field autotrigger? boolean Default: false When true, completion triggers automatically based on the server's `triggerCharacters`. --- @field convert? fun(item: lsp.CompletionItem): table Transforms an LSP CompletionItem to |complete-items|. ---@param client_id integer -- cgit From 15aa27bc1f999adac3f4d69742ca31992fb5f837 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sun, 13 Oct 2024 19:33:25 +0200 Subject: vim-patch:9.1.0778: filetype: lf config files are not recognized MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: filetype: lf config files are not recognized Solution: detect lfrc files as lf filetype, include a syntax script for lf files (Andis Spriņķis). References: - https://github.com/gokcehan/lf closes: vim/vim#15859 https://github.com/vim/vim/commit/0f146b7925178a5bdfdc8e07642976f0b76d9612 Co-authored-by: Andis Spriņķis --- runtime/lua/vim/filetype.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 25e4dd33f7..3b641898a3 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1611,6 +1611,7 @@ local filename = { ['ldaprc'] = 'ldapconf', ['.ldaprc'] = 'ldapconf', ['ldap.conf'] = 'ldapconf', + ['lfrc'] = 'lf', ['lftp.conf'] = 'lftp', ['.lftprc'] = 'lftp', ['/.libao'] = 'libao', -- cgit From d04d7bf65de8e77a8ea3aea59506abc328d77cd1 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sun, 13 Oct 2024 19:43:04 +0200 Subject: vim-patch:9.1.0779: filetype: neomuttlog files are not recognized Problem: filetype: neomuttlog files are not recognized Solution: detect '*.neomuttdebug' file as neomuttlog filetype, include neomuttlog syntax script (Richard Russon) closes: vim/vim#15858 https://github.com/vim/vim/commit/a2aa921f767da17d4b619f41b7a1210c762c91e2 Co-authored-by: Richard Russon Co-authored-by: AvidSeeker --- runtime/lua/vim/filetype.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 3b641898a3..a166397bfd 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -2264,6 +2264,7 @@ local pattern = { ['^%.?neomuttrc'] = detect_neomuttrc, ['/%.neomutt/neomuttrc'] = detect_neomuttrc, ['^Neomuttrc'] = detect_neomuttrc, + ['%.neomuttdebug'] = 'neomuttlog', }, ['^%.'] = { ['^%.cshrc'] = detect.csh, -- cgit From 9701cbf036aecf18e7d88681040c07d61c314ca6 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 15 Oct 2024 08:16:56 +0800 Subject: vim-patch:7b5e52d: runtime(doc): add preview flag to statusline example Problem: The standard statusline example is missing the preview flag "%w" Solution: Add the preview flag "%w" closes: vim/vim#15874 https://github.com/vim/vim/commit/7b5e52d16fb457c90cc44340a6190712aab7e03b Co-authored-by: saher --- runtime/lua/vim/_meta/options.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index ce3ff4f861..1270ad2519 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -6695,7 +6695,7 @@ vim.wo.stc = vim.wo.statuscolumn --- Emulate standard status line with 'ruler' set --- --- ```vim ---- set statusline=%<%f\ %h%m%r%=%-14.(%l,%c%V%)\ %P +--- set statusline=%<%f\ %h%w%m%r%=%-14.(%l,%c%V%)\ %P --- ``` --- Similar, but add ASCII value of char under the cursor (like "ga") --- -- cgit From dbd172e7e953bdab7132bd635532295a4dc64af9 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 15 Oct 2024 08:20:09 +0800 Subject: vim-patch:5bcfb5a: runtime(doc): add some docs for file-watcher programs fixes: vim/vim#15733 https://github.com/vim/vim/commit/5bcfb5a30cfd8e8574061bdd82a192f47aae09b5 Co-authored-by: Christian Brabandt --- runtime/lua/vim/_meta/options.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index 1270ad2519..36c0f583c7 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -289,12 +289,13 @@ vim.go.bk = vim.go.backup --- useful for example in source trees where all the files are symbolic or --- hard links and any changes should stay in the local source tree, not --- be propagated back to the original source. ---- *crontab* +--- *crontab* --- One situation where "no" and "auto" will cause problems: A program --- that opens a file, invokes Vim to edit that file, and then tests if --- the open file was changed (through the file descriptor) will check the --- backup file instead of the newly created file. "crontab -e" is an ---- example. +--- example, as are several `file-watcher` daemons like inotify. In that +--- case you probably want to switch this option. --- --- When a copy is made, the original file is truncated and then filled --- with the new text. This means that protection bits, owner and -- cgit From e0a5c3bb581752569df4490b48cb54e7c1ab0613 Mon Sep 17 00:00:00 2001 From: Maria José Solano Date: Tue, 15 Oct 2024 02:36:04 -0700 Subject: fix(lsp): handle multiline signature help labels #30460 --- runtime/lua/vim/lsp/handlers.lua | 15 +++++-- runtime/lua/vim/lsp/util.lua | 93 +++++++++++++++++++++++++--------------- 2 files changed, 69 insertions(+), 39 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 44548fec92..8e538242d9 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -462,6 +462,8 @@ M[ms.textDocument_typeDefinition] = location_handler --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_implementation M[ms.textDocument_implementation] = location_handler +local sig_help_ns = api.nvim_create_namespace('vim_lsp_signature_help') + --- |lsp-handler| for the method "textDocument/signatureHelp". --- --- The active parameter is highlighted with |hl-LspSignatureActiveParameter|. @@ -476,7 +478,7 @@ M[ms.textDocument_implementation] = location_handler --- ``` --- ---@param _ lsp.ResponseError? ----@param result lsp.SignatureHelp Response from the language server +---@param result lsp.SignatureHelp? Response from the language server ---@param ctx lsp.HandlerContext Client context ---@param config table Configuration table. --- - border: (default=nil) @@ -509,10 +511,15 @@ function M.signature_help(_, result, ctx, config) return end local fbuf, fwin = util.open_floating_preview(lines, 'markdown', config) + -- Highlight the active parameter. if hl then - -- Highlight the second line if the signature is wrapped in a Markdown code block. - local line = vim.startswith(lines[1], '```') and 1 or 0 - api.nvim_buf_add_highlight(fbuf, -1, 'LspSignatureActiveParameter', line, unpack(hl)) + vim.highlight.range( + fbuf, + sig_help_ns, + 'LspSignatureActiveParameter', + { hl[1], hl[2] }, + { hl[3], hl[4] } + ) end return fbuf, fwin end diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index ebc3f7c55e..fc822f1403 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -781,24 +781,37 @@ function M.convert_input_to_markdown_lines(input, contents) return contents end +--- Returns the line/column-based position in `contents` at the given offset. +--- +---@param offset integer +---@param contents string[] +---@return { [1]: integer, [2]: integer } +local function get_pos_from_offset(offset, contents) + local i = 0 + for l, line in ipairs(contents) do + if offset >= i and offset < i + #line then + return { l - 1, offset - i + 1 } + else + i = i + #line + 1 + end + end +end + --- Converts `textDocument/signatureHelp` response to markdown lines. --- ---@param signature_help lsp.SignatureHelp Response of `textDocument/SignatureHelp` ---@param ft string|nil filetype that will be use as the `lang` for the label markdown code block ---@param triggers table|nil list of trigger characters from the lsp server. used to better determine parameter offsets ---@return string[]|nil table list of lines of converted markdown. ----@return number[]|nil table of active hl +---@return Range4|nil table of active hl ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers) - if not signature_help.signatures then - return - end --The active signature. If omitted or the value lies outside the range of --`signatures` the value defaults to zero or is ignored if `signatures.length == 0`. --Whenever possible implementors should make an active decision about --the active signature and shouldn't rely on a default value. local contents = {} - local active_hl + local active_offset ---@type [integer, integer]|nil local active_signature = signature_help.activeSignature or 0 -- If the activeSignature is not inside the valid range, then clip it. -- In 3.15 of the protocol, activeSignature was allowed to be negative @@ -806,9 +819,6 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers active_signature = 0 end local signature = signature_help.signatures[active_signature + 1] - if not signature then - return - end local label = signature.label if ft then -- wrap inside a code block for proper rendering @@ -825,6 +835,8 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers M.convert_input_to_markdown_lines(signature.documentation, contents) end if signature.parameters and #signature.parameters > 0 then + -- First check if the signature has an activeParameter. If it doesn't check if the response + -- had that property instead. Else just default to 0. local active_parameter = (signature.activeParameter or signature_help.activeParameter or 0) if active_parameter < 0 then active_parameter = 0 @@ -837,8 +849,8 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers end local parameter = signature.parameters[active_parameter + 1] - if parameter then - --[=[ + local parameter_label = parameter.label + --[=[ --Represents a parameter of a callable-signature. A parameter can --have a label and a doc-comment. interface ParameterInformation { @@ -856,36 +868,47 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers documentation?: string | MarkupContent; } --]=] - if parameter.label then - if type(parameter.label) == 'table' then - active_hl = parameter.label - else - local offset = 1 - -- try to set the initial offset to the first found trigger character - for _, t in ipairs(triggers or {}) do - local trigger_offset = signature.label:find(t, 1, true) - if trigger_offset and (offset == 1 or trigger_offset < offset) then - offset = trigger_offset - end - end - for p, param in pairs(signature.parameters) do - offset = signature.label:find(param.label, offset, true) - if not offset then - break - end - if p == active_parameter + 1 then - active_hl = { offset - 1, offset + #parameter.label - 1 } - break - end - offset = offset + #param.label + 1 - end + if type(parameter_label) == 'table' then + active_offset = parameter_label + else + local offset = 1 ---@type integer|nil + -- try to set the initial offset to the first found trigger character + for _, t in ipairs(triggers or {}) do + local trigger_offset = signature.label:find(t, 1, true) + if trigger_offset and (offset == 1 or trigger_offset < offset) then + offset = trigger_offset end end - if parameter.documentation then - M.convert_input_to_markdown_lines(parameter.documentation, contents) + for p, param in pairs(signature.parameters) do + offset = signature.label:find(param.label, offset, true) + if not offset then + break + end + if p == active_parameter + 1 then + active_offset = { offset - 1, offset + #parameter_label - 1 } + break + end + offset = offset + #param.label + 1 end end + if parameter.documentation then + M.convert_input_to_markdown_lines(parameter.documentation, contents) + end + end + + local active_hl = nil + if active_offset then + active_hl = {} + -- Account for the start of the markdown block. + if ft then + active_offset[1], active_offset[2] = + active_offset[1] + #contents[1], active_offset[2] + #contents[1] + end + + list_extend(active_hl, get_pos_from_offset(active_offset[1], contents)) + list_extend(active_hl, get_pos_from_offset(active_offset[2], contents)) end + return contents, active_hl end -- cgit From 84623dbe93777c0a8e7ddf57470ddeb2ea738069 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 15 Oct 2024 18:53:59 +0800 Subject: vim-patch:9.1.0785: cannot preserve error position when setting quickfix list (#30820) Problem: cannot preserve error position when setting quickfix lists Solution: Add the 'u' action for setqflist()/setloclist() and try to keep the closes target position (Jeremy Fleischman) fixes: vim/vim#15839 closes: vim/vim#15841 https://github.com/vim/vim/commit/27fbf6e5e8bee5c6b61819a5e82a0b50b265f0b0 Co-authored-by: Jeremy Fleischman --- runtime/lua/vim/_meta/vimfn.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index 81e7070ac0..f588162112 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -8229,6 +8229,8 @@ function vim.fn.setpos(expr, list) end --- clear the list: >vim --- call setqflist([], 'r') --- < +--- 'u' Like 'r', but tries to preserve the current selection +--- in the quickfix list. --- 'f' All the quickfix lists in the quickfix stack are --- freed. --- -- cgit From 82b02ae2f2af439a8c678ed6b55a43121055f279 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 12 Oct 2024 11:23:31 +0200 Subject: fix(runtime): clean up one-off scripts Problem: Some runtime files no longer spark joy. Solution: Kondo the place up. Still sparks _some_ joy (moved to new `runtime/scripts` folder): * `macros/less.*` * `mswin.vim` * `tools/emoji_list.lua` No longer sparks joy (removed): * `macmap.vim` (gvimrc file; not useful in Nvim) * `tools/check_colors.vim` (no longer useful with new default colorscheme and treesitter) * `macros/editexisting.vim` (throws error on current Nvim) * `macros/justify.vim` (obsolete shim for `packadd! justify`) * `macros/matchit.vim` (same) * `macros/shellmenu.vim` (same) * `macros/swapmous.vim` (same) --- runtime/lua/vim/_meta/vimfn.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index f588162112..de1937f620 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -7923,7 +7923,7 @@ function vim.fn.setbufvar(buf, varname, val) end --- To clear the overrides pass an empty {list}: >vim --- call setcellwidths([]) --- ---- Date: Tue, 15 Oct 2024 16:56:35 +0100 Subject: feat(lsp.util): use faster version of vim.validate --- runtime/lua/vim/lsp/util.lua | 68 ++++++++++++++++---------------------------- 1 file changed, 24 insertions(+), 44 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index fc822f1403..15f5b86fec 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -375,11 +375,10 @@ end ---@param offset_encoding string utf-8|utf-16|utf-32 ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textEdit function M.apply_text_edits(text_edits, bufnr, offset_encoding) - validate({ - text_edits = { text_edits, 't', false }, - bufnr = { bufnr, 'number', false }, - offset_encoding = { offset_encoding, 'string', false }, - }) + validate('text_edits', text_edits, 'table', false) + validate('bufnr', bufnr, 'number', false) + validate('offset_encoding', offset_encoding, 'string', false) + if not next(text_edits) then return end @@ -932,14 +931,10 @@ end --- to display the full window height. ---@return table Options function M.make_floating_popup_options(width, height, opts) - validate({ - opts = { opts, 't', true }, - }) + validate('opts', opts, 'table', true) opts = opts or {} - validate({ - ['opts.offset_x'] = { opts.offset_x, 'n', true }, - ['opts.offset_y'] = { opts.offset_y, 'n', true }, - }) + validate('opts.offset_x', opts.offset_x, 'n', true) + validate('opts.offset_y', opts.offset_y, 'n', true) local anchor = '' local row, col @@ -1211,10 +1206,8 @@ end --- - separator insert separator after code block ---@return table stripped content function M.stylize_markdown(bufnr, contents, opts) - validate({ - contents = { contents, 't' }, - opts = { opts, 't', true }, - }) + validate('contents', contents, 'table') + validate('opts', opts, 'table', true) opts = opts or {} -- table of fence types to {ft, begin, end} @@ -1413,10 +1406,8 @@ end ---@return string[] table of lines containing normalized Markdown ---@see https://github.github.com/gfm function M._normalize_markdown(contents, opts) - validate({ - contents = { contents, 't' }, - opts = { opts, 't', true }, - }) + validate('contents', contents, 'table') + validate('opts', opts, 'table', true) opts = opts or {} -- 1. Carriage returns are removed @@ -1493,10 +1484,8 @@ end ---@return integer width size of float ---@return integer height size of float function M._make_floating_popup_size(contents, opts) - validate({ - contents = { contents, 't' }, - opts = { opts, 't', true }, - }) + validate('contents', contents, 'table') + validate('opts', opts, 'table', true) opts = opts or {} local width = opts.width @@ -1603,11 +1592,9 @@ end ---@return integer bufnr of newly created float window ---@return integer winid of newly created float window preview window function M.open_floating_preview(contents, syntax, opts) - validate({ - contents = { contents, 't' }, - syntax = { syntax, 's', true }, - opts = { opts, 't', true }, - }) + validate('contents', contents, 'table') + validate('syntax', syntax, 'string', true) + validate('opts', opts, 'table', true) opts = opts or {} opts.wrap = opts.wrap ~= false -- wrapping by default opts.focus = opts.focus ~= false @@ -1709,7 +1696,6 @@ do --[[ References ]] --- ---@param bufnr integer|nil Buffer id function M.buf_clear_references(bufnr) - validate({ bufnr = { bufnr, { 'n' }, true } }) api.nvim_buf_clear_namespace(bufnr or 0, reference_ns, 0, -1) end @@ -1720,10 +1706,8 @@ do --[[ References ]] ---@param offset_encoding string One of "utf-8", "utf-16", "utf-32". ---@see https://microsoft.github.io/language-server-protocol/specification/#textDocumentContentChangeEvent function M.buf_highlight_references(bufnr, references, offset_encoding) - validate({ - bufnr = { bufnr, 'n', true }, - offset_encoding = { offset_encoding, 'string', false }, - }) + validate('bufnr', bufnr, 'number', true) + validate('offset_encoding', offset_encoding, 'string', false) for _, reference in ipairs(references) do local start_line, start_char = reference['range']['start']['line'], reference['range']['start']['character'] @@ -1994,9 +1978,7 @@ end ---@param bufnr integer buffer handle or 0 for current, defaults to current ---@return string encoding first client if there is one, nil otherwise function M._get_offset_encoding(bufnr) - validate({ - bufnr = { bufnr, 'n', true }, - }) + validate('bufnr', bufnr, 'number', true) local offset_encoding @@ -2055,11 +2037,9 @@ end ---@return table { textDocument = { uri = `current_file_uri` }, range = { start = ---`start_position`, end = `end_position` } } function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding) - validate({ - start_pos = { start_pos, 't', true }, - end_pos = { end_pos, 't', true }, - offset_encoding = { offset_encoding, 's', true }, - }) + validate('start_pos', start_pos, 'table', true) + validate('end_pos', end_pos, 'table', true) + validate('offset_encoding', offset_encoding, 'string', true) bufnr = bufnr or api.nvim_get_current_buf() offset_encoding = offset_encoding or M._get_offset_encoding(bufnr) local A = list_extend({}, start_pos or api.nvim_buf_get_mark(bufnr, '<')) @@ -2112,7 +2092,7 @@ end ---@param bufnr integer|nil: Buffer handle, defaults to current ---@return integer indentation size function M.get_effective_tabstop(bufnr) - validate({ bufnr = { bufnr, 'n', true } }) + validate('bufnr', bufnr, 'number', true) local bo = bufnr and vim.bo[bufnr] or vim.bo local sw = bo.shiftwidth return (sw == 0 and bo.tabstop) or sw @@ -2124,7 +2104,7 @@ end ---@return lsp.DocumentFormattingParams object ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting function M.make_formatting_params(options) - validate({ options = { options, 't', true } }) + validate('options', options, 'table', true) options = vim.tbl_extend('keep', options or {}, { tabSize = M.get_effective_tabstop(), insertSpaces = vim.bo.expandtab, -- cgit From 0e8568d72c8825292af1b74018d8c3def0e3f16b Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 15 Oct 2024 17:02:14 +0100 Subject: feat(lsp.util): remove lsp spec extract --- runtime/lua/vim/lsp/util.lua | 18 ------------------ 1 file changed, 18 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 15f5b86fec..092d0a5576 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -849,24 +849,6 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers local parameter = signature.parameters[active_parameter + 1] local parameter_label = parameter.label - --[=[ - --Represents a parameter of a callable-signature. A parameter can - --have a label and a doc-comment. - interface ParameterInformation { - --The label of this parameter information. - -- - --Either a string or an inclusive start and exclusive end offsets within its containing - --signature label. (see SignatureInformation.label). The offsets are based on a UTF-16 - --string representation as `Position` and `Range` does. - -- - --*Note*: a label of type string should be a substring of its containing signature label. - --Its intended use case is to highlight the parameter label part in the `SignatureInformation.label`. - label: string | [number, number]; - --The human-readable doc-comment of this parameter. Will be shown - --in the UI but can be omitted. - documentation?: string | MarkupContent; - } - --]=] if type(parameter_label) == 'table' then active_offset = parameter_label else -- cgit From 80e37aa533573ef1ad96bcccc006b8d45dc963b9 Mon Sep 17 00:00:00 2001 From: Tristan Knight Date: Wed, 16 Oct 2024 17:12:19 +0100 Subject: fix(lsp): str_byteindex_enc bounds checking #30747 Problem: Previously the index was only checked against the UTF8 length. This could cause unexpected behaviours for strings containing multibyte chars Solution: Check indicies correctly against their max value before returning the fallback length --- runtime/lua/vim/lsp/util.lua | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index fc822f1403..2b9e734c18 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -171,25 +171,24 @@ end ---@param encoding string utf-8|utf-16|utf-32| defaults to utf-16 ---@return integer byte (utf-8) index of `encoding` index `index` in `line` function M._str_byteindex_enc(line, index, encoding) - local len = #line - if index > len then - -- LSP spec: if character > line length, default to the line length. - -- https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#position - return len - end + -- LSP spec: if character > line length, default to the line length. + -- https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#position + local len8 = #line if not encoding then encoding = 'utf-16' end if encoding == 'utf-8' then - if index then + if index and index <= len8 then return index else - return len + return len8 end - elseif encoding == 'utf-16' then - return vim.str_byteindex(line, index, true) + end + local len32, len16 = vim.str_utfindex(line) + if encoding == 'utf-16' then + return index <= len16 and vim.str_byteindex(line, index, true) or len8 elseif encoding == 'utf-32' then - return vim.str_byteindex(line, index) + return index <= len32 and vim.str_byteindex(line, index) or len8 else error('Invalid encoding: ' .. vim.inspect(encoding)) end -- cgit From c89150241d52ac70dd5bf0f4f8cb90902a7ccf6c Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 17 Oct 2024 07:48:42 +0800 Subject: vim-patch:6c2fc37: runtime(help): Update help syntax This commit makaes the following changes to the vim help syntax: - fix excessive URL detection in help, because `file:{filename}` in doc/options.txt is determined to be a URL. - update highlighting N for :resize in help - split Italian-specific syntax into separate help script - highlight `Note` in parentheses in help - update 'titlestring' behaviour in documentation for invalid '%' format closes: vim/vim#15883 https://github.com/vim/vim/commit/6c2fc377bfbfb6f1a46b1061413cd21116b596ed Co-authored-by: Milly --- runtime/lua/vim/_meta/options.lua | 4 +++- runtime/lua/vim/_meta/vimfn.lua | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index 36c0f583c7..c33753047f 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -7310,7 +7310,9 @@ vim.go.titleold = vim.o.titleold --- window. This happens only when the 'title' option is on. --- --- When this option contains printf-style '%' items, they will be ---- expanded according to the rules used for 'statusline'. +--- expanded according to the rules used for 'statusline'. If it contains +--- an invalid '%' format, the value is used as-is and no error or warning +--- will be given when the value is set. --- This option cannot be set in a modeline when 'modelineexpr' is off. --- --- Example: diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index de1937f620..d00bbe4770 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -10629,7 +10629,7 @@ function vim.fn.wait(timeout, condition, interval) end --- For example to make work like in wildmode, use: >vim --- cnoremap wildmenumode() ? "\\" : "\" --- < ---- (Note, this needs the 'wildcharm' option set appropriately). +--- (Note: this needs the 'wildcharm' option set appropriately). --- --- @return any function vim.fn.wildmenumode() end -- cgit From ce678043e3461d45f1251979cf047dd529b117ea Mon Sep 17 00:00:00 2001 From: Jordan <46637683+JordanllHarper@users.noreply.github.com> Date: Thu, 17 Oct 2024 09:35:19 +0100 Subject: feat(lsp): show server name in code actions #30830 Problem: If there are multiple LSP clients, it's not clear which actions are provided by which servers. #30710 Solution: Show the server name next to each action (only if there are multiple clients). --- runtime/lua/vim/lsp/buf.lua | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 301c1f0cb6..984222efbe 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -827,11 +827,19 @@ local function on_code_action_results(results, opts) return end - ---@param item {action: lsp.Command|lsp.CodeAction} + ---@param item {action: lsp.Command|lsp.CodeAction, ctx: lsp.HandlerContext} local function format_item(item) - local title = item.action.title:gsub('\r\n', '\\r\\n') - return title:gsub('\n', '\\n') + local clients = vim.lsp.get_clients({ bufnr = item.ctx.bufnr }) + local title = item.action.title:gsub('\r\n', '\\r\\n'):gsub('\n', '\\n') + + if #clients == 1 then + return title + end + + local source = vim.lsp.get_client_by_id(item.ctx.client_id).name + return ('%s [%s]'):format(title, source) end + local select_opts = { prompt = 'Code actions:', kind = 'codeaction', -- cgit From 21151144c6ee0d38841aea78b2e0ecb8a0046429 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 09:33:13 +0100 Subject: feat(meta): add type for quickfix entries --- runtime/lua/vim/_meta/builtin_types.lua | 91 +++++++++++++++++++++++++++++++++ runtime/lua/vim/_meta/vimfn.lua | 4 +- 2 files changed, 93 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/builtin_types.lua b/runtime/lua/vim/_meta/builtin_types.lua index aca6649957..eae76d80d7 100644 --- a/runtime/lua/vim/_meta/builtin_types.lua +++ b/runtime/lua/vim/_meta/builtin_types.lua @@ -66,6 +66,97 @@ --- @field winnr integer --- @field winrow integer +--- @class vim.quickfix.entry +--- buffer number; must be the number of a valid buffer +--- @field bufnr? integer +--- +--- name of a file; only used when "bufnr" is not +--- present or it is invalid. +--- @field filename? string +--- +--- name of a module; if given it will be used in +--- quickfix error window instead of the filename. +--- @field module? string +--- +--- line number in the file +--- @field lnum? integer +--- +--- end of lines, if the item spans multiple lines +--- @field end_lnum? integer +--- +--- search pattern used to locate the error +--- @field pattern? string +--- +--- column number +--- @field col? integer +--- +--- when non-zero: "col" is visual column +--- when zero: "col" is byte index +--- @field vcol? integer +--- +--- end column, if the item spans multiple columns +--- @field end_col? integer +--- +--- error number +--- @field nr? integer +--- +--- description of the error +--- @field text? string +--- +--- single-character error type, 'E', 'W', etc. +--- @field type? string +--- +--- recognized error message +--- @field valid? boolean +--- +--- custom data associated with the item, can be +--- any type. +--- @field user_data? any + +--- @class vim.fn.setqflist.what +--- +--- quickfix list context. See |quickfix-context| +--- @field context? table +--- +--- errorformat to use when parsing text from +--- "lines". If this is not present, then the +--- 'errorformat' option value is used. +--- See |quickfix-parse| +--- @field efm? string +--- +--- quickfix list identifier |quickfix-ID| +--- @field id? integer +--- index of the current entry in the quickfix +--- list specified by "id" or "nr". If set to '$', +--- then the last entry in the list is set as the +--- current entry. See |quickfix-index| +--- @field idx? integer +--- +--- list of quickfix entries. Same as the {list} +--- argument. +--- @field items? vim.quickfix.entry[] +--- +--- use 'errorformat' to parse a list of lines and +--- add the resulting entries to the quickfix list +--- {nr} or {id}. Only a |List| value is supported. +--- See |quickfix-parse| +--- @field lines? string[] +--- +--- list number in the quickfix stack; zero +--- means the current quickfix list and "$" means +--- the last quickfix list. +--- @field nr? integer +--- +--- function to get the text to display in the +--- quickfix window. The value can be the name of +--- a function or a funcref or a lambda. Refer +--- to |quickfix-window-function| for an explanation +--- of how to write the function and an example. +--- @field quickfixtextfunc? function +--- +--- quickfix list title text. See |quickfix-title| +--- @field title? string + --- @class vim.fn.sign_define.dict --- @field text string --- @field icon? string diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index de1937f620..4e5d3f5701 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -8286,9 +8286,9 @@ function vim.fn.setpos(expr, list) end --- independent of the 'errorformat' setting. Use a command like --- `:cc 1` to jump to the first position. --- ---- @param list any[] +--- @param list vim.quickfix.entry[] --- @param action? string ---- @param what? table +--- @param what? vim.fn.setqflist.what --- @return any function vim.fn.setqflist(list, action, what) end -- cgit From 92e4e3fb76c250fa82a704823a5731fde5f220e1 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 15 Oct 2024 17:27:27 +0100 Subject: feat(lsp.util): fix type errors --- runtime/lua/vim/lsp/util.lua | 310 +++++++++++++++++++++++-------------------- 1 file changed, 168 insertions(+), 142 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 092d0a5576..26692e0bcd 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -23,9 +23,8 @@ local default_border = { --- Check the border given by opts or the default border for the additional --- size it adds to a float. ----@param opts table optional options for the floating window ---- - border (string or table) the border ----@return table size of border in the form of { height = height, width = width } +---@param opts? {border:string|(string|[string,string])[]} +---@return {height:integer,width:integer} # size of border in the form of { height = height, width = width } local function get_border_size(opts) local border = opts and opts.border or default_border local height = 0 @@ -58,14 +57,16 @@ local function get_border_size(opts) ) ) end + --- @param id integer local function border_width(id) id = (id - 1) % #border + 1 - if type(border[id]) == 'table' then + local e = border[id] + if type(e) == 'table' then -- border specified as a table of - return vim.fn.strdisplaywidth(border[id][1]) - elseif type(border[id]) == 'string' then + return vim.fn.strdisplaywidth(e[1]) + elseif type(e) == 'string' then -- border specified as a list of border characters - return vim.fn.strdisplaywidth(border[id]) + return vim.fn.strdisplaywidth(e) end error( string.format( @@ -74,14 +75,16 @@ local function get_border_size(opts) ) ) end + --- @param id integer local function border_height(id) id = (id - 1) % #border + 1 - if type(border[id]) == 'table' then + local e = border[id] + if type(e) == 'table' then -- border specified as a table of - return #border[id][1] > 0 and 1 or 0 - elseif type(border[id]) == 'string' then + return #e[1] > 0 and 1 or 0 + elseif type(e) == 'string' then -- border specified as a list of border characters - return #border[id] > 0 and 1 or 0 + return #e > 0 and 1 or 0 end error( string.format( @@ -132,8 +135,8 @@ end --- Convert byte index to `encoding` index. --- Convenience wrapper around vim.str_utfindex ---@param line string line to be indexed ----@param index integer|nil byte index (utf-8), or `nil` for length ----@param encoding 'utf-8'|'utf-16'|'utf-32'|nil defaults to utf-16 +---@param index integer? byte index (utf-8), or `nil` for length +---@param encoding 'utf-8'|'utf-16'|'utf-32'? defaults to utf-16 ---@return integer `encoding` index of `index` in `line` function M._str_utfindex_enc(line, index, encoding) local len32, len16 = vim.str_utfindex(line) @@ -243,6 +246,8 @@ function M.set_lines(lines, A, B, new_lines) return lines end +--- @param fn fun(x:any):any[] +--- @return function local function sort_by_key(fn) return function(a, b) local ka, kb = fn(a), fn(b) @@ -391,10 +396,8 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) vim.bo[bufnr].buflisted = true -- Fix reversed range and indexing each text_edits - local index = 0 - --- @param text_edit lsp.TextEdit - text_edits = vim.tbl_map(function(text_edit) - index = index + 1 + for index, text_edit in ipairs(text_edits) do + --- @cast text_edit lsp.TextEdit|{_index: integer} text_edit._index = index if @@ -406,8 +409,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) text_edit.range.start = text_edit.range['end'] text_edit.range['end'] = start end - return text_edit - end, text_edits) + end -- Sort text_edits ---@param a lsp.TextEdit | { _index: integer } @@ -552,6 +554,9 @@ local function path_components(path) return vim.split(path, '/', { plain = true }) end +--- @param path string[] +--- @param prefix string[] +--- @return boolean local function path_under_prefix(path, prefix) for i, c in ipairs(prefix) do if c ~= path[i] then @@ -565,12 +570,12 @@ end ---@param prefix string ---@return integer[] local function get_bufs_with_prefix(prefix) - prefix = path_components(prefix) + local prefix_parts = path_components(prefix) local buffers = {} for _, v in ipairs(vim.api.nvim_list_bufs()) do local bname = vim.api.nvim_buf_get_name(v) local path = path_components(vim.fs.normalize(bname, { expand_env = false })) - if path_under_prefix(path, prefix) then + if path_under_prefix(path, prefix_parts) then table.insert(buffers, v) end end @@ -701,7 +706,7 @@ end --- Applies a `WorkspaceEdit`. --- ---@param workspace_edit lsp.WorkspaceEdit ----@param offset_encoding string utf-8|utf-16|utf-32 (required) +---@param offset_encoding 'utf-8'|'utf-16'|'utf-32' (required) ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit function M.apply_workspace_edit(workspace_edit, offset_encoding) if offset_encoding == nil then @@ -713,12 +718,13 @@ function M.apply_workspace_edit(workspace_edit, offset_encoding) if workspace_edit.documentChanges then for idx, change in ipairs(workspace_edit.documentChanges) do if change.kind == 'rename' then - M.rename(vim.uri_to_fname(change.oldUri), vim.uri_to_fname(change.newUri), change.options) + local options = change.options --[[@as vim.lsp.util.rename.Opts]] + M.rename(vim.uri_to_fname(change.oldUri), vim.uri_to_fname(change.newUri), options) elseif change.kind == 'create' then create_file(change) elseif change.kind == 'delete' then delete_file(change) - elseif change.kind then + elseif change.kind then --- @diagnostic disable-line:undefined-field error(string.format('Unsupported change: %q', vim.inspect(change))) else M.apply_text_document_edit(change, idx, offset_encoding) @@ -747,7 +753,7 @@ end --- then the corresponding value is returned without further modifications. --- ---@param input lsp.MarkedString|lsp.MarkedString[]|lsp.MarkupContent ----@param contents string[]|nil List of strings to extend with converted lines. Defaults to {}. +---@param contents string[]? List of strings to extend with converted lines. Defaults to {}. ---@return string[] extended with lines of converted markdown. ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover function M.convert_input_to_markdown_lines(input, contents) @@ -784,7 +790,7 @@ end --- ---@param offset integer ---@param contents string[] ----@return { [1]: integer, [2]: integer } +---@return { [1]: integer, [2]: integer }? local function get_pos_from_offset(offset, contents) local i = 0 for l, line in ipairs(contents) do @@ -799,18 +805,18 @@ end --- Converts `textDocument/signatureHelp` response to markdown lines. --- ---@param signature_help lsp.SignatureHelp Response of `textDocument/SignatureHelp` ----@param ft string|nil filetype that will be use as the `lang` for the label markdown code block ----@param triggers table|nil list of trigger characters from the lsp server. used to better determine parameter offsets ----@return string[]|nil table list of lines of converted markdown. ----@return Range4|nil table of active hl +---@param ft string? filetype that will be use as the `lang` for the label markdown code block +---@param triggers string[]? list of trigger characters from the lsp server. used to better determine parameter offsets +---@return string[]? # lines of converted markdown. +---@return Range4? # highlight range for the active parameter ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers) --The active signature. If omitted or the value lies outside the range of --`signatures` the value defaults to zero or is ignored if `signatures.length == 0`. --Whenever possible implementors should make an active decision about --the active signature and shouldn't rely on a default value. - local contents = {} - local active_offset ---@type [integer, integer]|nil + local contents = {} --- @type string[] + local active_offset ---@type [integer, integer]? local active_signature = signature_help.activeSignature or 0 -- If the activeSignature is not inside the valid range, then clip it. -- In 3.15 of the protocol, activeSignature was allowed to be negative @@ -824,12 +830,13 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers label = ('```%s\n%s\n```'):format(ft, label) end list_extend(contents, split(label, '\n', { plain = true, trimempty = true })) - if signature.documentation then + local doc = signature.documentation + if doc then -- if LSP returns plain string, we treat it as plaintext. This avoids -- special characters like underscore or similar from being interpreted -- as markdown font modifiers - if type(signature.documentation) == 'string' then - signature.documentation = { kind = 'plaintext', value = signature.documentation } + if type(doc) == 'string' then + signature.documentation = { kind = 'plaintext', value = doc } end M.convert_input_to_markdown_lines(signature.documentation, contents) end @@ -852,7 +859,7 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers if type(parameter_label) == 'table' then active_offset = parameter_label else - local offset = 1 ---@type integer|nil + local offset = 1 ---@type integer? -- try to set the initial offset to the first found trigger character for _, t in ipairs(triggers or {}) do local trigger_offset = signature.label:find(t, 1, true) @@ -861,7 +868,9 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers end end for p, param in pairs(signature.parameters) do - offset = signature.label:find(param.label, offset, true) + local plabel = param.label + assert(type(plabel) == 'string', 'Expected label to be a string') + offset = signature.label:find(plabel, offset, true) if not offset then break end @@ -879,15 +888,15 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers local active_hl = nil if active_offset then - active_hl = {} -- Account for the start of the markdown block. if ft then - active_offset[1], active_offset[2] = - active_offset[1] + #contents[1], active_offset[2] + #contents[1] + active_offset[1] = active_offset[1] + #contents[1] + active_offset[2] = active_offset[2] + #contents[1] end - list_extend(active_hl, get_pos_from_offset(active_offset[1], contents)) - list_extend(active_hl, get_pos_from_offset(active_offset[2], contents)) + active_hl = {} + list_extend(active_hl, get_pos_from_offset(active_offset[1], contents) or {}) + list_extend(active_hl, get_pos_from_offset(active_offset[2], contents) or {}) end return contents, active_hl @@ -898,19 +907,7 @@ end --- ---@param width integer window width (in character cells) ---@param height integer window height (in character cells) ----@param opts table optional ---- - offset_x (integer) offset to add to `col` ---- - offset_y (integer) offset to add to `row` ---- - border (string or table) override `border` ---- - focusable (string or table) override `focusable` ---- - zindex (string or table) override `zindex`, defaults to 50 ---- - relative ("mouse"|"cursor") defaults to "cursor" ---- - anchor_bias ("auto"|"above"|"below") defaults to "auto" ---- - "auto": place window based on which side of the cursor has more lines ---- - "above": place the window above the cursor unless there are not enough lines ---- to display the full window height. ---- - "below": place the window below the cursor unless there are not enough lines ---- to display the full window height. +---@param opts? vim.lsp.util.open_floating_preview.Opts ---@return table Options function M.make_floating_popup_options(width, height, opts) validate('opts', opts, 'table', true) @@ -919,7 +916,6 @@ function M.make_floating_popup_options(width, height, opts) validate('opts.offset_y', opts.offset_y, 'n', true) local anchor = '' - local row, col local lines_above = opts.relative == 'mouse' and vim.fn.getmousepos().line - 1 or vim.fn.winline() - 1 @@ -927,7 +923,7 @@ function M.make_floating_popup_options(width, height, opts) local anchor_bias = opts.anchor_bias or 'auto' - local anchor_below + local anchor_below --- @type boolean? if anchor_bias == 'below' then anchor_below = (lines_below > lines_above) or (height <= lines_below) @@ -939,6 +935,7 @@ function M.make_floating_popup_options(width, height, opts) end local border_height = get_border_size(opts).height + local row, col --- @type integer?, integer? if anchor_below then anchor = anchor .. 'N' height = math.max(math.min(lines_below - border_height, height), 0) @@ -960,7 +957,7 @@ function M.make_floating_popup_options(width, height, opts) end local title = (opts.border and opts.title) and opts.title or nil - local title_pos + local title_pos --- @type 'left'|'center'|'right'? if title then title_pos = opts.title_pos or 'center' @@ -982,13 +979,21 @@ function M.make_floating_popup_options(width, height, opts) } end +--- @class vim.lsp.util.show_document.Opts +--- @inlinedoc +--- +--- Jump to existing window if buffer is already open. +--- @field reuse_win? boolean +--- +--- Whether to focus/jump to location if possible. +--- (defaults: true) +--- @field focus? boolean + --- Shows document and optionally jumps to the location. --- ---@param location lsp.Location|lsp.LocationLink ----@param offset_encoding string|nil utf-8|utf-16|utf-32 ----@param opts table|nil options ---- - reuse_win (boolean) Jump to existing window if buffer is already open. ---- - focus (boolean) Whether to focus/jump to location if possible. Defaults to true. +---@param offset_encoding 'utf-8'|'utf-16'|'utf-32'? +---@param opts? vim.lsp.util.show_document.Opts ---@return boolean `true` if succeeded function M.show_document(location, offset_encoding, opts) -- location may be Location or LocationLink @@ -1042,8 +1047,8 @@ end --- Jumps to a location. --- ---@param location lsp.Location|lsp.LocationLink ----@param offset_encoding string|nil utf-8|utf-16|utf-32 ----@param reuse_win boolean|nil Jump to existing window if buffer is already open. +---@param offset_encoding string? utf-8|utf-16|utf-32 +---@param reuse_win boolean? Jump to existing window if buffer is already open. ---@return boolean `true` if the jump succeeded function M.jump_to_location(location, offset_encoding, reuse_win) if offset_encoding == nil then @@ -1063,9 +1068,9 @@ end --- - for LocationLink, targetRange is shown (e.g., body of function definition) --- ---@param location lsp.Location|lsp.LocationLink ----@param opts table ----@return integer|nil buffer id of float window ----@return integer|nil window id of float window +---@param opts? vim.lsp.util.open_floating_preview.Opts +---@return integer? buffer id of float window +---@return integer? window id of float window function M.preview_location(location, opts) -- location may be LocationLink or Location (more useful for the former) local uri = location.targetUri or location.uri @@ -1158,8 +1163,10 @@ local function collapse_blank_lines(contents) end local function get_markdown_fences() - local fences = {} - for _, fence in pairs(vim.g.markdown_fenced_languages or {}) do + local fences = {} --- @type table + for _, fence in + pairs(vim.g.markdown_fenced_languages or {} --[[@as string[] ]]) + do local lang, syntax = fence:match('^(.*)=(.*)$') if lang then fences[lang] = syntax @@ -1179,7 +1186,7 @@ end --- ---@param bufnr integer ---@param contents string[] of lines to show in window ----@param opts table with optional fields +---@param opts? table with optional fields --- - height of floating window --- - width of floating window --- - wrap_at character to wrap at for computing height @@ -1201,8 +1208,11 @@ function M.stylize_markdown(bufnr, contents, opts) text = { 'text', '', '' }, } - local match_begin = function(line) + --- @param line string + --- @return {type:string,ft:string}? + local function match_begin(line) for type, pattern in pairs(matchers) do + --- @type string? local ret = line:match(string.format('^%%s*%s%%s*$', pattern[2])) if ret then return { @@ -1213,7 +1223,10 @@ function M.stylize_markdown(bufnr, contents, opts) end end - local match_end = function(line, match) + --- @param line string + --- @param match {type:string,ft:string} + --- @return string + local function match_end(line, match) local pattern = matchers[match.type] return line:match(string.format('^%%s*%s%%s*$', pattern[3])) end @@ -1222,9 +1235,9 @@ function M.stylize_markdown(bufnr, contents, opts) contents = vim.split(table.concat(contents, '\n'), '\n', { trimempty = true }) local stripped = {} - local highlights = {} + local highlights = {} --- @type {ft:string,start:integer,finish:integer}[] -- keep track of lnums that contain markdown - local markdown_lines = {} + local markdown_lines = {} --- @type table do local i = 1 while i <= #contents do @@ -1280,18 +1293,23 @@ function M.stylize_markdown(bufnr, contents, opts) end -- Handle some common html escape sequences - stripped = vim.tbl_map(function(line) - local escapes = { - ['>'] = '>', - ['<'] = '<', - ['"'] = '"', - ['''] = "'", - [' '] = ' ', - [' '] = ' ', - ['&'] = '&', - } - return (string.gsub(line, '&[^ ;]+;', escapes)) - end, stripped) + --- @type string[] + stripped = vim.tbl_map( + --- @param line string + function(line) + local escapes = { + ['>'] = '>', + ['<'] = '<', + ['"'] = '"', + ['''] = "'", + [' '] = ' ', + [' '] = ' ', + ['&'] = '&', + } + return (line:gsub('&[^ ;]+;', escapes)) + end, + stripped + ) -- Compute size of float needed to show (wrapped) lines opts.wrap_at = opts.wrap_at or (vim.wo['wrap'] and api.nvim_win_get_width(0)) @@ -1310,7 +1328,7 @@ function M.stylize_markdown(bufnr, contents, opts) local idx = 1 -- keep track of syntaxes we already included. -- no need to include the same syntax more than once - local langs = {} + local langs = {} --- @type table local fences = get_markdown_fences() local function apply_syntax_to_region(ft, start, finish) if ft == '' then @@ -1333,6 +1351,7 @@ function M.stylize_markdown(bufnr, contents, opts) if #api.nvim_get_runtime_file(('syntax/%s.vim'):format(ft), true) == 0 then return end + --- @diagnostic disable-next-line:param-type-mismatch pcall(vim.cmd, string.format('syntax include %s syntax/%s.vim', lang, ft)) langs[lang] = true end @@ -1408,7 +1427,7 @@ end --- Closes the preview window --- ---@param winnr integer window id of preview window ----@param bufnrs table|nil optional list of ignored buffers +---@param bufnrs table? optional list of ignored buffers local function close_preview_window(winnr, bufnrs) vim.schedule(function() -- exit if we are in one of ignored buffers @@ -1456,13 +1475,8 @@ end ---@private --- Computes size of float needed to show contents (with optional wrapping) --- ----@param contents table of lines to show in window ----@param opts? table with optional fields ---- - height of floating window ---- - width of floating window ---- - wrap_at character to wrap at for computing height ---- - max_width maximal width of floating window ---- - max_height maximal height of floating window +---@param contents string[] of lines to show in window +---@param opts? vim.lsp.util.open_floating_preview.Opts ---@return integer width size of float ---@return integer height size of float function M._make_floating_popup_size(contents, opts) @@ -1475,7 +1489,7 @@ function M._make_floating_popup_size(contents, opts) local wrap_at = opts.wrap_at local max_width = opts.max_width local max_height = opts.max_height - local line_widths = {} + local line_widths = {} --- @type table if not width then width = 0 @@ -1491,12 +1505,10 @@ function M._make_floating_popup_size(contents, opts) width = math.min(width, screen_width) -- make sure borders are always inside the screen - if width + border_width > screen_width then - width = width - (width + border_width - screen_width) - end + width = math.min(width, screen_width - border_width) - if wrap_at and wrap_at > width then - wrap_at = width + if wrap_at then + wrap_at = math.min(wrap_at, width) end if max_width then @@ -1528,7 +1540,6 @@ function M._make_floating_popup_size(contents, opts) end --- @class vim.lsp.util.open_floating_preview.Opts ---- @inlinedoc --- --- Height of floating window --- @field height? integer @@ -1563,6 +1574,27 @@ end --- window with the same {focus_id} --- (default: `true`) --- @field focus? boolean +--- +--- offset to add to `col` +--- @field offset_x? integer +--- +--- offset to add to `row` +--- @field offset_y? integer +--- @field border? (string|[string,string])[] override `border` +--- @field zindex? integer override `zindex`, defaults to 50 +--- @field title? string +--- @field title_pos? 'left'|'center'|'right' +--- +--- (default: `'cursor'`) +--- @field relative? 'mouse'|'cursor' +--- +--- - "auto": place window based on which side of the cursor has more lines +--- - "above": place the window above the cursor unless there are not enough lines +--- to display the full window height. +--- - "below": place the window below the cursor unless there are not enough lines +--- to display the full window height. +--- (default: `'auto'`) +--- @field anchor_bias? 'auto'|'above'|'below' --- Shows contents in a floating window. --- @@ -1676,7 +1708,7 @@ do --[[ References ]] --- Removes document highlights from a buffer. --- - ---@param bufnr integer|nil Buffer id + ---@param bufnr integer? Buffer id function M.buf_clear_references(bufnr) api.nvim_buf_clear_namespace(bufnr or 0, reference_ns, 0, -1) end @@ -1729,16 +1761,6 @@ local position_sort = sort_by_key(function(v) return { v.start.line, v.start.character } end) ----@class vim.lsp.util.locations_to_items.ret ----@inlinedoc ----@field filename string ----@field lnum integer 1-indexed line number ----@field end_lnum integer 1-indexed end line number ----@field col integer 1-indexed column ----@field end_col integer 1-indexed end column ----@field text string ----@field user_data lsp.Location|lsp.LocationLink - --- Returns the items with the byte position calculated correctly and in sorted --- order, for display in quickfix and location lists. --- @@ -1751,7 +1773,7 @@ end) ---@param locations lsp.Location[]|lsp.LocationLink[] ---@param offset_encoding string offset_encoding for locations utf-8|utf-16|utf-32 --- default to first client of buffer ----@return vim.lsp.util.locations_to_items.ret[] +---@return vim.quickfix.entry[] # See |setqflist()| for the format function M.locations_to_items(locations, offset_encoding) if offset_encoding == nil then vim.notify_once( @@ -1761,7 +1783,7 @@ function M.locations_to_items(locations, offset_encoding) offset_encoding = vim.lsp.get_clients({ bufnr = 0 })[1].offset_encoding end - local items = {} + local items = {} --- @type vim.quickfix.entry[] ---@type table local grouped = setmetatable({}, { __index = function(t, k) @@ -1777,11 +1799,7 @@ function M.locations_to_items(locations, offset_encoding) table.insert(grouped[uri], { start = range.start, ['end'] = range['end'], location = d }) end - ---@type string[] - local keys = vim.tbl_keys(grouped) - table.sort(keys) - -- TODO(ashkan) I wish we could do this lazily. - for _, uri in ipairs(keys) do + for uri in vim.spairs(grouped) do local rows = grouped[uri] table.sort(rows, position_sort) local filename = vim.uri_to_fname(uri) @@ -1807,7 +1825,7 @@ function M.locations_to_items(locations, offset_encoding) local col = M._str_byteindex_enc(line, pos.character, offset_encoding) local end_col = M._str_byteindex_enc(end_line, end_pos.character, offset_encoding) - table.insert(items, { + items[#items + 1] = { filename = filename, lnum = row + 1, end_lnum = end_row + 1, @@ -1815,7 +1833,7 @@ function M.locations_to_items(locations, offset_encoding) end_col = end_col + 1, text = line, user_data = temp.location, - }) + } end end return items @@ -1830,31 +1848,36 @@ end --- Converts symbols to quickfix list items. --- ----@param symbols table DocumentSymbol[] or SymbolInformation[] +---@param symbols lsp.DocumentSymbol[]|lsp.SymbolInformation[] ---@param bufnr? integer +---@return vim.quickfix.entry[] # See |setqflist()| for the format function M.symbols_to_items(symbols, bufnr) + ---@param _symbols lsp.DocumentSymbol[]|lsp.SymbolInformation[] + ---@param _items vim.quickfix.entry[] + ---@param _bufnr integer + ---@return vim.quickfix.entry[] local function _symbols_to_items(_symbols, _items, _bufnr) for _, symbol in ipairs(_symbols) do if symbol.location then -- SymbolInformation type local range = symbol.location.range local kind = M._get_symbol_kind_name(symbol.kind) - table.insert(_items, { + _items[#_items + 1] = { filename = vim.uri_to_fname(symbol.location.uri), lnum = range.start.line + 1, col = range.start.character + 1, kind = kind, text = '[' .. kind .. '] ' .. symbol.name, - }) + } elseif symbol.selectionRange then -- DocumentSymbole type local kind = M._get_symbol_kind_name(symbol.kind) - table.insert(_items, { + _items[#_items + 1] = { -- bufnr = _bufnr, filename = api.nvim_buf_get_name(_bufnr), lnum = symbol.selectionRange.start.line + 1, col = symbol.selectionRange.start.character + 1, kind = kind, text = '[' .. kind .. '] ' .. symbol.name, - }) + } if symbol.children then for _, v in ipairs(_symbols_to_items(symbol.children, _items, _bufnr)) do for _, s in ipairs(v) do @@ -1889,7 +1912,7 @@ function M.trim_empty_lines(lines) break end end - return list_extend({}, lines, start, finish) + return vim.list_slice(lines, start, finish) end --- Accepts markdown lines and tries to reduce them to a filetype if they @@ -1922,8 +1945,8 @@ function M.try_trim_markdown_code_blocks(lines) return 'markdown' end ----@param window integer|nil: window handle or 0 for current, defaults to current ----@param offset_encoding? string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window` +---@param window integer?: window handle or 0 for current, defaults to current +---@param offset_encoding? 'utf-8'|'utf-16'|'utf-32'? defaults to `offset_encoding` of first client of buffer of `window` local function make_position_param(window, offset_encoding) window = window or 0 local buf = api.nvim_win_get_buf(window) @@ -1942,8 +1965,8 @@ end --- Creates a `TextDocumentPositionParams` object for the current buffer and cursor position. --- ----@param window integer|nil: window handle or 0 for current, defaults to current ----@param offset_encoding string|nil utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window` +---@param window integer?: window handle or 0 for current, defaults to current +---@param offset_encoding 'utf-8'|'utf-16'|'utf-32'? defaults to `offset_encoding` of first client of buffer of `window` ---@return lsp.TextDocumentPositionParams ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams function M.make_position_params(window, offset_encoding) @@ -1962,7 +1985,7 @@ end function M._get_offset_encoding(bufnr) validate('bufnr', bufnr, 'number', true) - local offset_encoding + local offset_encoding --- @type 'utf-8'|'utf-16'|'utf-32'? for _, client in pairs(vim.lsp.get_clients({ bufnr = bufnr })) do if client.offset_encoding == nil then @@ -1993,8 +2016,8 @@ end --- `textDocument/codeAction`, `textDocument/colorPresentation`, --- `textDocument/rangeFormatting`. --- ----@param window integer|nil: window handle or 0 for current, defaults to current ----@param offset_encoding "utf-8"|"utf-16"|"utf-32"|nil defaults to `offset_encoding` of first client of buffer of `window` +---@param window integer? window handle or 0 for current, defaults to current +---@param offset_encoding "utf-8"|"utf-16"|"utf-32"? defaults to `offset_encoding` of first client of buffer of `window` ---@return table { textDocument = { uri = `current_file_uri` }, range = { start = ---`current_position`, end = `current_position` } } function M.make_range_params(window, offset_encoding) @@ -2010,12 +2033,12 @@ end --- Using the given range in the current buffer, creates an object that --- is similar to |vim.lsp.util.make_range_params()|. --- ----@param start_pos integer[]|nil {row,col} mark-indexed position. +---@param start_pos [integer,integer]? {row,col} mark-indexed position. --- Defaults to the start of the last visual selection. ----@param end_pos integer[]|nil {row,col} mark-indexed position. +---@param end_pos [integer,integer]? {row,col} mark-indexed position. --- Defaults to the end of the last visual selection. ----@param bufnr integer|nil buffer handle or 0 for current, defaults to current ----@param offset_encoding "utf-8"|"utf-16"|"utf-32"|nil defaults to `offset_encoding` of first client of `bufnr` +---@param bufnr integer? buffer handle or 0 for current, defaults to current +---@param offset_encoding 'utf-8'|'utf-16'|'utf-32'? defaults to `offset_encoding` of first client of `bufnr` ---@return table { textDocument = { uri = `current_file_uri` }, range = { start = ---`start_position`, end = `end_position` } } function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding) @@ -2024,7 +2047,9 @@ function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding) validate('offset_encoding', offset_encoding, 'string', true) bufnr = bufnr or api.nvim_get_current_buf() offset_encoding = offset_encoding or M._get_offset_encoding(bufnr) + --- @type integer[] local A = list_extend({}, start_pos or api.nvim_buf_get_mark(bufnr, '<')) + --- @type integer[] local B = list_extend({}, end_pos or api.nvim_buf_get_mark(bufnr, '>')) -- convert to 0-index A[1] = A[1] - 1 @@ -2053,7 +2078,7 @@ end --- Creates a `TextDocumentIdentifier` object for the current buffer. --- ----@param bufnr integer|nil: Buffer handle, defaults to current +---@param bufnr integer?: Buffer handle, defaults to current ---@return lsp.TextDocumentIdentifier ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentIdentifier function M.make_text_document_params(bufnr) @@ -2071,7 +2096,7 @@ end --- Returns indentation size. --- ---@see 'shiftwidth' ----@param bufnr integer|nil: Buffer handle, defaults to current +---@param bufnr integer?: Buffer handle, defaults to current ---@return integer indentation size function M.get_effective_tabstop(bufnr) validate('bufnr', bufnr, 'number', true) @@ -2082,7 +2107,7 @@ end --- Creates a `DocumentFormattingParams` object for the current buffer and cursor position. --- ----@param options lsp.FormattingOptions|nil with valid `FormattingOptions` entries +---@param options lsp.FormattingOptions? with valid `FormattingOptions` entries ---@return lsp.DocumentFormattingParams object ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting function M.make_formatting_params(options) @@ -2125,6 +2150,7 @@ end function M.lookup_section(settings, section) vim.deprecate('vim.lsp.util.lookup_section()', 'vim.tbl_get() with `vim.split`', '0.12') for part in vim.gsplit(section, '.', { plain = true }) do + --- @diagnostic disable-next-line:no-unknown settings = settings[part] if settings == nil then return vim.NIL -- cgit From 1edfe5c09ebcb9e81464f805f99d276de4bcef9b Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 09:45:12 +0100 Subject: feat(lsp.util): use vim.api alias --- runtime/lua/vim/lsp/util.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 26692e0bcd..e1027685ee 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -125,10 +125,10 @@ local function split_lines(s, no_blank) end local function create_window_without_focus() - local prev = vim.api.nvim_get_current_win() + local prev = api.nvim_get_current_win() vim.cmd.new() - local new = vim.api.nvim_get_current_win() - vim.api.nvim_set_current_win(prev) + local new = api.nvim_get_current_win() + api.nvim_set_current_win(prev) return new end @@ -496,7 +496,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) -- make sure we don't go out of bounds pos[1] = math.min(pos[1], max) pos[2] = math.min(pos[2], #(get_line(bufnr, pos[1] - 1) or '')) - vim.api.nvim_buf_set_mark(bufnr or 0, mark, pos[1], pos[2], {}) + api.nvim_buf_set_mark(bufnr or 0, mark, pos[1], pos[2], {}) end end @@ -572,8 +572,8 @@ end local function get_bufs_with_prefix(prefix) local prefix_parts = path_components(prefix) local buffers = {} - for _, v in ipairs(vim.api.nvim_list_bufs()) do - local bname = vim.api.nvim_buf_get_name(v) + for _, v in ipairs(api.nvim_list_bufs()) do + local bname = api.nvim_buf_get_name(v) local path = path_components(vim.fs.normalize(bname, { expand_env = false })) if path_under_prefix(path, prefix_parts) then table.insert(buffers, v) @@ -632,7 +632,7 @@ function M.rename(old_fname, new_fname, opts) -- conflicting buffer may not be associated with a file. For example, 'buftype' can be "nofile" -- or "nowrite", or the buffer can be a normal buffer but has not been written to the file yet. -- Renaming should fail in such cases to avoid losing the contents of the conflicting buffer. - local old_bname = vim.api.nvim_buf_get_name(b) + local old_bname = api.nvim_buf_get_name(b) local new_bname = old_bname:gsub(old_fname_pat, escape_gsub_repl(new_fname)) if vim.fn.bufexists(new_bname) == 1 then local existing_buf = vim.fn.bufnr(new_bname) @@ -2173,7 +2173,7 @@ local function make_line_range_params(bufnr, start_line, end_line, offset_encodi ---@type lsp.Position local end_pos - if end_line == last_line and not vim.api.nvim_get_option_value('endofline', { buf = bufnr }) then + if end_line == last_line and not vim.bo[bufnr].endofline then end_pos = { line = end_line, character = M.character_offset(bufnr, end_line, #get_line(bufnr, end_line), offset_encoding), -- cgit From 5bec7288a5b56f1ac38673e59e32f6be774558f3 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 09:48:27 +0100 Subject: feat(lsp.util): remove uneeded do-end --- runtime/lua/vim/lsp/util.lua | 93 ++++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 47 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index e1027685ee..4922c9225e 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1238,57 +1238,56 @@ function M.stylize_markdown(bufnr, contents, opts) local highlights = {} --- @type {ft:string,start:integer,finish:integer}[] -- keep track of lnums that contain markdown local markdown_lines = {} --- @type table - do - local i = 1 - while i <= #contents do - local line = contents[i] - local match = match_begin(line) - if match then - local start = #stripped - i = i + 1 - while i <= #contents do - line = contents[i] - if match_end(line, match) then - i = i + 1 - break - end - table.insert(stripped, line) + + local i = 1 + while i <= #contents do + local line = contents[i] + local match = match_begin(line) + if match then + local start = #stripped + i = i + 1 + while i <= #contents do + line = contents[i] + if match_end(line, match) then i = i + 1 + break end - table.insert(highlights, { - ft = match.ft, - start = start + 1, - finish = #stripped, - }) - -- add a separator, but not on the last line - if opts.separator and i < #contents then - table.insert(stripped, '---') - markdown_lines[#stripped] = true - end - else - -- strip any empty lines or separators prior to this separator in actual markdown - if line:match('^---+$') then - while - markdown_lines[#stripped] - and (stripped[#stripped]:match('^%s*$') or stripped[#stripped]:match('^---+$')) - do - markdown_lines[#stripped] = false - table.remove(stripped, #stripped) - end - end - -- add the line if its not an empty line following a separator - if - not ( - line:match('^%s*$') - and markdown_lines[#stripped] - and stripped[#stripped]:match('^---+$') - ) - then - table.insert(stripped, line) - markdown_lines[#stripped] = true - end + table.insert(stripped, line) i = i + 1 end + table.insert(highlights, { + ft = match.ft, + start = start + 1, + finish = #stripped, + }) + -- add a separator, but not on the last line + if opts.separator and i < #contents then + table.insert(stripped, '---') + markdown_lines[#stripped] = true + end + else + -- strip any empty lines or separators prior to this separator in actual markdown + if line:match('^---+$') then + while + markdown_lines[#stripped] + and (stripped[#stripped]:match('^%s*$') or stripped[#stripped]:match('^---+$')) + do + markdown_lines[#stripped] = false + table.remove(stripped, #stripped) + end + end + -- add the line if its not an empty line following a separator + if + not ( + line:match('^%s*$') + and markdown_lines[#stripped] + and stripped[#stripped]:match('^---+$') + ) + then + table.insert(stripped, line) + markdown_lines[#stripped] = true + end + i = i + 1 end end -- cgit From f0973d42272e9474c758c87697e3803e1796d17c Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 09:58:10 +0100 Subject: feat(lsp.util): refactor symbols_to_items() - Remove the trivial function vim.lsp.util._get_symbol_kind_name() and its tests. --- runtime/lua/vim/lsp/util.lua | 74 +++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 43 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 4922c9225e..2101e940f0 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1838,57 +1838,45 @@ function M.locations_to_items(locations, offset_encoding) return items end --- According to LSP spec, if the client set "symbolKind.valueSet", --- the client must handle it properly even if it receives a value outside the specification. --- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol -function M._get_symbol_kind_name(symbol_kind) - return protocol.SymbolKind[symbol_kind] or 'Unknown' -end - --- Converts symbols to quickfix list items. --- ---@param symbols lsp.DocumentSymbol[]|lsp.SymbolInformation[] ---@param bufnr? integer ---@return vim.quickfix.entry[] # See |setqflist()| for the format function M.symbols_to_items(symbols, bufnr) - ---@param _symbols lsp.DocumentSymbol[]|lsp.SymbolInformation[] - ---@param _items vim.quickfix.entry[] - ---@param _bufnr integer - ---@return vim.quickfix.entry[] - local function _symbols_to_items(_symbols, _items, _bufnr) - for _, symbol in ipairs(_symbols) do - if symbol.location then -- SymbolInformation type - local range = symbol.location.range - local kind = M._get_symbol_kind_name(symbol.kind) - _items[#_items + 1] = { - filename = vim.uri_to_fname(symbol.location.uri), - lnum = range.start.line + 1, - col = range.start.character + 1, - kind = kind, - text = '[' .. kind .. '] ' .. symbol.name, - } - elseif symbol.selectionRange then -- DocumentSymbole type - local kind = M._get_symbol_kind_name(symbol.kind) - _items[#_items + 1] = { - -- bufnr = _bufnr, - filename = api.nvim_buf_get_name(_bufnr), - lnum = symbol.selectionRange.start.line + 1, - col = symbol.selectionRange.start.character + 1, - kind = kind, - text = '[' .. kind .. '] ' .. symbol.name, - } - if symbol.children then - for _, v in ipairs(_symbols_to_items(symbol.children, _items, _bufnr)) do - for _, s in ipairs(v) do - table.insert(_items, s) - end - end - end - end + bufnr = bufnr or 0 + local items = {} --- @type vim.quickfix.entry[] + for _, symbol in ipairs(symbols) do + --- @type string?, lsp.Position? + local filename, pos + + if symbol.location then + --- @cast symbol lsp.SymbolInformation + filename = vim.uri_to_fname(symbol.location.uri) + pos = symbol.location.range.start + elseif symbol.selectionRange then + --- @cast symbol lsp.DocumentSymbol + filename = api.nvim_buf_get_name(bufnr) + pos = symbol.selectionRange.start + end + + if filename and pos then + local kind = protocol.SymbolKind[symbol.kind] or 'Unknown' + items[#items + 1] = { + filename = filename, + lnum = pos.line + 1, + col = pos.character + 1, + kind = kind, + text = '[' .. kind .. '] ' .. symbol.name, + } + end + + if symbol.children then + list_extend(items, M.symbols_to_items(symbol.children, bufnr)) end - return _items end - return _symbols_to_items(symbols, {}, bufnr or 0) + + return items end --- Removes empty lines from the beginning and end. -- cgit From 0621718e3b5f522ad2df9ef7d4332fa324bc8db5 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 10:06:59 +0100 Subject: feat(lsp.util): remove metatable in locations_to_items --- runtime/lua/vim/lsp/util.lua | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 2101e940f0..d42b33f7a0 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1783,18 +1783,14 @@ function M.locations_to_items(locations, offset_encoding) end local items = {} --- @type vim.quickfix.entry[] + ---@type table - local grouped = setmetatable({}, { - __index = function(t, k) - local v = {} - rawset(t, k, v) - return v - end, - }) + local grouped = {} for _, d in ipairs(locations) do -- locations may be Location or LocationLink local uri = d.uri or d.targetUri local range = d.range or d.targetSelectionRange + grouped[uri] = grouped[uri] or {} table.insert(grouped[uri], { start = range.start, ['end'] = range['end'], location = d }) end -- cgit From d44d36b8ff6cbd966daefc4ea9b775236175c887 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 10:34:37 +0100 Subject: feat(lsp.util): simplify some bounds checking --- runtime/lua/vim/lsp/util.lua | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index d42b33f7a0..f77597e40d 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -843,16 +843,12 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers if signature.parameters and #signature.parameters > 0 then -- First check if the signature has an activeParameter. If it doesn't check if the response -- had that property instead. Else just default to 0. - local active_parameter = (signature.activeParameter or signature_help.activeParameter or 0) - if active_parameter < 0 then - active_parameter = 0 - end + local active_parameter = + math.max(signature.activeParameter or signature_help.activeParameter or 0, 0) -- If the activeParameter is > #parameters, then set it to the last -- NOTE: this is not fully according to the spec, but a client-side interpretation - if active_parameter >= #signature.parameters then - active_parameter = #signature.parameters - 1 - end + active_parameter = math.min(active_parameter, #signature.parameters - 1) local parameter = signature.parameters[active_parameter + 1] local parameter_label = parameter.label -- cgit From 1944c0d610ce1616f0e4c93fca22b614361224e7 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 12:49:14 +0100 Subject: feat(lsp.util): refactor get_border_size() --- runtime/lua/vim/lsp/util.lua | 130 ++++++++++++++++++++----------------------- 1 file changed, 59 insertions(+), 71 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index f77597e40d..3ae9e6669f 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -21,85 +21,73 @@ local default_border = { { ' ', 'NormalFloat' }, } +--- @param border string|(string|[string,string])[] +local function border_error(border) + error( + string.format( + 'invalid floating preview border: %s. :help vim.api.nvim_open_win()', + vim.inspect(border) + ), + 2 + ) +end + +local border_size = { + none = { 0, 0 }, + single = { 2, 2 }, + double = { 2, 2 }, + rounded = { 2, 2 }, + solid = { 2, 2 }, + shadow = { 1, 1 }, +} + --- Check the border given by opts or the default border for the additional --- size it adds to a float. ----@param opts? {border:string|(string|[string,string])[]} ----@return {height:integer,width:integer} # size of border in the form of { height = height, width = width } +--- @param opts? {border:string|(string|[string,string])[]} +--- @return integer height +--- @return integer width local function get_border_size(opts) local border = opts and opts.border or default_border - local height = 0 - local width = 0 if type(border) == 'string' then - local border_size = { - none = { 0, 0 }, - single = { 2, 2 }, - double = { 2, 2 }, - rounded = { 2, 2 }, - solid = { 2, 2 }, - shadow = { 1, 1 }, - } - if border_size[border] == nil then - error( - string.format( - 'invalid floating preview border: %s. :help vim.api.nvim_open_win()', - vim.inspect(border) - ) - ) - end - height, width = unpack(border_size[border]) - else - if 8 % #border ~= 0 then - error( - string.format( - 'invalid floating preview border: %s. :help vim.api.nvim_open_win()', - vim.inspect(border) - ) - ) - end - --- @param id integer - local function border_width(id) - id = (id - 1) % #border + 1 - local e = border[id] - if type(e) == 'table' then - -- border specified as a table of - return vim.fn.strdisplaywidth(e[1]) - elseif type(e) == 'string' then - -- border specified as a list of border characters - return vim.fn.strdisplaywidth(e) - end - error( - string.format( - 'invalid floating preview border: %s. :help vim.api.nvim_open_win()', - vim.inspect(border) - ) - ) + if not border_size[border] then + border_error(border) end - --- @param id integer - local function border_height(id) - id = (id - 1) % #border + 1 - local e = border[id] - if type(e) == 'table' then - -- border specified as a table of - return #e[1] > 0 and 1 or 0 - elseif type(e) == 'string' then - -- border specified as a list of border characters - return #e > 0 and 1 or 0 - end - error( - string.format( - 'invalid floating preview border: %s. :help vim.api.nvim_open_win()', - vim.inspect(border) - ) - ) + return unpack(border_size[border]) + end + + if 8 % #border ~= 0 then + border_error(border) + end + + --- @param id integer + --- @return string + local function elem(id) + id = (id - 1) % #border + 1 + local e = border[id] + if type(e) == 'table' then + -- border specified as a table of + return e[1] + elseif type(e) == 'string' then + -- border specified as a list of border characters + return e end - height = height + border_height(2) -- top - height = height + border_height(6) -- bottom - width = width + border_width(4) -- right - width = width + border_width(8) -- left + --- @diagnostic disable-next-line:missing-return + border_error(border) end - return { height = height, width = width } + --- @param e string + local function border_height(e) + return #e > 0 and 1 or 0 + end + + local top, bottom = elem(2), elem(6) + local height = border_height(top) + border_height(bottom) + + local right, left = elem(4), elem(8) + local width = vim.fn.strdisplaywidth(right) + vim.fn.strdisplaywidth(left) + + return height, width end --- Splits string at newlines, optionally removing unwanted blank lines. @@ -930,7 +918,7 @@ function M.make_floating_popup_options(width, height, opts) anchor_below = lines_below > lines_above end - local border_height = get_border_size(opts).height + local border_height = get_border_size(opts) local row, col --- @type integer?, integer? if anchor_below then anchor = anchor .. 'N' @@ -1495,7 +1483,7 @@ function M._make_floating_popup_size(contents, opts) end end - local border_width = get_border_size(opts).width + local _, border_width = get_border_size(opts) local screen_width = api.nvim_win_get_width(0) width = math.min(width, screen_width) -- cgit From ff1d7d42995931d17395223cec6fb2031a870d15 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 13:10:21 +0100 Subject: feat(lsp.util): get_bufs_with_prefix -> get_writeable_bufs --- runtime/lua/vim/lsp/util.lua | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 3ae9e6669f..31a9fdb2fc 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -554,17 +554,24 @@ local function path_under_prefix(path, prefix) return true end ---- Get list of buffers whose filename matches the given path prefix (normalized full path) +--- Get list of loaded writable buffers whose filename matches the given path +--- prefix (normalized full path). ---@param prefix string ---@return integer[] -local function get_bufs_with_prefix(prefix) +local function get_writable_bufs(prefix) local prefix_parts = path_components(prefix) - local buffers = {} - for _, v in ipairs(api.nvim_list_bufs()) do - local bname = api.nvim_buf_get_name(v) - local path = path_components(vim.fs.normalize(bname, { expand_env = false })) - if path_under_prefix(path, prefix_parts) then - table.insert(buffers, v) + local buffers = {} --- @type integer[] + for _, buf in ipairs(api.nvim_list_bufs()) do + -- No need to care about unloaded or nofile buffers. Also :saveas won't work for them. + if + api.nvim_buf_is_loaded(buf) + and not vim.list_contains({ 'nofile', 'nowrite' }, vim.bo[buf].buftype) + then + local bname = api.nvim_buf_get_name(buf) + local path = path_components(vim.fs.normalize(bname, { expand_env = false })) + if path_under_prefix(path, prefix_parts) then + buffers[#buffers + 1] = buf + end end end return buffers @@ -608,13 +615,7 @@ function M.rename(old_fname, new_fname, opts) local buf_rename = {} ---@type table local old_fname_pat = '^' .. vim.pesc(old_fname_full) - for b in - vim.iter(get_bufs_with_prefix(old_fname_full)):filter(function(b) - -- No need to care about unloaded or nofile buffers. Also :saveas won't work for them. - return api.nvim_buf_is_loaded(b) - and not vim.list_contains({ 'nofile', 'nowrite' }, vim.bo[b].buftype) - end) - do + for _, b in ipairs(get_writable_bufs(old_fname_full)) do -- Renaming a buffer may conflict with another buffer that happens to have the same name. In -- most cases, this would have been already detected by the file conflict check above, but the -- conflicting buffer may not be associated with a file. For example, 'buftype' can be "nofile" -- cgit From acbc6a7f91d15fe5f59df08227d196156fafedb4 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 13:17:10 +0100 Subject: fix(lsp.util): inconsistent handling of offset_encoding --- runtime/lua/vim/lsp/_tagfunc.lua | 7 ++++++- runtime/lua/vim/lsp/buf.lua | 2 ++ runtime/lua/vim/lsp/util.lua | 7 +++++-- 3 files changed, 13 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/_tagfunc.lua b/runtime/lua/vim/lsp/_tagfunc.lua index 4ad50e4a58..70917de48d 100644 --- a/runtime/lua/vim/lsp/_tagfunc.lua +++ b/runtime/lua/vim/lsp/_tagfunc.lua @@ -27,15 +27,20 @@ local function query_definition(pattern) return {} end local results = {} + + --- @param range lsp.Range + --- @param uri string + --- @param offset_encoding string local add = function(range, uri, offset_encoding) table.insert(results, mk_tag_item(pattern, range, uri, offset_encoding)) end + for client_id, lsp_results in pairs(assert(results_by_client)) do local client = lsp.get_client_by_id(client_id) local offset_encoding = client and client.offset_encoding or 'utf-16' local result = lsp_results.result or {} if result.range then -- Location - add(result.range, result.uri) + add(result.range, result.uri, offset_encoding) else result = result --[[@as (lsp.Location[]|lsp.LocationLink[])]] for _, item in pairs(result) do diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 301c1f0cb6..e3f4beeeb1 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -338,6 +338,8 @@ function M.rename(new_name, opts) -- Compute early to account for cursor movements after going async local cword = vim.fn.expand('') + --- @param range lsp.Range + --- @param offset_encoding string local function get_text_at_range(range, offset_encoding) return api.nvim_buf_get_text( bufnr, diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 31a9fdb2fc..aed742f001 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -347,7 +347,7 @@ end --- Position is a https://microsoft.github.io/language-server-protocol/specifications/specification-current/#position ---@param position lsp.Position ----@param offset_encoding? string utf-8|utf-16|utf-32 +---@param offset_encoding 'utf-8'|'utf-16'|'utf-32' ---@return integer local function get_line_byte_from_position(bufnr, position, offset_encoding) -- LSP's line and characters are 0-indexed @@ -357,7 +357,7 @@ local function get_line_byte_from_position(bufnr, position, offset_encoding) -- character if col > 0 then local line = get_line(bufnr, position.line) or '' - return M._str_byteindex_enc(line, col, offset_encoding or 'utf-16') + return M._str_byteindex_enc(line, col, offset_encoding) end return col end @@ -512,6 +512,7 @@ function M.apply_text_document_edit(text_document_edit, index, offset_encoding) 'apply_text_document_edit must be called with valid offset encoding', vim.log.levels.WARN ) + return end -- For lists of text document edits, @@ -703,6 +704,7 @@ function M.apply_workspace_edit(workspace_edit, offset_encoding) 'apply_workspace_edit must be called with valid offset encoding', vim.log.levels.WARN ) + return end if workspace_edit.documentChanges then for idx, change in ipairs(workspace_edit.documentChanges) do @@ -988,6 +990,7 @@ function M.show_document(location, offset_encoding, opts) end if offset_encoding == nil then vim.notify_once('show_document must be called with valid offset encoding', vim.log.levels.WARN) + return false end local bufnr = vim.uri_to_bufnr(uri) -- cgit From 8ad000ef7cf0409e2575d39c8061e17297b0cfec Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 13:32:12 +0100 Subject: feat(lsp.util): remove unneeded table --- runtime/lua/vim/lsp/util.lua | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index aed742f001..be43087a2c 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -428,47 +428,45 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) text_edit.newText, _ = string.gsub(text_edit.newText, '\r\n?', '\n') -- Convert from LSP style ranges to Neovim style ranges. - local e = { - start_row = text_edit.range.start.line, - start_col = get_line_byte_from_position(bufnr, text_edit.range.start, offset_encoding), - end_row = text_edit.range['end'].line, - end_col = get_line_byte_from_position(bufnr, text_edit.range['end'], offset_encoding), - text = split(text_edit.newText, '\n', { plain = true }), - } + local start_row = text_edit.range.start.line + local start_col = get_line_byte_from_position(bufnr, text_edit.range.start, offset_encoding) + local end_row = text_edit.range['end'].line + local end_col = get_line_byte_from_position(bufnr, text_edit.range['end'], offset_encoding) + local text = split(text_edit.newText, '\n', { plain = true }) local max = api.nvim_buf_line_count(bufnr) -- If the whole edit is after the lines in the buffer we can simply add the new text to the end -- of the buffer. - if max <= e.start_row then - api.nvim_buf_set_lines(bufnr, max, max, false, e.text) + if max <= start_row then + api.nvim_buf_set_lines(bufnr, max, max, false, text) else - local last_line_len = #(get_line(bufnr, math.min(e.end_row, max - 1)) or '') + local last_line_len = #(get_line(bufnr, math.min(end_row, max - 1)) or '') -- Some LSP servers may return +1 range of the buffer content but nvim_buf_set_text can't -- accept it so we should fix it here. - if max <= e.end_row then - e.end_row = max - 1 - e.end_col = last_line_len + if max <= end_row then + end_row = max - 1 + end_col = last_line_len has_eol_text_edit = true else - -- If the replacement is over the end of a line (i.e. e.end_col is equal to the line length and the + -- If the replacement is over the end of a line (i.e. end_col is equal to the line length and the -- replacement text ends with a newline We can likely assume that the replacement is assumed -- to be meant to replace the newline with another newline and we need to make sure this -- doesn't add an extra empty line. E.g. when the last line to be replaced contains a '\r' -- in the file some servers (clangd on windows) will include that character in the line -- while nvim_buf_set_text doesn't count it as part of the line. if - e.end_col >= last_line_len - and text_edit.range['end'].character > e.end_col + end_col >= last_line_len + and text_edit.range['end'].character > end_col and #text_edit.newText > 0 and string.sub(text_edit.newText, -1) == '\n' then - table.remove(e.text, #e.text) + table.remove(text, #text) end end - -- Make sure we don't go out of bounds for e.end_col - e.end_col = math.min(last_line_len, e.end_col) + -- Make sure we don't go out of bounds for end_col + end_col = math.min(last_line_len, end_col) - api.nvim_buf_set_text(bufnr, e.start_row, e.start_col, e.end_row, e.end_col, e.text) + api.nvim_buf_set_text(bufnr, start_row, start_col, end_row, end_col, text) end end -- cgit From 3f87e222f79bcbd4f6c863128064954d62da5b75 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 13:33:07 +0100 Subject: feat(lsp.util): remove some aliases --- runtime/lua/vim/lsp/util.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index be43087a2c..790a7001ad 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -6,7 +6,6 @@ local highlight = vim.highlight local uv = vim.uv local npcall = vim.F.npcall -local split = vim.split local M = {} @@ -432,7 +431,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) local start_col = get_line_byte_from_position(bufnr, text_edit.range.start, offset_encoding) local end_row = text_edit.range['end'].line local end_col = get_line_byte_from_position(bufnr, text_edit.range['end'], offset_encoding) - local text = split(text_edit.newText, '\n', { plain = true }) + local text = vim.split(text_edit.newText, '\n', { plain = true }) local max = api.nvim_buf_line_count(bufnr) -- If the whole edit is after the lines in the buffer we can simply add the new text to the end @@ -818,7 +817,7 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers -- wrap inside a code block for proper rendering label = ('```%s\n%s\n```'):format(ft, label) end - list_extend(contents, split(label, '\n', { plain = true, trimempty = true })) + list_extend(contents, vim.split(label, '\n', { plain = true, trimempty = true })) local doc = signature.documentation if doc then -- if LSP returns plain string, we treat it as plaintext. This avoids -- cgit From 97119a236937f47dab2d16321ce062b653bf3f2c Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 13:35:03 +0100 Subject: feat(lsp.util): use vim.w/b --- runtime/lua/vim/lsp/util.lua | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 790a7001ad..4953cb0fbc 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -5,8 +5,6 @@ local list_extend = vim.list_extend local highlight = vim.highlight local uv = vim.uv -local npcall = vim.F.npcall - local M = {} local default_border = { @@ -1082,7 +1080,7 @@ end local function find_window_by_var(name, value) for _, win in ipairs(api.nvim_list_wins()) do - if npcall(api.nvim_win_get_var, win, name) == value then + if vim.w[win][name] == value then return win end end @@ -1604,7 +1602,7 @@ function M.open_floating_preview(contents, syntax, opts) if opts.focus_id and opts.focusable ~= false and opts.focus then -- Go back to previous window if we are in a focusable one local current_winnr = api.nvim_get_current_win() - if npcall(api.nvim_win_get_var, current_winnr, opts.focus_id) then + if vim.w[current_winnr][opts.focus_id] then api.nvim_command('wincmd p') return bufnr, current_winnr end @@ -1621,7 +1619,7 @@ function M.open_floating_preview(contents, syntax, opts) -- check if another floating preview already exists for this buffer -- and close it if needed - local existing_float = npcall(api.nvim_buf_get_var, bufnr, 'lsp_floating_preview') + local existing_float = vim.b[bufnr].lsp_floating_preview if existing_float and api.nvim_win_is_valid(existing_float) then api.nvim_win_close(existing_float, true) end -- cgit From e954a16063e61c2c16f2a17885ab8377e07d4e2f Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 13:38:29 +0100 Subject: feat(lsp.util): remove some variables --- runtime/lua/vim/lsp/util.lua | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 4953cb0fbc..e0e93678e2 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -510,17 +510,12 @@ function M.apply_text_document_edit(text_document_edit, index, offset_encoding) return end - -- For lists of text document edits, - -- do not check the version after the first edit. - local should_check_version = true - if index and index > 1 then - should_check_version = false - end - -- `VersionedTextDocumentIdentifier`s version may be null -- https://microsoft.github.io/language-server-protocol/specification#versionedTextDocumentIdentifier if - should_check_version + -- For lists of text document edits, + -- do not check the version after the first edit. + not (index and index > 1) and ( text_document.version and text_document.version > 0 @@ -2179,9 +2174,7 @@ function M._refresh(method, opts) local textDocument = M.make_text_document_params(bufnr) - local only_visible = opts.only_visible or false - - if only_visible then + if opts.only_visible then for _, window in ipairs(api.nvim_list_wins()) do if api.nvim_win_get_buf(window) == bufnr then local first = vim.fn.line('w0', window) -- cgit From cbc82011ce19e308f249e1e3ba16f7b1219c9984 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 15:48:30 +0100 Subject: feat(lsp.util): improve offset_encoding type annotations --- runtime/lua/vim/lsp/util.lua | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index e0e93678e2..7c89be1d88 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -362,7 +362,7 @@ end --- Applies a list of text edits to a buffer. ---@param text_edits lsp.TextEdit[] ---@param bufnr integer Buffer id ----@param offset_encoding string utf-8|utf-16|utf-32 +---@param offset_encoding 'utf-8'|'utf-16'|'utf-32' ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textEdit function M.apply_text_edits(text_edits, bufnr, offset_encoding) validate('text_edits', text_edits, 'table', false) @@ -497,7 +497,7 @@ end --- ---@param text_document_edit lsp.TextDocumentEdit ---@param index? integer: Optional index of the edit, if from a list of edits (or nil, if not from a list) ----@param offset_encoding? string +---@param offset_encoding? 'utf-8'|'utf-16'|'utf-32' ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentEdit function M.apply_text_document_edit(text_document_edit, index, offset_encoding) local text_document = text_document_edit.textDocument @@ -1025,7 +1025,7 @@ end --- Jumps to a location. --- ---@param location lsp.Location|lsp.LocationLink ----@param offset_encoding string? utf-8|utf-16|utf-32 +---@param offset_encoding 'utf-8'|'utf-16'|'utf-32'? ---@param reuse_win boolean? Jump to existing window if buffer is already open. ---@return boolean `true` if the jump succeeded function M.jump_to_location(location, offset_encoding, reuse_win) @@ -1694,7 +1694,7 @@ do --[[ References ]] --- ---@param bufnr integer Buffer id ---@param references lsp.DocumentHighlight[] objects to highlight - ---@param offset_encoding string One of "utf-8", "utf-16", "utf-32". + ---@param offset_encoding 'utf-8'|'utf-16'|'utf-32' ---@see https://microsoft.github.io/language-server-protocol/specification/#textDocumentContentChangeEvent function M.buf_highlight_references(bufnr, references, offset_encoding) validate('bufnr', bufnr, 'number', true) @@ -1748,8 +1748,8 @@ end) --- |setloclist()|. --- ---@param locations lsp.Location[]|lsp.LocationLink[] ----@param offset_encoding string offset_encoding for locations utf-8|utf-16|utf-32 ---- default to first client of buffer +---@param offset_encoding? 'utf-8'|'utf-16'|'utf-32' +--- default to first client of buffer ---@return vim.quickfix.entry[] # See |setqflist()| for the format function M.locations_to_items(locations, offset_encoding) if offset_encoding == nil then @@ -2088,7 +2088,8 @@ end ---@param buf integer buffer number (0 for current) ---@param row integer 0-indexed line ---@param col integer 0-indexed byte offset in line ----@param offset_encoding string utf-8|utf-16|utf-32 defaults to `offset_encoding` of first client of `buf` +---@param offset_encoding? 'utf-8'|'utf-16'|'utf-32' +--- defaults to `offset_encoding` of first client of `buf` ---@return integer `offset_encoding` index of the character in line {row} column {col} in buffer {buf} function M.character_offset(buf, row, col, offset_encoding) local line = get_line(buf, row) @@ -2126,7 +2127,7 @@ end ---@param bufnr integer ---@param start_line integer ---@param end_line integer ----@param offset_encoding lsp.PositionEncodingKind +---@param offset_encoding 'utf-8'|'utf-16'|'utf-32' ---@return lsp.Range local function make_line_range_params(bufnr, start_line, end_line, offset_encoding) local last_line = api.nvim_buf_line_count(bufnr) - 1 -- cgit From a18fa2f11c3a66be8addc303efb5c8033a47f41e Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 16:11:48 +0100 Subject: feat(lsp.util): minor codestyle --- runtime/lua/vim/lsp/util.lua | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 7c89be1d88..76a18a409c 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1700,10 +1700,10 @@ do --[[ References ]] validate('bufnr', bufnr, 'number', true) validate('offset_encoding', offset_encoding, 'string', false) for _, reference in ipairs(references) do - local start_line, start_char = - reference['range']['start']['line'], reference['range']['start']['character'] - local end_line, end_char = - reference['range']['end']['line'], reference['range']['end']['character'] + local start_line = reference.range.start.line + local start_char = reference.range.start.character + local end_line = reference.range['end'].line + local end_char = reference.range['end'].character local start_idx = get_line_byte_from_position( bufnr, @@ -1772,8 +1772,7 @@ function M.locations_to_items(locations, offset_encoding) table.insert(grouped[uri], { start = range.start, ['end'] = range['end'], location = d }) end - for uri in vim.spairs(grouped) do - local rows = grouped[uri] + for uri, rows in vim.spairs(grouped) do table.sort(rows, position_sort) local filename = vim.uri_to_fname(uri) @@ -2008,19 +2007,19 @@ function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding) validate('offset_encoding', offset_encoding, 'string', true) bufnr = bufnr or api.nvim_get_current_buf() offset_encoding = offset_encoding or M._get_offset_encoding(bufnr) - --- @type integer[] - local A = list_extend({}, start_pos or api.nvim_buf_get_mark(bufnr, '<')) - --- @type integer[] - local B = list_extend({}, end_pos or api.nvim_buf_get_mark(bufnr, '>')) + --- @type [integer, integer] + local A = { unpack(start_pos or api.nvim_buf_get_mark(bufnr, '<')) } + --- @type [integer, integer] + local B = { unpack(end_pos or api.nvim_buf_get_mark(bufnr, '>')) } -- convert to 0-index A[1] = A[1] - 1 B[1] = B[1] - 1 -- account for offset_encoding. if A[2] > 0 then - A = { A[1], M.character_offset(bufnr, A[1], A[2], offset_encoding) } + A[2] = M.character_offset(bufnr, A[1], A[2], offset_encoding) end if B[2] > 0 then - B = { B[1], M.character_offset(bufnr, B[1], B[2], offset_encoding) } + B[2] = M.character_offset(bufnr, B[1], B[2], offset_encoding) end -- we need to offset the end character position otherwise we loose the last -- character of the selection, as LSP end position is exclusive -- cgit From 3f3e4837d5f7d2d9cb1c89bd3a5b2ee8a730772a Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Oct 2024 17:03:48 +0100 Subject: perf(validate): use lighter version - Also fix `vim.validate()` for PUC Lua when showing errors for values that aren't string or number. --- runtime/lua/vim/_editor.lua | 14 ++- runtime/lua/vim/_system.lua | 8 +- runtime/lua/vim/_watch.lua | 16 ++-- runtime/lua/vim/diagnostic.lua | 146 ++++++++++---------------------- runtime/lua/vim/filetype.lua | 4 +- runtime/lua/vim/fs.lua | 34 +++----- runtime/lua/vim/lsp.lua | 32 +++---- runtime/lua/vim/lsp/buf.lua | 16 ++-- runtime/lua/vim/lsp/client.lua | 14 ++- runtime/lua/vim/lsp/diagnostic.lua | 2 +- runtime/lua/vim/lsp/inlay_hint.lua | 9 +- runtime/lua/vim/lsp/rpc.lua | 24 ++---- runtime/lua/vim/lsp/semantic_tokens.lua | 16 ++-- runtime/lua/vim/secure.lua | 4 +- runtime/lua/vim/shared.lua | 21 +++-- runtime/lua/vim/treesitter.lua | 6 +- runtime/lua/vim/treesitter/dev.lua | 4 +- runtime/lua/vim/treesitter/language.lua | 8 +- runtime/lua/vim/ui.lua | 16 ++-- 19 files changed, 141 insertions(+), 253 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index ce269f087d..f0b8cf6a52 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -473,9 +473,7 @@ do --- @param handle? false|integer --- @return vim.var_accessor local function make_dict_accessor(scope, handle) - validate({ - scope = { scope, 's' }, - }) + validate('scope', scope, 'string') local mt = {} function mt:__newindex(k, v) return vim._setvar(scope, handle or 0, k, v) @@ -1171,12 +1169,10 @@ function vim.deprecate(name, alternative, version, plugin, backtrace) local displayed = vim._truncated_echo_once(msg) return displayed and msg or nil else - vim.validate { - name = { name, 'string' }, - alternative = { alternative, 'string', true }, - version = { version, 'string', true }, - plugin = { plugin, 'string', true }, - } + vim.validate('name', name, 'string') + vim.validate('alternative', alternative, 'string', true) + vim.validate('version', version, 'string', true) + vim.validate('plugin', plugin, 'string', true) local msg = ('%s is deprecated'):format(name) msg = alternative and ('%s, use %s instead.'):format(msg, alternative) or (msg .. '.') diff --git a/runtime/lua/vim/_system.lua b/runtime/lua/vim/_system.lua index d603971495..9e27b4c152 100644 --- a/runtime/lua/vim/_system.lua +++ b/runtime/lua/vim/_system.lua @@ -309,11 +309,9 @@ end --- @param on_exit? fun(out: vim.SystemCompleted) --- @return vim.SystemObj function M.run(cmd, opts, on_exit) - vim.validate({ - cmd = { cmd, 'table' }, - opts = { opts, 'table', true }, - on_exit = { on_exit, 'function', true }, - }) + vim.validate('cmd', cmd, 'table') + vim.validate('opts', opts, 'table', true) + vim.validate('on_exit', on_exit, 'function', true) opts = opts or {} diff --git a/runtime/lua/vim/_watch.lua b/runtime/lua/vim/_watch.lua index 11f6742941..7a306d1123 100644 --- a/runtime/lua/vim/_watch.lua +++ b/runtime/lua/vim/_watch.lua @@ -59,11 +59,9 @@ end --- @param callback vim._watch.Callback Callback for new events --- @return fun() cancel Stops the watcher function M.watch(path, opts, callback) - vim.validate({ - path = { path, 'string', false }, - opts = { opts, 'table', true }, - callback = { callback, 'function', false }, - }) + vim.validate('path', path, 'string', false) + vim.validate('opts', opts, 'table', true) + vim.validate('callback', callback, 'function', false) opts = opts or {} @@ -127,11 +125,9 @@ end --- @param callback vim._watch.Callback Callback for new events --- @return fun() cancel Stops the watcher function M.watchdirs(path, opts, callback) - vim.validate({ - path = { path, 'string', false }, - opts = { opts, 'table', true }, - callback = { callback, 'function', false }, - }) + vim.validate('path', path, 'string', false) + vim.validate('opts', opts, 'table', true) + vim.validate('callback', callback, 'function', false) opts = opts or {} local debounce = opts.debounce or 500 diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index ef00a1fa51..99fcfd57e7 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -320,7 +320,7 @@ local global_diagnostic_options = { --- @type table M.handlers = setmetatable({}, { __newindex = function(t, name, handler) - vim.validate({ handler = { handler, 't' } }) + vim.validate('handler', handler, 'table') rawset(t, name, handler) if global_diagnostic_options[name] == nil then global_diagnostic_options[name] = true @@ -477,10 +477,8 @@ end --- @param diagnostics vim.Diagnostic[] --- @return vim.Diagnostic[] local function reformat_diagnostics(format, diagnostics) - vim.validate({ - format = { format, 'f' }, - diagnostics = { diagnostics, 't' }, - }) + vim.validate('format', format, 'function') + vim.validate({ diagnostics = { diagnostics, vim.islist, 'a list of diagnostics' } }) local formatted = vim.deepcopy(diagnostics, true) for _, diagnostic in ipairs(formatted) do @@ -1012,10 +1010,8 @@ end --- When omitted, update the global diagnostic options. ---@return vim.diagnostic.Opts? : Current diagnostic config if {opts} is omitted. function M.config(opts, namespace) - vim.validate({ - opts = { opts, 't', true }, - namespace = { namespace, 'n', true }, - }) + vim.validate('opts', opts, 'table', true) + vim.validate('namespace', namespace, 'number', true) local t --- @type vim.diagnostic.Opts if namespace then @@ -1058,16 +1054,10 @@ end ---@param diagnostics vim.Diagnostic[] ---@param opts? vim.diagnostic.Opts Display options to pass to |vim.diagnostic.show()| function M.set(namespace, bufnr, diagnostics, opts) - vim.validate({ - namespace = { namespace, 'n' }, - bufnr = { bufnr, 'n' }, - diagnostics = { - diagnostics, - vim.islist, - 'a list of diagnostics', - }, - opts = { opts, 't', true }, - }) + vim.validate('namespace', namespace, 'number') + vim.validate('bufnr', bufnr, 'number') + vim.validate({ diagnostics = { diagnostics, vim.islist, 'a list of diagnostics' } }) + vim.validate('opts', opts, 'table', true) bufnr = get_bufnr(bufnr) @@ -1092,7 +1082,7 @@ end ---@param namespace integer Diagnostic namespace ---@return vim.diagnostic.NS : Namespace metadata function M.get_namespace(namespace) - vim.validate({ namespace = { namespace, 'n' } }) + vim.validate('namespace', namespace, 'number') if not all_namespaces[namespace] then local name --- @type string? for k, v in pairs(api.nvim_get_namespaces()) do @@ -1131,10 +1121,8 @@ end ---@return vim.Diagnostic[] : Fields `bufnr`, `end_lnum`, `end_col`, and `severity` --- are guaranteed to be present. function M.get(bufnr, opts) - vim.validate({ - bufnr = { bufnr, 'n', true }, - opts = { opts, 't', true }, - }) + vim.validate('bufnr', bufnr, 'number', true) + vim.validate('opts', opts, 'table', true) return vim.deepcopy(get_diagnostics(bufnr, opts, false), true) end @@ -1147,10 +1135,8 @@ end ---@return table : Table with actually present severity values as keys --- (see |diagnostic-severity|) and integer counts as values. function M.count(bufnr, opts) - vim.validate({ - bufnr = { bufnr, 'n', true }, - opts = { opts, 't', true }, - }) + vim.validate('bufnr', bufnr, 'number', true) + vim.validate('opts', opts, 'table', true) local diagnostics = get_diagnostics(bufnr, opts, false) local count = {} --- @type table @@ -1348,16 +1334,10 @@ end M.handlers.signs = { show = function(namespace, bufnr, diagnostics, opts) - vim.validate({ - namespace = { namespace, 'n' }, - bufnr = { bufnr, 'n' }, - diagnostics = { - diagnostics, - vim.islist, - 'a list of diagnostics', - }, - opts = { opts, 't', true }, - }) + vim.validate('namespace', namespace, 'number') + vim.validate('bufnr', bufnr, 'number') + vim.validate({ diagnostics = { diagnostics, vim.islist, 'a list of diagnostics' } }) + vim.validate('opts', opts, 'table', true) bufnr = get_bufnr(bufnr) opts = opts or {} @@ -1475,16 +1455,10 @@ M.handlers.signs = { M.handlers.underline = { show = function(namespace, bufnr, diagnostics, opts) - vim.validate({ - namespace = { namespace, 'n' }, - bufnr = { bufnr, 'n' }, - diagnostics = { - diagnostics, - vim.islist, - 'a list of diagnostics', - }, - opts = { opts, 't', true }, - }) + vim.validate('namespace', namespace, 'number') + vim.validate('bufnr', bufnr, 'number') + vim.validate({ diagnostics = { diagnostics, vim.islist, 'a list of diagnostics' } }) + vim.validate('opts', opts, 'table', true) bufnr = get_bufnr(bufnr) opts = opts or {} @@ -1548,16 +1522,10 @@ M.handlers.underline = { M.handlers.virtual_text = { show = function(namespace, bufnr, diagnostics, opts) - vim.validate({ - namespace = { namespace, 'n' }, - bufnr = { bufnr, 'n' }, - diagnostics = { - diagnostics, - vim.islist, - 'a list of diagnostics', - }, - opts = { opts, 't', true }, - }) + vim.validate('namespace', namespace, 'number') + vim.validate('bufnr', bufnr, 'number') + vim.validate({ diagnostics = { diagnostics, vim.islist, 'a list of diagnostics' } }) + vim.validate('opts', opts, 'table', true) bufnr = get_bufnr(bufnr) opts = opts or {} @@ -1681,10 +1649,8 @@ end ---@param bufnr integer? Buffer number, or 0 for current buffer. When --- omitted, hide diagnostics in all buffers. function M.hide(namespace, bufnr) - vim.validate({ - namespace = { namespace, 'n', true }, - bufnr = { bufnr, 'n', true }, - }) + vim.validate('namespace', namespace, 'number', true) + vim.validate('bufnr', bufnr, 'number', true) local buffers = bufnr and { get_bufnr(bufnr) } or vim.tbl_keys(diagnostic_cache) for _, iter_bufnr in ipairs(buffers) do @@ -1741,9 +1707,9 @@ end --- or {bufnr} is nil. ---@param opts? vim.diagnostic.Opts Display options. function M.show(namespace, bufnr, diagnostics, opts) + vim.validate('namespace', namespace, 'number', true) + vim.validate('bufnr', bufnr, 'number', true) vim.validate({ - namespace = { namespace, 'n', true }, - bufnr = { bufnr, 'n', true }, diagnostics = { diagnostics, function(v) @@ -1751,8 +1717,8 @@ function M.show(namespace, bufnr, diagnostics, opts) end, 'a list of diagnostics', }, - opts = { opts, 't', true }, }) + vim.validate('opts', opts, 'table', true) if not bufnr or not namespace then assert(not diagnostics, 'Cannot show diagnostics without a buffer and namespace') @@ -1825,9 +1791,7 @@ function M.open_float(opts, ...) bufnr = opts opts = ... --- @type vim.diagnostic.Opts.Float else - vim.validate({ - opts = { opts, 't', true }, - }) + vim.validate('opts', opts, 'table', true) end opts = opts or {} @@ -2038,10 +2002,8 @@ end ---@param bufnr integer? Remove diagnostics for the given buffer. When omitted, --- diagnostics are removed for all buffers. function M.reset(namespace, bufnr) - vim.validate({ - namespace = { namespace, 'n', true }, - bufnr = { bufnr, 'n', true }, - }) + vim.validate('namespace', namespace, 'number', true) + vim.validate('bufnr', bufnr, 'number', true) local buffers = bufnr and { get_bufnr(bufnr) } or vim.tbl_keys(diagnostic_cache) for _, iter_bufnr in ipairs(buffers) do @@ -2144,10 +2106,8 @@ function M.enable(enable, filter) '0.12' ) - vim.validate({ - enable = { enable, 'n', true }, -- Legacy `bufnr` arg. - filter = { filter, 'n', true }, -- Legacy `namespace` arg. - }) + vim.validate('enable', enable, 'number', true) -- Legacy `bufnr` arg. + vim.validate('filter', filter, 'number', true) -- Legacy `namespace` arg. local ns_id = type(filter) == 'number' and filter or nil filter = {} @@ -2156,10 +2116,8 @@ function M.enable(enable, filter) enable = true else filter = filter or {} - vim.validate({ - enable = { enable, 'b', true }, - filter = { filter, 't', true }, - }) + vim.validate('enable', enable, 'boolean', true) + vim.validate('filter', filter, 'table', true) end enable = enable == nil and true or enable @@ -2234,13 +2192,11 @@ end --- ERROR. ---@return vim.Diagnostic?: |vim.Diagnostic| structure or `nil` if {pat} fails to match {str}. function M.match(str, pat, groups, severity_map, defaults) - vim.validate({ - str = { str, 's' }, - pat = { pat, 's' }, - groups = { groups, 't' }, - severity_map = { severity_map, 't', true }, - defaults = { defaults, 't', true }, - }) + vim.validate('str', str, 'string') + vim.validate('pat', pat, 'string') + vim.validate('groups', groups, 'table') + vim.validate('severity_map', severity_map, 'table', true) + vim.validate('defaults', defaults, 'table', true) --- @type table severity_map = severity_map or M.severity @@ -2283,13 +2239,7 @@ local errlist_type_map = { ---@param diagnostics vim.Diagnostic[] ---@return table[] : Quickfix list items |setqflist-what| function M.toqflist(diagnostics) - vim.validate({ - diagnostics = { - diagnostics, - vim.islist, - 'a list of diagnostics', - }, - }) + vim.validate({ diagnostics = { diagnostics, vim.islist, 'a list of diagnostics' } }) local list = {} --- @type table[] for _, v in ipairs(diagnostics) do @@ -2323,13 +2273,7 @@ end ---@param list table[] List of quickfix items from |getqflist()| or |getloclist()|. ---@return vim.Diagnostic[] function M.fromqflist(list) - vim.validate({ - list = { - list, - vim.islist, - 'a list of quickfix items', - }, - }) + vim.validate('list', list, 'table') local diagnostics = {} --- @type vim.Diagnostic[] for _, item in ipairs(list) do diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index a166397bfd..280d665fea 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -2736,9 +2736,7 @@ end --- filetype specific buffer variables). The function accepts a buffer number as --- its only argument. function M.match(args) - vim.validate({ - arg = { args, 't' }, - }) + vim.validate('arg', args, 'table') if not (args.buf or args.filename or args.contents) then error('At least one of "buf", "filename", or "contents" must be given') diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index ccddf826f7..c23cd5af1c 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -53,7 +53,7 @@ function M.dirname(file) if file == nil then return nil end - vim.validate({ file = { file, 's' } }) + vim.validate('file', file, 'string') if iswin then file = file:gsub(os_sep, '/') --[[@as string]] if file:match('^%w:/?$') then @@ -83,7 +83,7 @@ function M.basename(file) if file == nil then return nil end - vim.validate({ file = { file, 's' } }) + vim.validate('file', file, 'string') if iswin then file = file:gsub(os_sep, '/') --[[@as string]] if file:match('^%w:/?$') then @@ -123,11 +123,9 @@ end function M.dir(path, opts) opts = opts or {} - vim.validate({ - path = { path, { 'string' } }, - depth = { opts.depth, { 'number' }, true }, - skip = { opts.skip, { 'function' }, true }, - }) + vim.validate('path', path, 'string') + vim.validate('depth', opts.depth, 'number', true) + vim.validate('skip', opts.skip, 'function', true) path = M.normalize(path) if not opts.depth or opts.depth == 1 then @@ -231,14 +229,12 @@ end ---@return (string[]) # Normalized paths |vim.fs.normalize()| of all matching items function M.find(names, opts) opts = opts or {} - vim.validate({ - names = { names, { 's', 't', 'f' } }, - path = { opts.path, 's', true }, - upward = { opts.upward, 'b', true }, - stop = { opts.stop, 's', true }, - type = { opts.type, 's', true }, - limit = { opts.limit, 'n', true }, - }) + vim.validate({ names = { names, { 'string', 'table', 'function' } } }) + vim.validate('path', opts.path, 'string', true) + vim.validate('upward', opts.upward, 'boolean', true) + vim.validate('stop', opts.stop, 'string', true) + vim.validate('type', opts.type, 'string', true) + vim.validate('limit', opts.limit, 'number', true) if type(names) == 'string' then names = { names } @@ -547,11 +543,9 @@ function M.normalize(path, opts) opts = opts or {} if not opts._fast then - vim.validate({ - path = { path, { 'string' } }, - expand_env = { opts.expand_env, { 'boolean' }, true }, - win = { opts.win, { 'boolean' }, true }, - }) + vim.validate('path', path, 'string') + vim.validate('expand_env', opts.expand_env, 'boolean', true) + vim.validate('win', opts.win, 'boolean', true) end local win = opts.win == nil and iswin or not not opts.win diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 60677554ce..8a0eef418d 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -86,7 +86,7 @@ lsp._request_name_to_capability = { ---@param bufnr (integer|nil) Buffer number to resolve. Defaults to current buffer ---@return integer bufnr local function resolve_bufnr(bufnr) - validate({ bufnr = { bufnr, 'n', true } }) + validate('bufnr', bufnr, 'number', true) if bufnr == nil or bufnr == 0 then return api.nvim_get_current_buf() end @@ -630,10 +630,8 @@ end ---@param client_id (integer) Client id ---@return boolean success `true` if client was attached successfully; `false` otherwise function lsp.buf_attach_client(bufnr, client_id) - validate({ - bufnr = { bufnr, 'n', true }, - client_id = { client_id, 'n' }, - }) + validate('bufnr', bufnr, 'number', true) + validate('client_id', client_id, 'number') bufnr = resolve_bufnr(bufnr) if not api.nvim_buf_is_loaded(bufnr) then log.warn(string.format('buf_attach_client called on unloaded buffer (id: %d): ', bufnr)) @@ -669,10 +667,8 @@ end ---@param bufnr integer Buffer handle, or 0 for current ---@param client_id integer Client id function lsp.buf_detach_client(bufnr, client_id) - validate({ - bufnr = { bufnr, 'n', true }, - client_id = { client_id, 'n' }, - }) + validate('bufnr', bufnr, 'number', true) + validate('client_id', client_id, 'number') bufnr = resolve_bufnr(bufnr) local client = all_clients[client_id] @@ -773,7 +769,7 @@ end ---@param filter? vim.lsp.get_clients.Filter ---@return vim.lsp.Client[]: List of |vim.lsp.Client| objects function lsp.get_clients(filter) - validate({ filter = { filter, 't', true } }) + validate('filter', filter, 'table', true) filter = filter or {} @@ -870,12 +866,10 @@ api.nvim_create_autocmd('VimLeavePre', { ---cancel all the requests. You could instead ---iterate all clients and call their `cancel_request()` methods. function lsp.buf_request(bufnr, method, params, handler, on_unsupported) - validate({ - bufnr = { bufnr, 'n', true }, - method = { method, 's' }, - handler = { handler, 'f', true }, - on_unsupported = { on_unsupported, 'f', true }, - }) + validate('bufnr', bufnr, 'number', true) + validate('method', method, 'string') + validate('handler', handler, 'function', true) + validate('on_unsupported', on_unsupported, 'function', true) bufnr = resolve_bufnr(bufnr) local method_supported = false @@ -992,10 +986,8 @@ end --- ---@return boolean success true if any client returns true; false otherwise function lsp.buf_notify(bufnr, method, params) - validate({ - bufnr = { bufnr, 'n', true }, - method = { method, 's' }, - }) + validate('bufnr', bufnr, 'number', true) + validate('method', method, 'string') local resp = false for _, client in ipairs(lsp.get_clients({ bufnr = bufnr })) do if client.rpc.notify(method, params) then diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index ccef314d86..f6837a627f 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -21,10 +21,8 @@ local M = {} --- ---@see |vim.lsp.buf_request()| local function request(method, params, handler) - validate({ - method = { method, 's' }, - handler = { handler, 'f', true }, - }) + validate('method', method, 'string') + validate('handler', handler, 'function', true) return vim.lsp.buf_request(0, method, params, handler) end @@ -439,7 +437,7 @@ end ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references ---@param opts? vim.lsp.ListOpts function M.references(context, opts) - validate({ context = { context, 't', true } }) + validate('context', context, 'table', true) local params = util.make_position_params() params.context = context or { includeDeclaration = true, @@ -857,7 +855,7 @@ end ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction ---@see vim.lsp.protocol.CodeActionTriggerKind function M.code_action(opts) - validate({ options = { opts, 't', true } }) + validate('options', opts, 'table', true) opts = opts or {} -- Detect old API call code_action(context) which should now be -- code_action({ context = context} ) @@ -935,10 +933,8 @@ end --- @param command_params lsp.ExecuteCommandParams --- @see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand function M.execute_command(command_params) - validate({ - command = { command_params.command, 's' }, - arguments = { command_params.arguments, 't', true }, - }) + validate('command', command_params.command, 'string') + validate('arguments', command_params.arguments, 'table', true) command_params = { command = command_params.command, arguments = command_params.arguments, diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index e3c82f4169..d5fc5b8908 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -291,7 +291,7 @@ local client_index = 0 --- @param filename (string) path to check --- @return boolean # true if {filename} exists and is a directory, false otherwise local function is_dir(filename) - validate({ filename = { filename, 's' } }) + validate('filename', filename, 'string') local stat = uv.fs_stat(filename) return stat and stat.type == 'directory' or false end @@ -312,9 +312,7 @@ local valid_encodings = { --- @param encoding string? Encoding to normalize --- @return string # normalized encoding name local function validate_encoding(encoding) - validate({ - encoding = { encoding, 's', true }, - }) + validate('encoding', encoding, 'string', true) if not encoding then return valid_encodings.UTF16 end @@ -350,9 +348,7 @@ end --- Validates a client configuration as given to |vim.lsp.start_client()|. --- @param config vim.lsp.ClientConfig local function validate_config(config) - validate({ - config = { config, 't' }, - }) + validate('config', config, 'table') validate({ handlers = { config.handlers, 't', true }, capabilities = { config.capabilities, 't', true }, @@ -640,7 +636,7 @@ end --- @param bufnr (integer|nil) Buffer number to resolve. Defaults to current buffer --- @return integer bufnr local function resolve_bufnr(bufnr) - validate({ bufnr = { bufnr, 'n', true } }) + validate('bufnr', bufnr, 'number', true) if bufnr == nil or bufnr == 0 then return api.nvim_get_current_buf() end @@ -806,7 +802,7 @@ end --- @return boolean status true if notification was successful. false otherwise --- @see |vim.lsp.client.notify()| function Client:_cancel_request(id) - validate({ id = { id, 'n' } }) + validate('id', id, 'number') local request = self.requests[id] if request and request.type == 'pending' then request.type = 'cancel' diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index c10312484b..bf72222536 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -195,7 +195,7 @@ local _client_pull_namespaces = {} ---@param client_id integer The id of the LSP client ---@param is_pull boolean? Whether the namespace is for a pull or push client. Defaults to push function M.get_namespace(client_id, is_pull) - vim.validate({ client_id = { client_id, 'n' } }) + vim.validate('client_id', client_id, 'number') local client = vim.lsp.get_client_by_id(client_id) if is_pull then diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua index 61059180fe..61d119e653 100644 --- a/runtime/lua/vim/lsp/inlay_hint.lua +++ b/runtime/lua/vim/lsp/inlay_hint.lua @@ -145,7 +145,7 @@ end --- @return vim.lsp.inlay_hint.get.ret[] --- @since 12 function M.get(filter) - vim.validate({ filter = { filter, 'table', true } }) + vim.validate('filter', filter, 'table', true) filter = filter or {} local bufnr = filter.bufnr @@ -375,11 +375,11 @@ api.nvim_set_decoration_provider(namespace, { --- @return boolean --- @since 12 function M.is_enabled(filter) - vim.validate({ filter = { filter, 'table', true } }) + vim.validate('filter', filter, 'table', true) filter = filter or {} local bufnr = filter.bufnr - vim.validate({ bufnr = { bufnr, 'number', true } }) + vim.validate('bufnr', bufnr, 'number', true) if bufnr == nil then return globalstate.enabled elseif bufnr == 0 then @@ -406,7 +406,8 @@ end --- @param filter vim.lsp.inlay_hint.enable.Filter? --- @since 12 function M.enable(enable, filter) - vim.validate({ enable = { enable, 'boolean', true }, filter = { filter, 'table', true } }) + vim.validate('enable', enable, 'boolean', true) + vim.validate('filter', filter, 'table', true) enable = enable == nil or enable filter = filter or {} diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index e79dbd2db3..ac43801c1c 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -152,9 +152,7 @@ end ---@param err table The error object ---@return string error_message The formatted error message function M.format_rpc_error(err) - validate({ - err = { err, 't' }, - }) + validate('err', err, 'table') -- There is ErrorCodes in the LSP specification, -- but in ResponseError.code it is not used and the actual type is number. @@ -329,10 +327,8 @@ end ---@return boolean success `true` if request could be sent, `false` if not ---@return integer? message_id if request could be sent, `nil` if not function Client:request(method, params, callback, notify_reply_callback) - validate({ - callback = { callback, 'f' }, - notify_reply_callback = { notify_reply_callback, 'f', true }, - }) + validate('callback', callback, 'function') + validate('notify_reply_callback', notify_reply_callback, 'function', true) self.message_index = self.message_index + 1 local message_id = self.message_index local result = self:encode_and_send({ @@ -465,9 +461,7 @@ function Client:handle_body(body) local notify_reply_callbacks = self.notify_reply_callbacks local notify_reply_callback = notify_reply_callbacks and notify_reply_callbacks[result_id] if notify_reply_callback then - validate({ - notify_reply_callback = { notify_reply_callback, 'f' }, - }) + validate('notify_reply_callback', notify_reply_callback, 'function') notify_reply_callback(result_id) notify_reply_callbacks[result_id] = nil end @@ -498,9 +492,7 @@ function Client:handle_body(body) local callback = message_callbacks and message_callbacks[result_id] if callback then message_callbacks[result_id] = nil - validate({ - callback = { callback, 'f' }, - }) + validate('callback', callback, 'function') if decoded.error then decoded.error = setmetatable(decoded.error, { __tostring = M.format_rpc_error, @@ -734,10 +726,8 @@ end function M.start(cmd, dispatchers, extra_spawn_params) log.info('Starting RPC client', { cmd = cmd, extra = extra_spawn_params }) - validate({ - cmd = { cmd, 't' }, - dispatchers = { dispatchers, 't', true }, - }) + validate('cmd', cmd, 'table') + validate('dispatchers', dispatchers, 'table', true) extra_spawn_params = extra_spawn_params or {} diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index 8182457dd0..2c7958b0d5 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -565,10 +565,8 @@ local M = {} --- - debounce (integer, default: 200): Debounce token requests --- to the server by the given number in milliseconds function M.start(bufnr, client_id, opts) - vim.validate({ - bufnr = { bufnr, 'n', false }, - client_id = { client_id, 'n', false }, - }) + vim.validate('bufnr', bufnr, 'number') + vim.validate('client_id', client_id, 'number') if bufnr == 0 then bufnr = api.nvim_get_current_buf() @@ -622,10 +620,8 @@ end ---@param bufnr (integer) Buffer number, or `0` for current buffer ---@param client_id (integer) The ID of the |vim.lsp.Client| function M.stop(bufnr, client_id) - vim.validate({ - bufnr = { bufnr, 'n', false }, - client_id = { client_id, 'n', false }, - }) + vim.validate('bufnr', bufnr, 'number') + vim.validate('client_id', client_id, 'number') if bufnr == 0 then bufnr = api.nvim_get_current_buf() @@ -708,9 +704,7 @@ end ---@param bufnr (integer|nil) filter by buffer. All buffers if nil, current --- buffer if 0 function M.force_refresh(bufnr) - vim.validate({ - bufnr = { bufnr, 'n', true }, - }) + vim.validate('bufnr', bufnr, 'number', true) local buffers = bufnr == nil and vim.tbl_keys(STHighlighter.active) or bufnr == 0 and { api.nvim_get_current_buf() } diff --git a/runtime/lua/vim/secure.lua b/runtime/lua/vim/secure.lua index 266725cce2..0d3a271ac7 100644 --- a/runtime/lua/vim/secure.lua +++ b/runtime/lua/vim/secure.lua @@ -26,7 +26,7 @@ end --- ---@param trust table Trust table to write local function write_trust(trust) - vim.validate({ trust = { trust, 't' } }) + vim.validate('trust', trust, 'table') local f = assert(io.open(vim.fn.stdpath('state') .. '/trust', 'w')) local t = {} ---@type string[] @@ -49,7 +49,7 @@ end ---@return (string|nil) The contents of the given file if it exists and is --- trusted, or nil otherwise. function M.read(path) - vim.validate({ path = { path, 's' } }) + vim.validate('path', path, 'string') local fullpath = vim.uv.fs_realpath(vim.fs.normalize(path)) if not fullpath then return nil diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 4d06cdd77d..0f636401bb 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -109,7 +109,9 @@ function vim.gsplit(s, sep, opts) if type(opts) == 'boolean' then plain = opts -- For backwards compatibility. else - vim.validate({ s = { s, 's' }, sep = { sep, 's' }, opts = { opts, 't', true } }) + vim.validate('s', s, 'string') + vim.validate('sep', sep, 'string') + vim.validate('opts', opts, 'table', true) opts = opts or {} plain, trimempty = opts.plain, opts.trimempty end @@ -303,7 +305,8 @@ end ---@param opts? vim.tbl_contains.Opts Keyword arguments |kwargs|: ---@return boolean `true` if `t` contains `value` function vim.tbl_contains(t, value, opts) - vim.validate({ t = { t, 't' }, opts = { opts, 't', true } }) + vim.validate('t', t, 'table') + vim.validate('opts', opts, 'table', true) --- @cast t table local pred --- @type fun(v: any): boolean? @@ -550,12 +553,10 @@ end ---@param finish integer? Final index on src. Defaults to `#src` ---@return T dst function vim.list_extend(dst, src, start, finish) - vim.validate({ - dst = { dst, 't' }, - src = { src, 't' }, - start = { start, 'n', true }, - finish = { finish, 'n', true }, - }) + vim.validate('dst', dst, 'table') + vim.validate('src', src, 'table') + vim.validate('start', start, 'number', true) + vim.validate('finish', finish, 'number', true) for i = start or 1, finish or #src do table.insert(dst, src[i]) end @@ -987,6 +988,10 @@ do ok = (actual == expected) or (v == nil and optional == true) if not ok then + if not jit and (actual ~= 'string' or actual ~= 'number') then + -- PUC-Lua can only handle string and number for %s in string.format() + v = vim.inspect(v) + end err_msg = ('%s: expected %s, got %s%s'):format( name, expected, diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 4727c0d61d..c24a732fe3 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -133,10 +133,8 @@ end --- ---@return vim.treesitter.LanguageTree object to use for parsing function M.get_string_parser(str, lang, opts) - vim.validate({ - str = { str, 'string' }, - lang = { lang, 'string' }, - }) + vim.validate('str', str, 'string') + vim.validate('lang', lang, 'string') return LanguageTree.new(str, lang, opts) end diff --git a/runtime/lua/vim/treesitter/dev.lua b/runtime/lua/vim/treesitter/dev.lua index 90c3720b80..7b522ca92b 100644 --- a/runtime/lua/vim/treesitter/dev.lua +++ b/runtime/lua/vim/treesitter/dev.lua @@ -330,9 +330,7 @@ end --- --- @param opts vim.treesitter.dev.inspect_tree.Opts? function M.inspect_tree(opts) - vim.validate({ - opts = { opts, 't', true }, - }) + vim.validate('opts', opts, 'table', true) opts = opts or {} diff --git a/runtime/lua/vim/treesitter/language.lua b/runtime/lua/vim/treesitter/language.lua index aa1d38df97..ce12ca433a 100644 --- a/runtime/lua/vim/treesitter/language.lua +++ b/runtime/lua/vim/treesitter/language.lua @@ -108,11 +108,9 @@ function M.add(lang, opts) local path = opts.path local symbol_name = opts.symbol_name - vim.validate({ - lang = { lang, 'string' }, - path = { path, 'string', true }, - symbol_name = { symbol_name, 'string', true }, - }) + vim.validate('lang', lang, 'string') + vim.validate('path', path, 'string', true) + vim.validate('symbol_name', symbol_name, 'string', true) -- parser names are assumed to be lowercase (consistent behavior on case-insensitive file systems) lang = lang:lower() diff --git a/runtime/lua/vim/ui.lua b/runtime/lua/vim/ui.lua index 532decf5e9..c0f03ceadc 100644 --- a/runtime/lua/vim/ui.lua +++ b/runtime/lua/vim/ui.lua @@ -37,10 +37,8 @@ local M = {} --- `idx` is the 1-based index of `item` within `items`. --- `nil` if the user aborted the dialog. function M.select(items, opts, on_choice) - vim.validate({ - items = { items, 'table', false }, - on_choice = { on_choice, 'function', false }, - }) + vim.validate('items', items, 'table', false) + vim.validate('on_choice', on_choice, 'function', false) opts = opts or {} local choices = { opts.prompt or 'Select one of:' } local format_item = opts.format_item or tostring @@ -86,10 +84,8 @@ end --- an empty string if nothing was entered), or --- `nil` if the user aborted the dialog. function M.input(opts, on_confirm) - vim.validate({ - opts = { opts, 'table', true }, - on_confirm = { on_confirm, 'function', false }, - }) + vim.validate('opts', opts, 'table', true) + vim.validate('on_confirm', on_confirm, 'function', false) opts = (opts and not vim.tbl_isempty(opts)) and opts or vim.empty_dict() @@ -135,9 +131,7 @@ end --- ---@see |vim.system()| function M.open(path, opt) - vim.validate({ - path = { path, 'string' }, - }) + vim.validate('path', path, 'string') local is_uri = path:match('%w+:') if not is_uri then path = vim.fs.normalize(path) -- cgit From 31745b17e60df8efd25bc2d76fc4bd16f9959441 Mon Sep 17 00:00:00 2001 From: Maria José Solano Date: Thu, 17 Oct 2024 21:03:20 -0700 Subject: fix(types): add narrower vim.validate types --- runtime/lua/vim/shared.lua | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 0f636401bb..d11489b539 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -779,15 +779,17 @@ function vim.endswith(s, suffix) end do - --- @alias vim.validate.Type - --- | 't' | 'table' - --- | 's' | 'string' - --- | 'n' | 'number' - --- | 'f' | 'function' - --- | 'c' | 'callable' + --- @alias vim.validate.LuaType --- | 'nil' + --- | 'number' + --- | 'string' + --- | 'boolean' + --- | 'table' + --- | 'function' --- | 'thread' - --- | 'userdata + --- | 'userdata' + --- + --- @alias vim.validate.Type vim.validate.LuaType | 't' | 's' | 'n' | 'f' | 'c' local type_names = { ['table'] = 'table', @@ -810,7 +812,7 @@ do --- @nodoc --- @class vim.validate.Spec [any, string|string[], boolean] --- @field [1] any Argument value - --- @field [2] string|string[]|fun(v:any):boolean, string? Type name, or callable + --- @field [2] vim.validate.Type|vim.validate.Type[]|fun(v:any):boolean, string? Type name, or callable --- @field [3]? boolean local function _is_type(val, t) @@ -973,7 +975,7 @@ do --- only if the argument is valid. Can optionally return an additional --- informative error message as the second returned value. --- - msg: (optional) error string if validation fails - --- @overload fun(name: string, val: any, expected: string, optional?: boolean) + --- @overload fun(name: string, val: any, expected: vim.validate.LuaType, optional?: boolean) function vim.validate(opt, ...) local ok = false local err_msg ---@type string? -- cgit From 564173e5568ccf11bf6554680922d3716fb6862b Mon Sep 17 00:00:00 2001 From: temhelk Date: Fri, 18 Oct 2024 03:20:58 +0300 Subject: fix(lsp.util): wrong arguments to 'validate' function --- runtime/lua/vim/lsp/util.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index e3eef8901b..83c8226326 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -888,8 +888,8 @@ end function M.make_floating_popup_options(width, height, opts) validate('opts', opts, 'table', true) opts = opts or {} - validate('opts.offset_x', opts.offset_x, 'n', true) - validate('opts.offset_y', opts.offset_y, 'n', true) + validate('opts.offset_x', opts.offset_x, 'number', true) + validate('opts.offset_y', opts.offset_y, 'number', true) local anchor = '' -- cgit From f4b620c4e6430ea8cbc5632d6853b24da09d7dfc Mon Sep 17 00:00:00 2001 From: Uthman Mohamed <83053931+uthmanmoh@users.noreply.github.com> Date: Fri, 18 Oct 2024 05:22:15 -0400 Subject: feat(vim.ui.open): support lemonade #30845 --- runtime/lua/vim/ui.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/ui.lua b/runtime/lua/vim/ui.lua index c0f03ceadc..f66b34ac44 100644 --- a/runtime/lua/vim/ui.lua +++ b/runtime/lua/vim/ui.lua @@ -159,8 +159,10 @@ function M.open(path, opt) cmd = { 'wslview', path } elseif vim.fn.executable('explorer.exe') == 1 then cmd = { 'explorer.exe', path } + elseif vim.fn.executable('lemonade') == 1 then + cmd = { 'lemonade', 'open', path } else - return nil, 'vim.ui.open: no handler found (tried: wslview, explorer.exe, xdg-open)' + return nil, 'vim.ui.open: no handler found (tried: wslview, explorer.exe, xdg-open, lemonade)' end return vim.system(cmd, job_opt), nil -- cgit From 3cf602486ce5cfaa50f33edbe179928f84527dc9 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 20 Oct 2024 07:59:43 +0800 Subject: feat(terminal)!: make 'belloff' and 'visualbell' apply to terminal bell (#30859) vim-patch:8.2.4744: a terminal window can't use the bell vim-patch:8.2.4745: using wrong flag for using bell in the terminal BREAKING CHANGE: Bells from :terminal are now silent by default, unless 'belloff' option doesn't contain "term" or "all". --- runtime/lua/vim/_meta/options.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index c33753047f..cf70429ce5 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -430,6 +430,7 @@ vim.go.bsk = vim.go.backupskip --- separated list of items. For each item that is present, the bell --- will be silenced. This is most useful to specify specific events in --- insert mode to be silenced. +--- You can also make it flash by using 'visualbell'. --- --- item meaning when present ~ --- all All events. @@ -453,6 +454,7 @@ vim.go.bsk = vim.go.backupskip --- register Unknown register after in `Insert-mode`. --- shell Bell from shell output `:!`. --- spell Error happened on spell suggest. +--- term Bell from `:terminal` output. --- wildmode More matches in `cmdline-completion` available --- (depends on the 'wildmode' setting). --- -- cgit From dff684fdb3d2e787ac6d6fd49ec52ede604fd0ce Mon Sep 17 00:00:00 2001 From: Mathias Fussenegger Date: Tue, 8 Oct 2024 19:04:28 +0200 Subject: feat(lsp)!: support multiple clients in lsp.buf.references Relates to: - https://github.com/neovim/neovim/issues/17712 - https://github.com/neovim/neovim/issues/30034 --- runtime/lua/vim/lsp/buf.lua | 58 ++++++++++++++++++++++++++++++++++++---- runtime/lua/vim/lsp/handlers.lua | 25 ----------------- 2 files changed, 53 insertions(+), 30 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index f6837a627f..8803c0495a 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -438,11 +438,59 @@ end ---@param opts? vim.lsp.ListOpts function M.references(context, opts) validate('context', context, 'table', true) - local params = util.make_position_params() - params.context = context or { - includeDeclaration = true, - } - request_with_opts(ms.textDocument_references, params, opts) + local clients = vim.lsp.get_clients({ method = ms.textDocument_references }) + if not next(clients) then + return + end + local win = api.nvim_get_current_win() + local bufnr = api.nvim_get_current_buf() + opts = opts or {} + + local all_items = {} + local title = 'References' + + local function on_done() + if not next(all_items) then + vim.notify('No references found') + else + local list = { + title = title, + items = all_items, + context = { + method = ms.textDocument_references, + bufnr = bufnr, + }, + } + if opts.loclist then + vim.fn.setloclist(0, {}, ' ', list) + vim.cmd.lopen() + elseif opts.on_list then + assert(vim.is_callable(opts.on_list), 'on_list is not a function') + opts.on_list(list) + else + vim.fn.setqflist({}, ' ', list) + vim.cmd('botright copen') + end + end + end + + local remaining = #clients + for _, client in ipairs(clients) do + local params = util.make_position_params(win, client.offset_encoding) + + ---@diagnostic disable-next-line: inject-field + params.context = context or { + includeDeclaration = true, + } + client.request(ms.textDocument_references, params, function(_, result) + local items = util.locations_to_items(result or {}, client.offset_encoding) + vim.list_extend(all_items, items) + remaining = remaining - 1 + if remaining == 0 then + on_done() + end + end) + end end --- Lists all symbols in the current buffer in the quickfix window. diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 8e538242d9..3306e480dd 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -242,31 +242,6 @@ M[ms.textDocument_inlayHint] = function(...) return vim.lsp.inlay_hint.on_inlayhint(...) end ---- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references -M[ms.textDocument_references] = function(_, result, ctx, config) - if not result or vim.tbl_isempty(result) then - vim.notify('No references found') - return - end - - local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) - config = config or {} - local title = 'References' - local items = util.locations_to_items(result, client.offset_encoding) - - local list = { title = title, items = items, context = ctx } - if config.loclist then - vim.fn.setloclist(0, {}, ' ', list) - vim.cmd.lopen() - elseif config.on_list then - assert(vim.is_callable(config.on_list), 'on_list is not a function') - config.on_list(list) - else - vim.fn.setqflist({}, ' ', list) - vim.cmd('botright copen') - end -end - --- Return a function that converts LSP responses to list items and opens the list --- --- The returned function has an optional {config} parameter that accepts |vim.lsp.ListOpts| -- cgit From 9b8907d90508d7b66f025bbd1f5a48a78c5ce035 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 20 Oct 2024 22:18:26 +0800 Subject: feat(float): allow enabling mouse for non-focusable window (#30844) Problem: Cannot allow mouse interaction for non-focusable float window. Solution: Add a "mouse" field to float window config. --- runtime/lua/vim/_meta/api.lua | 7 ++++++- runtime/lua/vim/_meta/api_keysets.lua | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index c66b295d3a..8236cc7cf0 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -1767,7 +1767,12 @@ function vim.api.nvim_open_term(buffer, opts) end --- fractional. --- - focusable: Enable focus by user actions (wincmds, mouse events). --- Defaults to true. Non-focusable windows can be entered by ---- `nvim_set_current_win()`. +--- `nvim_set_current_win()`, or, when the `mouse` field is set to true, +--- by mouse events. +--- - mouse: Specify how this window interacts with mouse events. +--- Defaults to `focusable` value. +--- - If false, mouse events pass through this window. +--- - If true, mouse events interact with this window normally. --- - external: GUI should display the window as an external --- top-level window. Currently accepts no other positioning --- configuration together with this. diff --git a/runtime/lua/vim/_meta/api_keysets.lua b/runtime/lua/vim/_meta/api_keysets.lua index 2fe5c32faf..bf184dee2d 100644 --- a/runtime/lua/vim/_meta/api_keysets.lua +++ b/runtime/lua/vim/_meta/api_keysets.lua @@ -295,6 +295,7 @@ error('Cannot require a meta file') --- @field bufpos? any[] --- @field external? boolean --- @field focusable? boolean +--- @field mouse? boolean --- @field vertical? boolean --- @field zindex? integer --- @field border? any -- cgit From 0083e03d6fa7586d0d6360b40b52b0cab0d2e7ba Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Sun, 20 Oct 2024 23:40:44 +0200 Subject: feat(lsp)!: support multiple clients in goto methods (#30877) Relates to: - https://github.com/neovim/neovim/issues/30034 - https://github.com/neovim/neovim/issues/17712 - https://github.com/neovim/neovim/issues/16363 Closes: - https://github.com/neovim/neovim/issues/26936 (but only provides bufnr and method) - https://github.com/neovim/neovim/issues/22318 Might fix: https://github.com/neovim/neovim/issues/30737 --- runtime/lua/vim/lsp/buf.lua | 89 +++++++++++++++++++++++++++++++++++----- runtime/lua/vim/lsp/handlers.lua | 51 ----------------------- runtime/lua/vim/lsp/util.lua | 9 +--- 3 files changed, 81 insertions(+), 68 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 8803c0495a..7bf3565499 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -50,6 +50,79 @@ local function request_with_opts(name, params, opts) request(name, params, req_handler) end +---@param method string +---@param opts? vim.lsp.LocationOpts +local function get_locations(method, opts) + opts = opts or {} + local bufnr = api.nvim_get_current_buf() + local clients = vim.lsp.get_clients({ method = method, bufnr = bufnr }) + if not next(clients) then + vim.notify(vim.lsp._unsupported_method(method), vim.log.levels.WARN) + return + end + local win = api.nvim_get_current_win() + local remaining = #clients + + ---@type vim.quickfix.entry[] + local all_items = {} + + ---@param result nil|lsp.Location|lsp.Location[] + ---@param client vim.lsp.Client + local function on_response(_, result, client) + local locations = {} + if result then + locations = vim.islist(result) and result or { result } + end + local items = util.locations_to_items(locations, client.offset_encoding) + vim.list_extend(all_items, items) + remaining = remaining - 1 + if remaining == 0 then + if vim.tbl_isempty(all_items) then + vim.notify('No locations found', vim.log.levels.INFO) + return + end + + local title = 'LSP locations' + if opts.on_list then + assert(vim.is_callable(opts.on_list), 'on_list is not a function') + opts.on_list({ + title = title, + items = all_items, + context = { bufnr = bufnr, method = method }, + }) + return + end + + if #all_items == 1 then + local item = all_items[1] + local b = item.bufnr or vim.fn.bufadd(item.filename) + vim.bo[b].buflisted = true + local w = opts.reuse_win and vim.fn.win_findbuf(b)[1] or win + api.nvim_win_set_buf(w, b) + api.nvim_win_set_cursor(w, { item.lnum, item.col - 1 }) + vim._with({ win = w }, function() + -- Open folds under the cursor + vim.cmd('normal! zv') + end) + return + end + if opts.loclist then + vim.fn.setloclist(0, {}, ' ', { title = title, items = all_items }) + vim.cmd.lopen() + else + vim.fn.setqflist({}, ' ', { title = title, items = all_items }) + vim.cmd('botright copen') + end + end + end + for _, client in ipairs(clients) do + local params = util.make_position_params(win, client.offset_encoding) + client.request(method, params, function(_, result) + on_response(_, result, client) + end) + end +end + --- @class vim.lsp.ListOpts --- --- list-handler replacing the default handler. @@ -87,30 +160,26 @@ end --- @note Many servers do not implement this method. Generally, see |vim.lsp.buf.definition()| instead. --- @param opts? vim.lsp.LocationOpts function M.declaration(opts) - local params = util.make_position_params() - request_with_opts(ms.textDocument_declaration, params, opts) + get_locations(ms.textDocument_declaration, opts) end --- Jumps to the definition of the symbol under the cursor. --- @param opts? vim.lsp.LocationOpts function M.definition(opts) - local params = util.make_position_params() - request_with_opts(ms.textDocument_definition, params, opts) + get_locations(ms.textDocument_definition, opts) end --- Jumps to the definition of the type of the symbol under the cursor. --- @param opts? vim.lsp.LocationOpts function M.type_definition(opts) - local params = util.make_position_params() - request_with_opts(ms.textDocument_typeDefinition, params, opts) + get_locations(ms.textDocument_typeDefinition, opts) end --- Lists all the implementations for the symbol under the cursor in the --- quickfix window. --- @param opts? vim.lsp.LocationOpts function M.implementation(opts) - local params = util.make_position_params() - request_with_opts(ms.textDocument_implementation, params, opts) + get_locations(ms.textDocument_implementation, opts) end --- Displays signature information about the symbol under the cursor in a @@ -438,12 +507,12 @@ end ---@param opts? vim.lsp.ListOpts function M.references(context, opts) validate('context', context, 'table', true) - local clients = vim.lsp.get_clients({ method = ms.textDocument_references }) + local bufnr = api.nvim_get_current_buf() + local clients = vim.lsp.get_clients({ method = ms.textDocument_references, bufnr = bufnr }) if not next(clients) then return end local win = api.nvim_get_current_win() - local bufnr = api.nvim_get_current_buf() opts = opts or {} local all_items = {} diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 3306e480dd..c87cf94b98 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -386,57 +386,6 @@ end --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover M[ms.textDocument_hover] = M.hover ---- Jumps to a location. Used as a handler for multiple LSP methods. ----@param _ nil not used ----@param result (table) result of LSP method; a location or a list of locations. ----@param ctx (lsp.HandlerContext) table containing the context of the request, including the method ----@param config? vim.lsp.LocationOpts ----(`textDocument/definition` can return `Location` or `Location[]` -local function location_handler(_, result, ctx, config) - if result == nil or vim.tbl_isempty(result) then - log.info(ctx.method, 'No location found') - return nil - end - local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) - - config = config or {} - - -- textDocument/definition can return Location or Location[] - -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_definition - if not vim.islist(result) then - result = { result } - end - - local title = 'LSP locations' - local items = util.locations_to_items(result, client.offset_encoding) - - if config.on_list then - assert(vim.is_callable(config.on_list), 'on_list is not a function') - config.on_list({ title = title, items = items }) - return - end - if #result == 1 then - util.jump_to_location(result[1], client.offset_encoding, config.reuse_win) - return - end - if config.loclist then - vim.fn.setloclist(0, {}, ' ', { title = title, items = items }) - vim.cmd.lopen() - else - vim.fn.setqflist({}, ' ', { title = title, items = items }) - vim.cmd('botright copen') - end -end - ---- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_declaration -M[ms.textDocument_declaration] = location_handler ---- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_definition -M[ms.textDocument_definition] = location_handler ---- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_typeDefinition -M[ms.textDocument_typeDefinition] = location_handler ---- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_implementation -M[ms.textDocument_implementation] = location_handler - local sig_help_ns = api.nvim_create_namespace('vim_lsp_signature_help') --- |lsp-handler| for the method "textDocument/signatureHelp". diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 83c8226326..2c28633e3c 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1023,18 +1023,13 @@ end --- Jumps to a location. --- +---@deprecated ---@param location lsp.Location|lsp.LocationLink ---@param offset_encoding 'utf-8'|'utf-16'|'utf-32'? ---@param reuse_win boolean? Jump to existing window if buffer is already open. ---@return boolean `true` if the jump succeeded function M.jump_to_location(location, offset_encoding, reuse_win) - if offset_encoding == nil then - vim.notify_once( - 'jump_to_location must be called with valid offset encoding', - vim.log.levels.WARN - ) - end - + vim.deprecate('vim.lsp.util.jump_to_location', nil, '0.12') return M.show_document(location, offset_encoding, { reuse_win = reuse_win, focus = true }) end -- cgit From c9c17fda80d843158d2785b047fca3a6dd78ea2f Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 17 Oct 2024 17:57:14 +0200 Subject: feat(deprecations): vim._defer_deprecated_module() --- runtime/lua/vim/shared.lua | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index d11489b539..fd6a37001d 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -1164,6 +1164,32 @@ function vim._defer_require(root, mod) }) end +--- @private +--- Creates a module alias/shim that lazy-loads a target module. +--- +--- Unlike `vim.defaulttable()` this also: +--- - implements __call +--- - calls vim.deprecate() +--- +--- @param old_name string Name of the deprecated module, which will be shimmed. +--- @param new_name string Name of the new module, which will be loaded by require(). +function vim._defer_deprecated_module(old_name, new_name) + return setmetatable({}, { + ---@param _ table + ---@param k string + __index = function(_, k) + vim.deprecate(old_name, new_name, '2.0.0', nil, false) + local target = require(new_name) + return target[k] + end, + __call = function(self) + vim.deprecate(old_name, new_name, '2.0.0', nil, false) + local target = require(new_name) + return target(self) + end, + }) +end + --- @nodoc --- @class vim.context.mods --- @field bo? table -- cgit From 960fdc775a88389d8b19389b8c74c98921b9acac Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 17 Oct 2024 17:58:13 +0200 Subject: fix(lua): vim.deprecate does not support major>0 --- runtime/lua/vim/_editor.lua | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index f0b8cf6a52..037cef9383 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -1149,16 +1149,22 @@ function vim.deprecate(name, alternative, version, plugin, backtrace) if plugin == 'Nvim' then require('vim.deprecated.health').add(name, version, traceback(), alternative) - -- Only issue warning if feature is hard-deprecated as specified by MAINTAIN.md. - -- Example: if removal_version is 0.12 (soft-deprecated since 0.10-dev), show warnings starting at - -- 0.11, including 0.11-dev + -- Show a warning only if feature is hard-deprecated (see MAINTAIN.md). + -- Example: if removal `version` is 0.12 (soft-deprecated since 0.10-dev), show warnings + -- starting at 0.11, including 0.11-dev. local major, minor = version:match('(%d+)%.(%d+)') major, minor = tonumber(major), tonumber(minor) + local nvim_major = 0 --- Current Nvim major version. + + -- We can't "subtract" from a major version, so: + -- * Always treat `major > nvim_major` as soft-deprecation. + -- * Compare `minor - 1` if `major == nvim_major`. + if major > nvim_major then + return -- Always soft-deprecation (see MAINTAIN.md). + end local hard_deprecated_since = string.format('nvim-%d.%d', major, minor - 1) - -- Assume there will be no next minor version before bumping up the major version - local is_hard_deprecated = minor == 0 or vim.fn.has(hard_deprecated_since) == 1 - if not is_hard_deprecated then + if major == nvim_major and vim.fn.has(hard_deprecated_since) == 0 then return end -- cgit From 18b43c331d8a0ed87d7cbefe2a18543b8e4ad360 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 17 Oct 2024 11:16:16 +0200 Subject: refactor: rename vim.highlight => vim.hl Problem: - `vim.highlight` module does not follow `:help dev-name-common`, which documents the name for "highlight" as "hl". - Shorter names are usually preferred. Solution: Rename `vim.highlight` to `vim.hl`. This is not a breaking change until 2.0 (or maybe never). --- runtime/lua/vim/_editor.lua | 5 +- runtime/lua/vim/_meta.lua | 2 +- runtime/lua/vim/diagnostic.lua | 4 +- runtime/lua/vim/highlight.lua | 194 ----------------------------- runtime/lua/vim/hl.lua | 194 +++++++++++++++++++++++++++++ runtime/lua/vim/lsp/handlers.lua | 2 +- runtime/lua/vim/lsp/semantic_tokens.lua | 6 +- runtime/lua/vim/lsp/util.lua | 5 +- runtime/lua/vim/treesitter/highlighter.lua | 2 +- 9 files changed, 208 insertions(+), 206 deletions(-) delete mode 100644 runtime/lua/vim/highlight.lua create mode 100644 runtime/lua/vim/hl.lua (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 037cef9383..7aba7498f9 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -32,7 +32,7 @@ for k, v in pairs({ func = true, F = true, lsp = true, - highlight = true, + hl = true, diagnostic = true, keymap = true, ui = true, @@ -1197,4 +1197,7 @@ require('vim._options') ---@deprecated vim.loop = vim.uv +-- Deprecated. Remove at Nvim 2.0 +vim.highlight = vim._defer_deprecated_module('vim.highlight', 'vim.hl') + return vim diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua index c9f207cb20..f8672d1f1f 100644 --- a/runtime/lua/vim/_meta.lua +++ b/runtime/lua/vim/_meta.lua @@ -15,7 +15,7 @@ vim.fs = require('vim.fs') vim.func = require('vim.func') vim.glob = require('vim.glob') vim.health = require('vim.health') -vim.highlight = require('vim.highlight') +vim.hl = require('vim.hl') vim.iter = require('vim.iter') vim.keymap = require('vim.keymap') vim.loader = require('vim.loader') diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 99fcfd57e7..3570956efd 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -1498,13 +1498,13 @@ M.handlers.underline = { end end - vim.highlight.range( + vim.hl.range( bufnr, underline_ns, higroup, { diagnostic.lnum, diagnostic.col }, { diagnostic.end_lnum, diagnostic.end_col }, - { priority = vim.highlight.priorities.diagnostics } + { priority = vim.hl.priorities.diagnostics } ) end save_extmarks(underline_ns, bufnr) diff --git a/runtime/lua/vim/highlight.lua b/runtime/lua/vim/highlight.lua deleted file mode 100644 index a8d88db372..0000000000 --- a/runtime/lua/vim/highlight.lua +++ /dev/null @@ -1,194 +0,0 @@ -local api = vim.api - -local M = {} - ---- Table with default priorities used for highlighting: ---- - `syntax`: `50`, used for standard syntax highlighting ---- - `treesitter`: `100`, used for treesitter-based highlighting ---- - `semantic_tokens`: `125`, used for LSP semantic token highlighting ---- - `diagnostics`: `150`, used for code analysis such as diagnostics ---- - `user`: `200`, used for user-triggered highlights such as LSP document ---- symbols or `on_yank` autocommands -M.priorities = { - syntax = 50, - treesitter = 100, - semantic_tokens = 125, - diagnostics = 150, - user = 200, -} - ---- @class vim.highlight.range.Opts ---- @inlinedoc ---- ---- Type of range. See [getregtype()] ---- (default: `'v'` i.e. charwise) ---- @field regtype? string ---- ---- Indicates whether the range is end-inclusive ---- (default: `false`) ---- @field inclusive? boolean ---- ---- Indicates priority of highlight ---- (default: `vim.highlight.priorities.user`) ---- @field priority? integer - ---- Apply highlight group to range of text. ---- ----@param bufnr integer Buffer number to apply highlighting to ----@param ns integer Namespace to add highlight to ----@param higroup string Highlight group to use for highlighting ----@param start integer[]|string Start of region as a (line, column) tuple or string accepted by |getpos()| ----@param finish integer[]|string End of region as a (line, column) tuple or string accepted by |getpos()| ----@param opts? vim.highlight.range.Opts -function M.range(bufnr, ns, higroup, start, finish, opts) - opts = opts or {} - local regtype = opts.regtype or 'v' - local inclusive = opts.inclusive or false - local priority = opts.priority or M.priorities.user - - local v_maxcol = vim.v.maxcol - - local pos1 = type(start) == 'string' and vim.fn.getpos(start) - or { - bufnr, - start[1] + 1, - start[2] ~= -1 and start[2] ~= v_maxcol and start[2] + 1 or v_maxcol, - 0, - } - local pos2 = type(finish) == 'string' and vim.fn.getpos(finish) - or { - bufnr, - finish[1] + 1, - finish[2] ~= -1 and start[2] ~= v_maxcol and finish[2] + 1 or v_maxcol, - 0, - } - - local buf_line_count = vim.api.nvim_buf_line_count(bufnr) - pos1[2] = math.min(pos1[2], buf_line_count) - pos2[2] = math.min(pos2[2], buf_line_count) - - if pos1[2] <= 0 or pos1[3] <= 0 or pos2[2] <= 0 or pos2[3] <= 0 then - return - end - - vim._with({ buf = bufnr }, function() - if pos1[3] ~= v_maxcol then - local max_col1 = vim.fn.col({ pos1[2], '$' }) - pos1[3] = math.min(pos1[3], max_col1) - end - if pos2[3] ~= v_maxcol then - local max_col2 = vim.fn.col({ pos2[2], '$' }) - pos2[3] = math.min(pos2[3], max_col2) - end - end) - - local region = vim.fn.getregionpos(pos1, pos2, { - type = regtype, - exclusive = not inclusive, - eol = true, - }) - -- For non-blockwise selection, use a single extmark. - if regtype == 'v' or regtype == 'V' then - region = { { region[1][1], region[#region][2] } } - if - regtype == 'V' - or region[1][2][2] == pos1[2] and pos1[3] == v_maxcol - or region[1][2][2] == pos2[2] and pos2[3] == v_maxcol - then - region[1][2][2] = region[1][2][2] + 1 - region[1][2][3] = 0 - end - end - - for _, res in ipairs(region) do - local start_row = res[1][2] - 1 - local start_col = res[1][3] - 1 - local end_row = res[2][2] - 1 - local end_col = res[2][3] - api.nvim_buf_set_extmark(bufnr, ns, start_row, start_col, { - hl_group = higroup, - end_row = end_row, - end_col = end_col, - priority = priority, - strict = false, - }) - end -end - -local yank_ns = api.nvim_create_namespace('hlyank') -local yank_timer --- @type uv.uv_timer_t? -local yank_cancel --- @type fun()? - ---- Highlight the yanked text during a |TextYankPost| event. ---- ---- Add the following to your `init.vim`: ---- ---- ```vim ---- autocmd TextYankPost * silent! lua vim.highlight.on_yank {higroup='Visual', timeout=300} ---- ``` ---- ---- @param opts table|nil Optional parameters ---- - higroup highlight group for yanked region (default "IncSearch") ---- - timeout time in ms before highlight is cleared (default 150) ---- - on_macro highlight when executing macro (default false) ---- - on_visual highlight when yanking visual selection (default true) ---- - event event structure (default vim.v.event) ---- - priority integer priority (default |vim.highlight.priorities|`.user`) -function M.on_yank(opts) - vim.validate({ - opts = { - opts, - function(t) - if t == nil then - return true - else - return type(t) == 'table' - end - end, - 'a table or nil to configure options (see `:h highlight.on_yank`)', - }, - }) - opts = opts or {} - local event = opts.event or vim.v.event - local on_macro = opts.on_macro or false - local on_visual = (opts.on_visual ~= false) - - if not on_macro and vim.fn.reg_executing() ~= '' then - return - end - if event.operator ~= 'y' or event.regtype == '' then - return - end - if not on_visual and event.visual then - return - end - - local higroup = opts.higroup or 'IncSearch' - local timeout = opts.timeout or 150 - - local bufnr = vim.api.nvim_get_current_buf() - local winid = vim.api.nvim_get_current_win() - if yank_timer then - yank_timer:close() - assert(yank_cancel) - yank_cancel() - end - - vim.api.nvim__ns_set(yank_ns, { wins = { winid } }) - M.range(bufnr, yank_ns, higroup, "'[", "']", { - regtype = event.regtype, - inclusive = event.inclusive, - priority = opts.priority or M.priorities.user, - }) - - yank_cancel = function() - yank_timer = nil - yank_cancel = nil - pcall(vim.api.nvim_buf_clear_namespace, bufnr, yank_ns, 0, -1) - pcall(vim.api.nvim__ns_set, { wins = {} }) - end - - yank_timer = vim.defer_fn(yank_cancel, timeout) -end - -return M diff --git a/runtime/lua/vim/hl.lua b/runtime/lua/vim/hl.lua new file mode 100644 index 0000000000..49bbaa3cc6 --- /dev/null +++ b/runtime/lua/vim/hl.lua @@ -0,0 +1,194 @@ +local api = vim.api + +local M = {} + +--- Table with default priorities used for highlighting: +--- - `syntax`: `50`, used for standard syntax highlighting +--- - `treesitter`: `100`, used for treesitter-based highlighting +--- - `semantic_tokens`: `125`, used for LSP semantic token highlighting +--- - `diagnostics`: `150`, used for code analysis such as diagnostics +--- - `user`: `200`, used for user-triggered highlights such as LSP document +--- symbols or `on_yank` autocommands +M.priorities = { + syntax = 50, + treesitter = 100, + semantic_tokens = 125, + diagnostics = 150, + user = 200, +} + +--- @class vim.hl.range.Opts +--- @inlinedoc +--- +--- Type of range. See [getregtype()] +--- (default: `'v'` i.e. charwise) +--- @field regtype? string +--- +--- Indicates whether the range is end-inclusive +--- (default: `false`) +--- @field inclusive? boolean +--- +--- Highlight priority +--- (default: `vim.hl.priorities.user`) +--- @field priority? integer + +--- Apply highlight group to range of text. +--- +---@param bufnr integer Buffer number to apply highlighting to +---@param ns integer Namespace to add highlight to +---@param higroup string Highlight group to use for highlighting +---@param start integer[]|string Start of region as a (line, column) tuple or string accepted by |getpos()| +---@param finish integer[]|string End of region as a (line, column) tuple or string accepted by |getpos()| +---@param opts? vim.hl.range.Opts +function M.range(bufnr, ns, higroup, start, finish, opts) + opts = opts or {} + local regtype = opts.regtype or 'v' + local inclusive = opts.inclusive or false + local priority = opts.priority or M.priorities.user + + local v_maxcol = vim.v.maxcol + + local pos1 = type(start) == 'string' and vim.fn.getpos(start) + or { + bufnr, + start[1] + 1, + start[2] ~= -1 and start[2] ~= v_maxcol and start[2] + 1 or v_maxcol, + 0, + } + local pos2 = type(finish) == 'string' and vim.fn.getpos(finish) + or { + bufnr, + finish[1] + 1, + finish[2] ~= -1 and start[2] ~= v_maxcol and finish[2] + 1 or v_maxcol, + 0, + } + + local buf_line_count = vim.api.nvim_buf_line_count(bufnr) + pos1[2] = math.min(pos1[2], buf_line_count) + pos2[2] = math.min(pos2[2], buf_line_count) + + if pos1[2] <= 0 or pos1[3] <= 0 or pos2[2] <= 0 or pos2[3] <= 0 then + return + end + + vim._with({ buf = bufnr }, function() + if pos1[3] ~= v_maxcol then + local max_col1 = vim.fn.col({ pos1[2], '$' }) + pos1[3] = math.min(pos1[3], max_col1) + end + if pos2[3] ~= v_maxcol then + local max_col2 = vim.fn.col({ pos2[2], '$' }) + pos2[3] = math.min(pos2[3], max_col2) + end + end) + + local region = vim.fn.getregionpos(pos1, pos2, { + type = regtype, + exclusive = not inclusive, + eol = true, + }) + -- For non-blockwise selection, use a single extmark. + if regtype == 'v' or regtype == 'V' then + region = { { region[1][1], region[#region][2] } } + if + regtype == 'V' + or region[1][2][2] == pos1[2] and pos1[3] == v_maxcol + or region[1][2][2] == pos2[2] and pos2[3] == v_maxcol + then + region[1][2][2] = region[1][2][2] + 1 + region[1][2][3] = 0 + end + end + + for _, res in ipairs(region) do + local start_row = res[1][2] - 1 + local start_col = res[1][3] - 1 + local end_row = res[2][2] - 1 + local end_col = res[2][3] + api.nvim_buf_set_extmark(bufnr, ns, start_row, start_col, { + hl_group = higroup, + end_row = end_row, + end_col = end_col, + priority = priority, + strict = false, + }) + end +end + +local yank_ns = api.nvim_create_namespace('hlyank') +local yank_timer --- @type uv.uv_timer_t? +local yank_cancel --- @type fun()? + +--- Highlight the yanked text during a |TextYankPost| event. +--- +--- Add the following to your `init.vim`: +--- +--- ```vim +--- autocmd TextYankPost * silent! lua vim.hl.on_yank {higroup='Visual', timeout=300} +--- ``` +--- +--- @param opts table|nil Optional parameters +--- - higroup highlight group for yanked region (default "IncSearch") +--- - timeout time in ms before highlight is cleared (default 150) +--- - on_macro highlight when executing macro (default false) +--- - on_visual highlight when yanking visual selection (default true) +--- - event event structure (default vim.v.event) +--- - priority integer priority (default |vim.hl.priorities|`.user`) +function M.on_yank(opts) + vim.validate({ + opts = { + opts, + function(t) + if t == nil then + return true + else + return type(t) == 'table' + end + end, + 'a table or nil to configure options (see `:h vim.hl.on_yank`)', + }, + }) + opts = opts or {} + local event = opts.event or vim.v.event + local on_macro = opts.on_macro or false + local on_visual = (opts.on_visual ~= false) + + if not on_macro and vim.fn.reg_executing() ~= '' then + return + end + if event.operator ~= 'y' or event.regtype == '' then + return + end + if not on_visual and event.visual then + return + end + + local higroup = opts.higroup or 'IncSearch' + local timeout = opts.timeout or 150 + + local bufnr = vim.api.nvim_get_current_buf() + local winid = vim.api.nvim_get_current_win() + if yank_timer then + yank_timer:close() + assert(yank_cancel) + yank_cancel() + end + + vim.api.nvim__ns_set(yank_ns, { wins = { winid } }) + M.range(bufnr, yank_ns, higroup, "'[", "']", { + regtype = event.regtype, + inclusive = event.inclusive, + priority = opts.priority or M.priorities.user, + }) + + yank_cancel = function() + yank_timer = nil + yank_cancel = nil + pcall(vim.api.nvim_buf_clear_namespace, bufnr, yank_ns, 0, -1) + pcall(vim.api.nvim__ns_set, { wins = {} }) + end + + yank_timer = vim.defer_fn(yank_cancel, timeout) +end + +return M diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index c87cf94b98..37cb07025c 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -437,7 +437,7 @@ function M.signature_help(_, result, ctx, config) local fbuf, fwin = util.open_floating_preview(lines, 'markdown', config) -- Highlight the active parameter. if hl then - vim.highlight.range( + vim.hl.range( fbuf, sig_help_ns, 'LspSignatureActiveParameter', diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index 2c7958b0d5..0f6e45c330 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -443,7 +443,7 @@ function STHighlighter:on_win(topline, botline) vim.api.nvim_buf_set_extmark(self.bufnr, state.namespace, token.line, token.start_col, { hl_group = hl_group, end_col = token.end_col, - priority = vim.highlight.priorities.semantic_tokens + delta, + priority = vim.hl.priorities.semantic_tokens + delta, strict = false, }) end @@ -723,7 +723,7 @@ end --- @inlinedoc --- --- Priority for the applied extmark. ---- (Default: `vim.highlight.priorities.semantic_tokens + 3`) +--- (Default: `vim.hl.priorities.semantic_tokens + 3`) --- @field priority? integer --- Highlight a semantic token. @@ -752,7 +752,7 @@ function M.highlight_token(token, bufnr, client_id, hl_group, opts) end opts = opts or {} - local priority = opts.priority or vim.highlight.priorities.semantic_tokens + 3 + local priority = opts.priority or vim.hl.priorities.semantic_tokens + 3 vim.api.nvim_buf_set_extmark(bufnr, state.namespace, token.line, token.start_col, { hl_group = hl_group, diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 2c28633e3c..2e9c71cf38 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -2,7 +2,6 @@ local protocol = require('vim.lsp.protocol') local validate = vim.validate local api = vim.api local list_extend = vim.list_extend -local highlight = vim.highlight local uv = vim.uv local M = {} @@ -1716,13 +1715,13 @@ do --[[ References ]] [protocol.DocumentHighlightKind.Write] = 'LspReferenceWrite', } local kind = reference['kind'] or protocol.DocumentHighlightKind.Text - highlight.range( + vim.hl.range( bufnr, reference_ns, document_highlight_kind[kind], { start_line, start_idx }, { end_line, end_idx }, - { priority = vim.highlight.priorities.user } + { priority = vim.hl.priorities.user } ) end end diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index a94c408f4e..7bdcdc774a 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -328,7 +328,7 @@ local function on_line_impl(self, buf, line, is_spell_nav) -- The "priority" attribute can be set at the pattern level or on a particular capture local priority = ( tonumber(metadata.priority or metadata[capture] and metadata[capture].priority) - or vim.highlight.priorities.treesitter + or vim.hl.priorities.treesitter ) + spell_pri_offset -- The "conceal" attribute can be set at the pattern level or on a particular capture -- cgit From 3572319b4cb1a4163624a5fe328886f1928dbc4a Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Fri, 18 Oct 2024 11:33:12 +0100 Subject: feat(vim.validate): improve fast form and deprecate spec form Problem: `vim.validate()` takes two forms when it only needs one. Solution: - Teach the fast form all the features of the spec form. - Deprecate the spec form. - General optimizations for both forms. - Add a `message` argument which can be used alongside or in place of the `optional` argument. --- runtime/lua/vim/_editor.lua | 12 +- runtime/lua/vim/_watch.lua | 8 +- runtime/lua/vim/diagnostic.lua | 56 +++--- runtime/lua/vim/fs.lua | 2 +- runtime/lua/vim/func/_memoize.lua | 6 +- runtime/lua/vim/hl.lua | 14 +- runtime/lua/vim/keymap.lua | 18 +- runtime/lua/vim/lsp/client.lua | 34 ++-- runtime/lua/vim/secure.lua | 16 +- runtime/lua/vim/shared.lua | 295 ++++++++++++++++---------------- runtime/lua/vim/termcap.lua | 6 +- runtime/lua/vim/treesitter.lua | 8 +- runtime/lua/vim/treesitter/language.lua | 6 +- runtime/lua/vim/ui.lua | 6 +- 14 files changed, 213 insertions(+), 274 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 7aba7498f9..58283ac64b 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -467,13 +467,11 @@ vim.cmd = setmetatable({}, { -- These are the vim.env/v/g/o/bo/wo variable magic accessors. do - local validate = vim.validate - --- @param scope string --- @param handle? false|integer --- @return vim.var_accessor local function make_dict_accessor(scope, handle) - validate('scope', scope, 'string') + vim.validate('scope', scope, 'string') local mt = {} function mt:__newindex(k, v) return vim._setvar(scope, handle or 0, k, v) @@ -589,7 +587,7 @@ end ---@param timeout integer Number of milliseconds to wait before calling `fn` ---@return table timer luv timer object function vim.defer_fn(fn, timeout) - vim.validate({ fn = { fn, 'c', true } }) + vim.validate('fn', fn, 'callable', true) local timer = assert(vim.uv.new_timer()) timer:start( timeout, @@ -680,10 +678,8 @@ function vim.on_key(fn, ns_id) return vim.tbl_count(on_key_cbs) end - vim.validate({ - fn = { fn, 'c', true }, - ns_id = { ns_id, 'n', true }, - }) + vim.validate('fn', fn, 'callable', true) + vim.validate('ns_id', ns_id, 'number', true) if ns_id == nil or ns_id == 0 then ns_id = vim.api.nvim_create_namespace('') diff --git a/runtime/lua/vim/_watch.lua b/runtime/lua/vim/_watch.lua index 7a306d1123..13894c6147 100644 --- a/runtime/lua/vim/_watch.lua +++ b/runtime/lua/vim/_watch.lua @@ -59,9 +59,9 @@ end --- @param callback vim._watch.Callback Callback for new events --- @return fun() cancel Stops the watcher function M.watch(path, opts, callback) - vim.validate('path', path, 'string', false) + vim.validate('path', path, 'string') vim.validate('opts', opts, 'table', true) - vim.validate('callback', callback, 'function', false) + vim.validate('callback', callback, 'function') opts = opts or {} @@ -125,9 +125,9 @@ end --- @param callback vim._watch.Callback Callback for new events --- @return fun() cancel Stops the watcher function M.watchdirs(path, opts, callback) - vim.validate('path', path, 'string', false) + vim.validate('path', path, 'string') vim.validate('opts', opts, 'table', true) - vim.validate('callback', callback, 'function', false) + vim.validate('callback', callback, 'function') opts = opts or {} local debounce = opts.debounce or 500 diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 3570956efd..392db5b800 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -478,7 +478,7 @@ end --- @return vim.Diagnostic[] local function reformat_diagnostics(format, diagnostics) vim.validate('format', format, 'function') - vim.validate({ diagnostics = { diagnostics, vim.islist, 'a list of diagnostics' } }) + vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics') local formatted = vim.deepcopy(diagnostics, true) for _, diagnostic in ipairs(formatted) do @@ -1056,7 +1056,7 @@ end function M.set(namespace, bufnr, diagnostics, opts) vim.validate('namespace', namespace, 'number') vim.validate('bufnr', bufnr, 'number') - vim.validate({ diagnostics = { diagnostics, vim.islist, 'a list of diagnostics' } }) + vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics') vim.validate('opts', opts, 'table', true) bufnr = get_bufnr(bufnr) @@ -1336,7 +1336,7 @@ M.handlers.signs = { show = function(namespace, bufnr, diagnostics, opts) vim.validate('namespace', namespace, 'number') vim.validate('bufnr', bufnr, 'number') - vim.validate({ diagnostics = { diagnostics, vim.islist, 'a list of diagnostics' } }) + vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics') vim.validate('opts', opts, 'table', true) bufnr = get_bufnr(bufnr) @@ -1457,7 +1457,7 @@ M.handlers.underline = { show = function(namespace, bufnr, diagnostics, opts) vim.validate('namespace', namespace, 'number') vim.validate('bufnr', bufnr, 'number') - vim.validate({ diagnostics = { diagnostics, vim.islist, 'a list of diagnostics' } }) + vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics') vim.validate('opts', opts, 'table', true) bufnr = get_bufnr(bufnr) @@ -1524,7 +1524,7 @@ M.handlers.virtual_text = { show = function(namespace, bufnr, diagnostics, opts) vim.validate('namespace', namespace, 'number') vim.validate('bufnr', bufnr, 'number') - vim.validate({ diagnostics = { diagnostics, vim.islist, 'a list of diagnostics' } }) + vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics') vim.validate('opts', opts, 'table', true) bufnr = get_bufnr(bufnr) @@ -1709,15 +1709,7 @@ end function M.show(namespace, bufnr, diagnostics, opts) vim.validate('namespace', namespace, 'number', true) vim.validate('bufnr', bufnr, 'number', true) - vim.validate({ - diagnostics = { - diagnostics, - function(v) - return v == nil or vim.islist(v) - end, - 'a list of diagnostics', - }, - }) + vim.validate('diagnostics', diagnostics, vim.islist, true, 'a list of diagnostics') vim.validate('opts', opts, 'table', true) if not bufnr or not namespace then @@ -1869,13 +1861,7 @@ function M.open_float(opts, ...) local highlights = {} --- @type table[] local header = if_nil(opts.header, 'Diagnostics:') if header then - vim.validate({ - header = { - header, - { 'string', 'table' }, - "'string' or 'table'", - }, - }) + vim.validate('header', header, { 'string', 'table' }, "'string' or 'table'") if type(header) == 'table' then -- Don't insert any lines for an empty string if string.len(if_nil(header[1], '')) > 0 then @@ -1903,13 +1889,12 @@ function M.open_float(opts, ...) local prefix, prefix_hl_group --- @type string?, string? if prefix_opt then - vim.validate({ - prefix = { - prefix_opt, - { 'string', 'table', 'function' }, - "'string' or 'table' or 'function'", - }, - }) + vim.validate( + 'prefix', + prefix_opt, + { 'string', 'table', 'function' }, + "'string' or 'table' or 'function'" + ) if type(prefix_opt) == 'string' then prefix, prefix_hl_group = prefix_opt, 'NormalFloat' elseif type(prefix_opt) == 'table' then @@ -1923,13 +1908,12 @@ function M.open_float(opts, ...) local suffix, suffix_hl_group --- @type string?, string? if suffix_opt then - vim.validate({ - suffix = { - suffix_opt, - { 'string', 'table', 'function' }, - "'string' or 'table' or 'function'", - }, - }) + vim.validate( + 'suffix', + suffix_opt, + { 'string', 'table', 'function' }, + "'string' or 'table' or 'function'" + ) if type(suffix_opt) == 'string' then suffix, suffix_hl_group = suffix_opt, 'NormalFloat' elseif type(suffix_opt) == 'table' then @@ -2239,7 +2223,7 @@ local errlist_type_map = { ---@param diagnostics vim.Diagnostic[] ---@return table[] : Quickfix list items |setqflist-what| function M.toqflist(diagnostics) - vim.validate({ diagnostics = { diagnostics, vim.islist, 'a list of diagnostics' } }) + vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics') local list = {} --- @type table[] for _, v in ipairs(diagnostics) do diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index c23cd5af1c..d91eeaf02f 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -229,7 +229,7 @@ end ---@return (string[]) # Normalized paths |vim.fs.normalize()| of all matching items function M.find(names, opts) opts = opts or {} - vim.validate({ names = { names, { 'string', 'table', 'function' } } }) + vim.validate('names', names, { 'string', 'table', 'function' }) vim.validate('path', opts.path, 'string', true) vim.validate('upward', opts.upward, 'boolean', true) vim.validate('stop', opts.stop, 'string', true) diff --git a/runtime/lua/vim/func/_memoize.lua b/runtime/lua/vim/func/_memoize.lua index 65210351bf..6e557905a7 100644 --- a/runtime/lua/vim/func/_memoize.lua +++ b/runtime/lua/vim/func/_memoize.lua @@ -39,10 +39,8 @@ end --- @param strong? boolean --- @return F return function(hash, fn, strong) - vim.validate({ - hash = { hash, { 'number', 'string', 'function' } }, - fn = { fn, 'function' }, - }) + vim.validate('hash', hash, { 'number', 'string', 'function' }) + vim.validate('fn', fn, 'function') ---@type table> local cache = {} diff --git a/runtime/lua/vim/hl.lua b/runtime/lua/vim/hl.lua index 49bbaa3cc6..099efa3c61 100644 --- a/runtime/lua/vim/hl.lua +++ b/runtime/lua/vim/hl.lua @@ -135,19 +135,7 @@ local yank_cancel --- @type fun()? --- - event event structure (default vim.v.event) --- - priority integer priority (default |vim.hl.priorities|`.user`) function M.on_yank(opts) - vim.validate({ - opts = { - opts, - function(t) - if t == nil then - return true - else - return type(t) == 'table' - end - end, - 'a table or nil to configure options (see `:h vim.hl.on_yank`)', - }, - }) + vim.validate('opts', opts, 'table', true) opts = opts or {} local event = opts.event or vim.v.event local on_macro = opts.on_macro or false diff --git a/runtime/lua/vim/keymap.lua b/runtime/lua/vim/keymap.lua index 50ca0d2d0e..4c19435ef8 100644 --- a/runtime/lua/vim/keymap.lua +++ b/runtime/lua/vim/keymap.lua @@ -42,12 +42,10 @@ local keymap = {} ---@see |mapcheck()| ---@see |mapset()| function keymap.set(mode, lhs, rhs, opts) - vim.validate({ - mode = { mode, { 's', 't' } }, - lhs = { lhs, 's' }, - rhs = { rhs, { 's', 'f' } }, - opts = { opts, 't', true }, - }) + vim.validate('mode', mode, { 'string', 'table' }) + vim.validate('lhs', lhs, 'string') + vim.validate('rhs', rhs, { 'string', 'function' }) + vim.validate('opts', opts, 'table', true) opts = vim.deepcopy(opts or {}, true) @@ -107,11 +105,9 @@ end ---@param opts? vim.keymap.del.Opts ---@see |vim.keymap.set()| function keymap.del(modes, lhs, opts) - vim.validate({ - mode = { modes, { 's', 't' } }, - lhs = { lhs, 's' }, - opts = { opts, 't', true }, - }) + vim.validate('mode', modes, { 'string', 'table' }) + vim.validate('lhs', lhs, 'string') + vim.validate('opts', opts, 'table', true) opts = opts or {} modes = type(modes) == 'string' and { modes } or modes diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index d5fc5b8908..d9d6b851d0 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -349,24 +349,22 @@ end --- @param config vim.lsp.ClientConfig local function validate_config(config) validate('config', config, 'table') - validate({ - handlers = { config.handlers, 't', true }, - capabilities = { config.capabilities, 't', true }, - cmd_cwd = { config.cmd_cwd, optional_validator(is_dir), 'directory' }, - cmd_env = { config.cmd_env, 't', true }, - detached = { config.detached, 'b', true }, - name = { config.name, 's', true }, - on_error = { config.on_error, 'f', true }, - on_exit = { config.on_exit, { 'f', 't' }, true }, - on_init = { config.on_init, { 'f', 't' }, true }, - on_attach = { config.on_attach, { 'f', 't' }, true }, - settings = { config.settings, 't', true }, - commands = { config.commands, 't', true }, - before_init = { config.before_init, { 'f', 't' }, true }, - offset_encoding = { config.offset_encoding, 's', true }, - flags = { config.flags, 't', true }, - get_language_id = { config.get_language_id, 'f', true }, - }) + validate('handlers', config.handlers, 'table', true) + validate('capabilities', config.capabilities, 'table', true) + validate('cmd_cwd', config.cmd_cwd, optional_validator(is_dir), 'directory') + validate('cmd_env', config.cmd_env, 'table', true) + validate('detached', config.detached, 'boolean', true) + validate('name', config.name, 'string', true) + validate('on_error', config.on_error, 'function', true) + validate('on_exit', config.on_exit, { 'function', 'table' }, true) + validate('on_init', config.on_init, { 'function', 'table' }, true) + validate('on_attach', config.on_attach, { 'function', 'table' }, true) + validate('settings', config.settings, 'table', true) + validate('commands', config.commands, 'table', true) + validate('before_init', config.before_init, { 'function', 'table' }, true) + validate('offset_encoding', config.offset_encoding, 'string', true) + validate('flags', config.flags, 'table', true) + validate('get_language_id', config.get_language_id, 'function', true) assert( ( diff --git a/runtime/lua/vim/secure.lua b/runtime/lua/vim/secure.lua index 0d3a271ac7..7b1d071270 100644 --- a/runtime/lua/vim/secure.lua +++ b/runtime/lua/vim/secure.lua @@ -132,17 +132,11 @@ end ---@return boolean success true if operation was successful ---@return string msg full path if operation was successful, else error message function M.trust(opts) - vim.validate({ - path = { opts.path, 's', true }, - bufnr = { opts.bufnr, 'n', true }, - action = { - opts.action, - function(m) - return m == 'allow' or m == 'deny' or m == 'remove' - end, - [["allow" or "deny" or "remove"]], - }, - }) + vim.validate('path', opts.path, 'string', true) + vim.validate('bufnr', opts.bufnr, 'number', true) + vim.validate('action', opts.action, function(m) + return m == 'allow' or m == 'deny' or m == 'remove' + end, [["allow" or "deny" or "remove"]]) ---@cast opts vim.trust.opts local path = opts.path diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index fd6a37001d..94a194cde9 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -251,7 +251,8 @@ end ---@param t table Table ---@return table : Table of transformed values function vim.tbl_map(func, t) - vim.validate({ func = { func, 'c' }, t = { t, 't' } }) + vim.validate('func', func, 'callable') + vim.validate('t', t, 'table') --- @cast t table local rettab = {} --- @type table @@ -268,7 +269,8 @@ end ---@param t table (table) Table ---@return T[] : Table of filtered values function vim.tbl_filter(func, t) - vim.validate({ func = { func, 'c' }, t = { t, 't' } }) + vim.validate('func', func, 'callable') + vim.validate('t', t, 'table') --- @cast t table local rettab = {} --- @type table @@ -311,7 +313,7 @@ function vim.tbl_contains(t, value, opts) local pred --- @type fun(v: any): boolean? if opts and opts.predicate then - vim.validate({ value = { value, 'c' } }) + vim.validate('value', value, 'callable') pred = value else pred = function(v) @@ -779,237 +781,226 @@ function vim.endswith(s, suffix) end do - --- @alias vim.validate.LuaType - --- | 'nil' - --- | 'number' - --- | 'string' - --- | 'boolean' - --- | 'table' - --- | 'function' - --- | 'thread' - --- | 'userdata' - --- - --- @alias vim.validate.Type vim.validate.LuaType | 't' | 's' | 'n' | 'f' | 'c' + --- @alias vim.validate.Validator + --- | type|'callable' + --- | (type|'callable')[] + --- | fun(v:any):boolean, string? - local type_names = { - ['table'] = 'table', - t = 'table', - ['string'] = 'string', - s = 'string', - ['number'] = 'number', - n = 'number', - ['boolean'] = 'boolean', + local type_aliases = { b = 'boolean', - ['function'] = 'function', - f = 'function', - ['callable'] = 'callable', c = 'callable', - ['nil'] = 'nil', - ['thread'] = 'thread', - ['userdata'] = 'userdata', + f = 'function', + n = 'number', + s = 'string', + t = 'table', } --- @nodoc - --- @class vim.validate.Spec [any, string|string[], boolean] + --- @class vim.validate.Spec --- @field [1] any Argument value - --- @field [2] vim.validate.Type|vim.validate.Type[]|fun(v:any):boolean, string? Type name, or callable - --- @field [3]? boolean + --- @field [2] vim.validate.Validator Argument validator + --- @field [3]? boolean|string Optional flag or error message - local function _is_type(val, t) + local function is_type(val, t) return type(val) == t or (t == 'callable' and vim.is_callable(val)) end --- @param param_name string - --- @param spec vim.validate.Spec + --- @param val any + --- @param validator vim.validate.Validator + --- @param message? string + --- @param allow_alias? boolean Allow short type names: 'n', 's', 't', 'b', 'f', 'c' --- @return string? - local function is_param_valid(param_name, spec) - if type(spec) ~= 'table' then - return string.format('opt[%s]: expected table, got %s', param_name, type(spec)) - end - - local val = spec[1] -- Argument value - local types = spec[2] -- Type name, or callable - local optional = (true == spec[3]) + local function is_valid(param_name, val, validator, message, allow_alias) + if type(validator) == 'string' then + local expected = allow_alias and type_aliases[validator] or validator - if type(types) == 'string' then - types = { types } - end + if not expected then + return string.format('invalid type name: %s', validator) + end - if vim.is_callable(types) then + if not is_type(val, expected) then + return string.format('%s: expected %s, got %s', param_name, expected, type(val)) + end + elseif vim.is_callable(validator) then -- Check user-provided validation function - local valid, optional_message = types(val) + local valid, opt_msg = validator(val) if not valid then - local error_message = - string.format('%s: expected %s, got %s', param_name, (spec[3] or '?'), tostring(val)) - if optional_message ~= nil then - error_message = string.format('%s. Info: %s', error_message, optional_message) + local err_msg = + string.format('%s: expected %s, got %s', param_name, message or '?', tostring(val)) + + if opt_msg then + err_msg = string.format('%s. Info: %s', err_msg, opt_msg) end - return error_message + return err_msg end - elseif type(types) == 'table' then - local success = false - for i, t in ipairs(types) do - local t_name = type_names[t] - if not t_name then + elseif type(validator) == 'table' then + for _, t in ipairs(validator) do + local expected = allow_alias and type_aliases[t] or t + if not expected then return string.format('invalid type name: %s', t) end - types[i] = t_name - if (optional and val == nil) or _is_type(val, t_name) then - success = true - break + if is_type(val, expected) then + return -- success end end - if not success then - return string.format( - '%s: expected %s, got %s', - param_name, - table.concat(types, '|'), - type(val) - ) + + -- Normalize validator types for error message + if allow_alias then + for i, t in ipairs(validator) do + validator[i] = type_aliases[t] or t + end end + + return string.format( + '%s: expected %s, got %s', + param_name, + table.concat(validator, '|'), + type(val) + ) else - return string.format('invalid type name: %s', tostring(types)) + return string.format('invalid validator: %s', tostring(validator)) end end - --- @param opt table - --- @return boolean, string? - local function is_valid(opt) - if type(opt) ~= 'table' then - return false, string.format('opt: expected table, got %s', type(opt)) - end - + --- @param opt table + --- @return string? + local function validate_spec(opt) local report --- @type table? for param_name, spec in pairs(opt) do - local msg = is_param_valid(param_name, spec) - if msg then + local err_msg --- @type string? + if type(spec) ~= 'table' then + err_msg = string.format('opt[%s]: expected table, got %s', param_name, type(spec)) + else + local value, validator = spec[1], spec[2] + local msg = type(spec[3]) == 'string' and spec[3] or nil --[[@as string?]] + local optional = spec[3] == true + if not (optional and value == nil) then + err_msg = is_valid(param_name, value, validator, msg, true) + end + end + + if err_msg then report = report or {} - report[param_name] = msg + report[param_name] = err_msg end end if report then for _, msg in vim.spairs(report) do -- luacheck: ignore - return false, msg + return msg end end - - return true end --- Validate function arguments. --- --- This function has two valid forms: --- - --- 1. vim.validate(name: str, value: any, type: string, optional?: bool) - --- 2. vim.validate(spec: table) + --- 1. `vim.validate(name, value, validator[, optional][, message])` --- - --- Form 1 validates that argument {name} with value {value} has the type - --- {type}. {type} must be a value returned by |lua-type()|. If {optional} is - --- true, then {value} may be null. This form is significantly faster and - --- should be preferred for simple cases. + --- Validates that argument {name} with value {value} satisfies + --- {validator}. If {optional} is given and is `true`, then {value} may be + --- `nil`. If {message} is given, then it is used as the expected type in the + --- error message. --- - --- Example: + --- Example: --- - --- ```lua - --- function vim.startswith(s, prefix) - --- vim.validate('s', s, 'string') - --- vim.validate('prefix', prefix, 'string') - --- ... - --- end - --- ``` + --- ```lua + --- function vim.startswith(s, prefix) + --- vim.validate('s', s, 'string') + --- vim.validate('prefix', prefix, 'string') + --- ... + --- end + --- ``` --- - --- Form 2 validates a parameter specification (types and values). Specs are - --- evaluated in alphanumeric order, until the first failure. + --- 2. `vim.validate(spec)` (deprecated) + --- where `spec` is of type + --- `table)` --- - --- Usage example: + --- Validates a argument specification. + --- Specs are evaluated in alphanumeric order, until the first failure. --- - --- ```lua - --- function user.new(name, age, hobbies) - --- vim.validate{ - --- name={name, 'string'}, - --- age={age, 'number'}, - --- hobbies={hobbies, 'table'}, - --- } - --- ... - --- end - --- ``` + --- Example: + --- + --- ```lua + --- function user.new(name, age, hobbies) + --- vim.validate{ + --- name={name, 'string'}, + --- age={age, 'number'}, + --- hobbies={hobbies, 'table'}, + --- } + --- ... + --- end + --- ``` --- --- Examples with explicit argument values (can be run directly): --- --- ```lua - --- vim.validate{arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}} + --- vim.validate('arg1', {'foo'}, 'table') + --- --> NOP (success) + --- vim.validate('arg2', 'foo', 'string') --- --> NOP (success) --- - --- vim.validate{arg1={1, 'table'}} + --- vim.validate('arg1', 1, 'table') --- --> error('arg1: expected table, got number') --- - --- vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}} + --- vim.validate('arg1', 3, function(a) return (a % 2) == 0 end, 'even number') --- --> error('arg1: expected even number, got 3') --- ``` --- --- If multiple types are valid they can be given as a list. --- --- ```lua - --- vim.validate{arg1={{'foo'}, {'table', 'string'}}, arg2={'foo', {'table', 'string'}}} + --- vim.validate('arg1', {'foo'}, {'table', 'string'}) + --- vim.validate('arg2', 'foo', {'table', 'string'}) --- -- NOP (success) --- - --- vim.validate{arg1={1, {'string', 'table'}}} + --- vim.validate('arg1', 1, {'string', 'table'}) --- -- error('arg1: expected string|table, got number') --- ``` --- - ---@param opt table (table) Names of parameters to validate. Each key is a parameter - --- name; each value is a tuple in one of these forms: - --- 1. (arg_value, type_name, optional) - --- - arg_value: argument value - --- - type_name: string|table type name, one of: ("table", "t", "string", - --- "s", "number", "n", "boolean", "b", "function", "f", "nil", - --- "thread", "userdata") or list of them. - --- - optional: (optional) boolean, if true, `nil` is valid - --- 2. (arg_value, fn, msg) - --- - arg_value: argument value - --- - fn: any function accepting one argument, returns true if and - --- only if the argument is valid. Can optionally return an additional - --- informative error message as the second returned value. - --- - msg: (optional) error string if validation fails - --- @overload fun(name: string, val: any, expected: vim.validate.LuaType, optional?: boolean) - function vim.validate(opt, ...) - local ok = false - local err_msg ---@type string? - local narg = select('#', ...) - if narg == 0 then - ok, err_msg = is_valid(opt) - elseif narg >= 2 then - -- Overloaded signature for fast/simple cases - local name = opt --[[@as string]] - local v, expected, optional = ... ---@type string, string, boolean? - local actual = type(v) - - ok = (actual == expected) or (v == nil and optional == true) + --- @note `validator` set to a value returned by |lua-type()| provides the + --- best performance. + --- + --- @param name string Argument name + --- @param value string Argument value + --- @param validator vim.validate.Validator + --- - (`string|string[]`): Any value that can be returned from |lua-type()| in addition to + --- `'callable'`: `'boolean'`, `'callable'`, `'function'`, `'nil'`, `'number'`, `'string'`, `'table'`, + --- `'thread'`, `'userdata'`. + --- - (`fun(val:any): boolean, string?`) A function that returns a boolean and an optional + --- string message. + --- @param optional? boolean Argument is optional (may be omitted) + --- @param message? string message when validation fails + --- @overload fun(name: string, val: any, validator: vim.validate.Validator, message: string) + --- @overload fun(spec: table) + function vim.validate(name, value, validator, optional, message) + local err_msg --- @type string? + if validator then -- Form 1 + -- Check validator as a string first to optimize the common case. + local ok = (type(value) == validator) or (value == nil and optional == true) if not ok then - if not jit and (actual ~= 'string' or actual ~= 'number') then - -- PUC-Lua can only handle string and number for %s in string.format() - v = vim.inspect(v) - end - err_msg = ('%s: expected %s, got %s%s'):format( - name, - expected, - actual, - v and (' (%s)'):format(v) or '' - ) + local msg = type(optional) == 'string' and optional or message --[[@as string?]] + -- Check more complicated validators + err_msg = is_valid(name, value, validator, msg, false) end + elseif type(name) == 'table' then -- Form 2 + vim.deprecate('vim.validate', 'vim.validate(name, value, validator, optional_or_msg)', '1.0') + err_msg = validate_spec(name) else error('invalid arguments') end - if not ok then + if err_msg then error(err_msg, 2) end end end + --- Returns true if object `f` can be called as a function. --- ---@param f any Any object diff --git a/runtime/lua/vim/termcap.lua b/runtime/lua/vim/termcap.lua index 1da2e71839..2c36561587 100644 --- a/runtime/lua/vim/termcap.lua +++ b/runtime/lua/vim/termcap.lua @@ -17,10 +17,8 @@ local M = {} --- otherwise. {seq} is the control sequence for the capability if found, or nil for --- boolean capabilities. function M.query(caps, cb) - vim.validate({ - caps = { caps, { 'string', 'table' } }, - cb = { cb, 'f' }, - }) + vim.validate('caps', caps, { 'string', 'table' }) + vim.validate('cb', cb, 'function') if type(caps) ~= 'table' then caps = { caps } diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index c24a732fe3..9f6dc53932 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -241,11 +241,9 @@ end --- ---@return boolean True if the {node} contains the {range} function M.node_contains(node, range) - vim.validate({ - -- allow a table so nodes can be mocked - node = { node, { 'userdata', 'table' } }, - range = { range, M._range.validate, 'integer list with 4 or 6 elements' }, - }) + -- allow a table so nodes can be mocked + vim.validate('node', node, { 'userdata', 'table' }) + vim.validate('range', range, M._range.validate, 'integer list with 4 or 6 elements') return M._range.contains({ node:range() }, range) end diff --git a/runtime/lua/vim/treesitter/language.lua b/runtime/lua/vim/treesitter/language.lua index ce12ca433a..446051dfd7 100644 --- a/runtime/lua/vim/treesitter/language.lua +++ b/runtime/lua/vim/treesitter/language.lua @@ -154,10 +154,8 @@ end --- @param lang string Name of parser --- @param filetype string|string[] Filetype(s) to associate with lang function M.register(lang, filetype) - vim.validate({ - lang = { lang, 'string' }, - filetype = { filetype, { 'string', 'table' } }, - }) + vim.validate('lang', lang, 'string') + vim.validate('filetype', filetype, { 'string', 'table' }) for _, f in ipairs(ensure_list(filetype)) do if f ~= '' then diff --git a/runtime/lua/vim/ui.lua b/runtime/lua/vim/ui.lua index f66b34ac44..b2e972d07a 100644 --- a/runtime/lua/vim/ui.lua +++ b/runtime/lua/vim/ui.lua @@ -37,8 +37,8 @@ local M = {} --- `idx` is the 1-based index of `item` within `items`. --- `nil` if the user aborted the dialog. function M.select(items, opts, on_choice) - vim.validate('items', items, 'table', false) - vim.validate('on_choice', on_choice, 'function', false) + vim.validate('items', items, 'table') + vim.validate('on_choice', on_choice, 'function') opts = opts or {} local choices = { opts.prompt or 'Select one of:' } local format_item = opts.format_item or tostring @@ -85,7 +85,7 @@ end --- `nil` if the user aborted the dialog. function M.input(opts, on_confirm) vim.validate('opts', opts, 'table', true) - vim.validate('on_confirm', on_confirm, 'function', false) + vim.validate('on_confirm', on_confirm, 'function') opts = (opts and not vim.tbl_isempty(opts)) and opts or vim.empty_dict() -- cgit From c8e47f648052f8001d8a493cf7ca6c4867a36bc3 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Mon, 21 Oct 2024 11:36:02 +0100 Subject: fix(meta): do not use hyphens in param names Fixes #30882 --- runtime/lua/vim/_meta/vimfn.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index f40a94a442..bde3101479 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -221,16 +221,16 @@ function vim.fn.assert_beeps(cmd) end --- @return 0|1 function vim.fn.assert_equal(expected, actual, msg) end ---- When the files {fname-one} and {fname-two} do not contain +--- When the files {fname_one} and {fname_two} do not contain --- exactly the same text an error message is added to |v:errors|. --- Also see |assert-return|. ---- When {fname-one} or {fname-two} does not exist the error will +--- When {fname_one} or {fname_two} does not exist the error will --- mention that. --- ---- @param fname-one string ---- @param fname-two string +--- @param fname_one string +--- @param fname_two string --- @return 0|1 -function vim.fn.assert_equalfile(fname-one, fname-two) end +function vim.fn.assert_equalfile(fname_one, fname_two) end --- When v:exception does not contain the string {error} an error --- message is added to |v:errors|. Also see |assert-return|. -- cgit From b7779aad41dada52cf08aecbe2ca941edd2ecd59 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Tue, 22 Oct 2024 00:14:43 +0200 Subject: vim-patch:9.1.0795: filetype: Vivado memory info file are not recognized Problem: filetype: Vivado memory info file are not recognized Solution: detect '*.mmi' memory info file as xml filetype (Wu, Zhenyu) References: https://docs.amd.com/r/en-US/ug1580-updatemem/MMI-File-Syntax closes: vim/vim#15906 https://github.com/vim/vim/commit/0887e62bce3f46c20d2fa5f8ece1ca001e44ce63 Co-authored-by: Wu, Zhenyu --- runtime/lua/vim/filetype.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 280d665fea..b824b60536 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1304,6 +1304,7 @@ local extension = { xpfm = 'xml', spfm = 'xml', bxml = 'xml', + mmi = 'xml', xcu = 'xml', xlb = 'xml', xlc = 'xml', -- cgit From a6d1165771781732cb8ff6105df6f0429c46cde1 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Tue, 22 Oct 2024 00:16:48 +0200 Subject: vim-patch:9.1.0796: filetype: libtool files are not recognized Problem: filetype: libtool files are not recognized Solution: detect '*.{lo,la,lai}' as sh filetype (Wu, Zhenyu) closes: vim/vim#15751 https://github.com/vim/vim/commit/bfe568d8c49662c3a3485834066c0a4c32ded56b Co-authored-by: Wu, Zhenyu --- runtime/lua/vim/filetype.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index b824b60536..2e7c95f85c 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1068,6 +1068,9 @@ local extension = { envrc = detect.sh, ksh = detect.ksh, sh = detect.sh, + lo = 'sh', + la = 'sh', + lai = 'sh', mdd = 'sh', sieve = 'sieve', siv = 'sieve', -- cgit From 07b4cb6ada2da108bbf8277bec63068234ceaa67 Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Tue, 22 Oct 2024 22:14:01 +0600 Subject: docs(options): remove description for hidden options #30903 Problem: Hidden options are documented despite being no-ops. Solution: Remove docs for hidden options. Move tags for options that we plan to restore, to ":help nvim-missing". Move tags for permanently removed options, to ":help nvim-removed". --- runtime/lua/vim/_meta/options.lua | 259 -------------------------------------- 1 file changed, 259 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index cf70429ce5..00f7554832 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -575,19 +575,6 @@ vim.o.briopt = vim.o.breakindentopt vim.wo.breakindentopt = vim.o.breakindentopt vim.wo.briopt = vim.wo.breakindentopt ---- Which directory to use for the file browser: ---- last Use same directory as with last file browser, where a ---- file was opened or saved. ---- buffer Use the directory of the related buffer. ---- current Use the current directory. ---- {path} Use the specified directory ---- ---- @type string -vim.o.browsedir = "" -vim.o.bsdir = vim.o.browsedir -vim.go.browsedir = vim.o.browsedir -vim.go.bsdir = vim.go.browsedir - --- This option specifies what happens when a buffer is no longer --- displayed in a window: --- follow the global 'hidden' option @@ -2900,148 +2887,6 @@ vim.o.gfw = vim.o.guifontwide vim.go.guifontwide = vim.o.guifontwide vim.go.gfw = vim.go.guifontwide ---- This option only has an effect in the GUI version of Vim. It is a ---- sequence of letters which describes what components and options of the ---- GUI should be used. ---- To avoid problems with flags that are added in the future, use the ---- "+=" and "-=" feature of ":set" `add-option-flags`. ---- ---- Valid letters are as follows: ---- *guioptions_a* *'go-a'* ---- 'a' Autoselect: If present, then whenever VISUAL mode is started, ---- or the Visual area extended, Vim tries to become the owner of ---- the windowing system's global selection. This means that the ---- Visually highlighted text is available for pasting into other ---- applications as well as into Vim itself. When the Visual mode ---- ends, possibly due to an operation on the text, or when an ---- application wants to paste the selection, the highlighted text ---- is automatically yanked into the "* selection register. ---- Thus the selection is still available for pasting into other ---- applications after the VISUAL mode has ended. ---- If not present, then Vim won't become the owner of the ---- windowing system's global selection unless explicitly told to ---- by a yank or delete operation for the "* register. ---- The same applies to the modeless selection. ---- *'go-P'* ---- 'P' Like autoselect but using the "+ register instead of the "* ---- register. ---- *'go-A'* ---- 'A' Autoselect for the modeless selection. Like 'a', but only ---- applies to the modeless selection. ---- ---- 'guioptions' autoselect Visual autoselect modeless ~ ---- "" - - ---- "a" yes yes ---- "A" - yes ---- "aA" yes yes ---- ---- *'go-c'* ---- 'c' Use console dialogs instead of popup dialogs for simple ---- choices. ---- *'go-d'* ---- 'd' Use dark theme variant if available. ---- *'go-e'* ---- 'e' Add tab pages when indicated with 'showtabline'. ---- 'guitablabel' can be used to change the text in the labels. ---- When 'e' is missing a non-GUI tab pages line may be used. ---- The GUI tabs are only supported on some systems, currently ---- Mac OS/X and MS-Windows. ---- *'go-i'* ---- 'i' Use a Vim icon. ---- *'go-m'* ---- 'm' Menu bar is present. ---- *'go-M'* ---- 'M' The system menu "$VIMRUNTIME/menu.vim" is not sourced. Note ---- that this flag must be added in the vimrc file, before ---- switching on syntax or filetype recognition (when the `gvimrc` ---- file is sourced the system menu has already been loaded; the ---- `:syntax on` and `:filetype on` commands load the menu too). ---- *'go-g'* ---- 'g' Grey menu items: Make menu items that are not active grey. If ---- 'g' is not included inactive menu items are not shown at all. ---- *'go-T'* ---- 'T' Include Toolbar. Currently only in Win32 GUI. ---- *'go-r'* ---- 'r' Right-hand scrollbar is always present. ---- *'go-R'* ---- 'R' Right-hand scrollbar is present when there is a vertically ---- split window. ---- *'go-l'* ---- 'l' Left-hand scrollbar is always present. ---- *'go-L'* ---- 'L' Left-hand scrollbar is present when there is a vertically ---- split window. ---- *'go-b'* ---- 'b' Bottom (horizontal) scrollbar is present. Its size depends on ---- the longest visible line, or on the cursor line if the 'h' ---- flag is included. `gui-horiz-scroll` ---- *'go-h'* ---- 'h' Limit horizontal scrollbar size to the length of the cursor ---- line. Reduces computations. `gui-horiz-scroll` ---- ---- And yes, you may even have scrollbars on the left AND the right if ---- you really want to :-). See `gui-scrollbars` for more information. ---- ---- *'go-v'* ---- 'v' Use a vertical button layout for dialogs. When not included, ---- a horizontal layout is preferred, but when it doesn't fit a ---- vertical layout is used anyway. Not supported in GTK 3. ---- *'go-p'* ---- 'p' Use Pointer callbacks for X11 GUI. This is required for some ---- window managers. If the cursor is not blinking or hollow at ---- the right moment, try adding this flag. This must be done ---- before starting the GUI. Set it in your `gvimrc`. Adding or ---- removing it after the GUI has started has no effect. ---- *'go-k'* ---- 'k' Keep the GUI window size when adding/removing a scrollbar, or ---- toolbar, tabline, etc. Instead, the behavior is similar to ---- when the window is maximized and will adjust 'lines' and ---- 'columns' to fit to the window. Without the 'k' flag Vim will ---- try to keep 'lines' and 'columns' the same when adding and ---- removing GUI components. ---- ---- @type string -vim.o.guioptions = "" -vim.o.go = vim.o.guioptions -vim.go.guioptions = vim.o.guioptions -vim.go.go = vim.go.guioptions - ---- When non-empty describes the text to use in a label of the GUI tab ---- pages line. When empty and when the result is empty Vim will use a ---- default label. See `setting-guitablabel` for more info. ---- ---- The format of this option is like that of 'statusline'. ---- 'guitabtooltip' is used for the tooltip, see below. ---- The expression will be evaluated in the `sandbox` when set from a ---- modeline, see `sandbox-option`. ---- This option cannot be set in a modeline when 'modelineexpr' is off. ---- ---- Only used when the GUI tab pages line is displayed. 'e' must be ---- present in 'guioptions'. For the non-GUI tab pages line 'tabline' is ---- used. ---- ---- @type string -vim.o.guitablabel = "" -vim.o.gtl = vim.o.guitablabel -vim.go.guitablabel = vim.o.guitablabel -vim.go.gtl = vim.go.guitablabel - ---- When non-empty describes the text to use in a tooltip for the GUI tab ---- pages line. When empty Vim will use a default tooltip. ---- This option is otherwise just like 'guitablabel' above. ---- You can include a line break. Simplest method is to use `:let`: ---- ---- ```vim ---- let &guitabtooltip = "line one\nline two" ---- ``` ---- ---- ---- @type string -vim.o.guitabtooltip = "" -vim.o.gtt = vim.o.guitabtooltip -vim.go.guitabtooltip = vim.o.guitabtooltip -vim.go.gtt = vim.go.guitabtooltip - --- Name of the main help file. All distributed help files should be --- placed together in one directory. Additionally, all "doc" directories --- in 'runtimepath' will be used. @@ -3184,29 +3029,6 @@ vim.o.ic = vim.o.ignorecase vim.go.ignorecase = vim.o.ignorecase vim.go.ic = vim.go.ignorecase ---- When set the Input Method is always on when starting to edit a command ---- line, unless entering a search pattern (see 'imsearch' for that). ---- Setting this option is useful when your input method allows entering ---- English characters directly, e.g., when it's used to type accented ---- characters with dead keys. ---- ---- @type boolean -vim.o.imcmdline = false -vim.o.imc = vim.o.imcmdline -vim.go.imcmdline = vim.o.imcmdline -vim.go.imc = vim.go.imcmdline - ---- When set the Input Method is never used. This is useful to disable ---- the IM when it doesn't work properly. ---- Currently this option is on by default for SGI/IRIX machines. This ---- may change in later releases. ---- ---- @type boolean -vim.o.imdisable = false -vim.o.imd = vim.o.imdisable -vim.go.imdisable = vim.o.imdisable -vim.go.imd = vim.go.imdisable - --- Specifies whether :lmap or an Input Method (IM) is to be used in --- Insert mode. Valid values: --- 0 :lmap is off and IM is off @@ -4491,74 +4313,6 @@ vim.go.mousemev = vim.go.mousemoveevent vim.o.mousescroll = "ver:3,hor:6" vim.go.mousescroll = vim.o.mousescroll ---- This option tells Vim what the mouse pointer should look like in ---- different modes. The option is a comma-separated list of parts, much ---- like used for 'guicursor'. Each part consist of a mode/location-list ---- and an argument-list: ---- mode-list:shape,mode-list:shape,.. ---- The mode-list is a dash separated list of these modes/locations: ---- In a normal window: ~ ---- n Normal mode ---- v Visual mode ---- ve Visual mode with 'selection' "exclusive" (same as 'v', ---- if not specified) ---- o Operator-pending mode ---- i Insert mode ---- r Replace mode ---- ---- Others: ~ ---- c appending to the command-line ---- ci inserting in the command-line ---- cr replacing in the command-line ---- m at the 'Hit ENTER' or 'More' prompts ---- ml idem, but cursor in the last line ---- e any mode, pointer below last window ---- s any mode, pointer on a status line ---- sd any mode, while dragging a status line ---- vs any mode, pointer on a vertical separator line ---- vd any mode, while dragging a vertical separator line ---- a everywhere ---- ---- The shape is one of the following: ---- avail name looks like ~ ---- w x arrow Normal mouse pointer ---- w x blank no pointer at all (use with care!) ---- w x beam I-beam ---- w x updown up-down sizing arrows ---- w x leftright left-right sizing arrows ---- w x busy The system's usual busy pointer ---- w x no The system's usual "no input" pointer ---- x udsizing indicates up-down resizing ---- x lrsizing indicates left-right resizing ---- x crosshair like a big thin + ---- x hand1 black hand ---- x hand2 white hand ---- x pencil what you write with ---- x question big ? ---- x rightup-arrow arrow pointing right-up ---- w x up-arrow arrow pointing up ---- x any X11 pointer number (see X11/cursorfont.h) ---- ---- The "avail" column contains a 'w' if the shape is available for Win32, ---- x for X11. ---- Any modes not specified or shapes not available use the normal mouse ---- pointer. ---- ---- Example: ---- ---- ```vim ---- set mouseshape=s:udsizing,m:no ---- ``` ---- will make the mouse turn to a sizing arrow over the status lines and ---- indicate no input when the hit-enter prompt is displayed (since ---- clicking the mouse has no effect in this state.) ---- ---- @type string -vim.o.mouseshape = "" -vim.o.mouses = vim.o.mouseshape -vim.go.mouseshape = vim.o.mouseshape -vim.go.mouses = vim.go.mouseshape - --- Defines the maximum time in msec between two mouse clicks for the --- second click to be recognized as a multi click. --- @@ -4678,19 +4432,6 @@ vim.o.ofu = vim.o.omnifunc vim.bo.omnifunc = vim.o.omnifunc vim.bo.ofu = vim.bo.omnifunc ---- only for Windows ---- Enable reading and writing from devices. This may get Vim stuck on a ---- device that can be opened but doesn't actually do the I/O. Therefore ---- it is off by default. ---- Note that on Windows editing "aux.h", "lpt1.txt" and the like also ---- result in editing a device. ---- ---- @type boolean -vim.o.opendevice = false -vim.o.odev = vim.o.opendevice -vim.go.opendevice = vim.o.opendevice -vim.go.odev = vim.go.opendevice - --- This option specifies a function to be called by the `g@` operator. --- See `:map-operator` for more info and an example. The value can be --- the name of a function, a `lambda` or a `Funcref`. See -- cgit From 629a5b71b55e439392e2a5791940f6dc8d0dd7c0 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 22 Oct 2024 12:16:24 +0100 Subject: fix(lsp): support multiple clients in typehierarchy --- runtime/lua/vim/lsp/buf.lua | 139 +++++++++++++++++++++++--------------------- runtime/lua/vim/ui.lua | 5 +- 2 files changed, 75 insertions(+), 69 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 7bf3565499..fc41246588 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -569,6 +569,23 @@ function M.document_symbol(opts) request_with_opts(ms.textDocument_documentSymbol, params, opts) end +--- @param client_id integer +--- @param method string +--- @param params table +--- @param handler? lsp.Handler +--- @param bufnr? integer +local function request_with_id(client_id, method, params, handler, bufnr) + local client = vim.lsp.get_client_by_id(client_id) + if not client then + vim.notify( + string.format('Client with id=%d disappeared during call hierarchy request', client_id), + vim.log.levels.WARN + ) + return + end + client.request(method, params, handler, bufnr) +end + --- @param call_hierarchy_items lsp.CallHierarchyItem[] --- @return lsp.CallHierarchyItem? local function pick_call_hierarchy_item(call_hierarchy_items) @@ -600,19 +617,11 @@ local function call_hierarchy(method) vim.notify('No item resolved', vim.log.levels.WARN) return end - local call_hierarchy_item = pick_call_hierarchy_item(result) - if not call_hierarchy_item then + local item = pick_call_hierarchy_item(result) + if not item then return end - local client = vim.lsp.get_client_by_id(ctx.client_id) - if client then - client.request(method, { item = call_hierarchy_item }, nil, ctx.bufnr) - else - vim.notify( - string.format('Client with id=%d disappeared during call hierarchy request', ctx.client_id), - vim.log.levels.WARN - ) - end + request_with_id(ctx.client_id, method, { item = item }, nil, ctx.bufnr) end) end @@ -630,78 +639,74 @@ function M.outgoing_calls() call_hierarchy(ms.callHierarchy_outgoingCalls) end +--- @param item lsp.TypeHierarchyItem +local function format_type_hierarchy_item(item) + if not item.detail or #item.detail == 0 then + return item.name + end + return string.format('%s %s', item.name, item.detail) +end + --- Lists all the subtypes or supertypes of the symbol under the --- cursor in the |quickfix| window. If the symbol can resolve to --- multiple items, the user can pick one using |vim.ui.select()|. ---@param kind "subtypes"|"supertypes" function M.typehierarchy(kind) local method = kind == 'subtypes' and ms.typeHierarchy_subtypes or ms.typeHierarchy_supertypes - - --- Merge results from multiple clients into a single table. Client-ID is preserved. - --- - --- @param results table - --- @return [integer, lsp.TypeHierarchyItem][] - local function merge_results(results) - local merged_results = {} - for client_id, client_result in pairs(results) do - if client_result.error then - vim.notify(client_result.error.message, vim.log.levels.WARN) - elseif client_result.result then - for _, item in pairs(client_result.result) do - table.insert(merged_results, { client_id, item }) - end - end - end - return merged_results + local bufnr = api.nvim_get_current_buf() + local clients = vim.lsp.get_clients({ bufnr = bufnr, method = method }) + if not next(clients) then + vim.notify(vim.lsp._unsupported_method(method), vim.log.levels.WARN) + return end - local bufnr = api.nvim_get_current_buf() - local params = util.make_position_params() - --- @param results table - vim.lsp.buf_request_all(bufnr, ms.textDocument_prepareTypeHierarchy, params, function(results) - local merged_results = merge_results(results) - if #merged_results == 0 then - vim.notify('No items resolved', vim.log.levels.INFO) - return - end + local win = api.nvim_get_current_win() - if #merged_results == 1 then - local item = merged_results[1] - local client = vim.lsp.get_client_by_id(item[1]) - if client then - client.request(method, { item = item[2] }, nil, bufnr) - else - vim.notify( - string.format('Client with id=%d disappeared during call hierarchy request', item[1]), - vim.log.levels.WARN - ) - end + --- @param results [integer, lsp.TypeHierarchyItem][] + local function on_response(results) + if #results == 0 then + vim.notify('No items resolved', vim.log.levels.INFO) + elseif #results == 1 then + local client_id, item = results[1][1], results[1][2] + request_with_id(client_id, method, { item = item }, nil, bufnr) else - local select_opts = { + vim.ui.select(results, { prompt = 'Select a type hierarchy item:', kind = 'typehierarchy', - format_item = function(item) - if not item[2].detail or #item[2].detail == 0 then - return item[2].name - end - return string.format('%s %s', item[2].name, item[2].detail) + format_item = function(x) + return format_type_hierarchy_item(x[2]) end, - } - - vim.ui.select(merged_results, select_opts, function(item) - local client = vim.lsp.get_client_by_id(item[1]) - if client then - --- @type lsp.TypeHierarchyItem - client.request(method, { item = item[2] }, nil, bufnr) - else - vim.notify( - string.format('Client with id=%d disappeared during call hierarchy request', item[1]), - vim.log.levels.WARN - ) + }, function(x) + if x then + local client_id, item = x[1], x[2] + request_with_id(client_id, method, { item = item }, nil, bufnr) end end) end - end) + end + + local results = {} --- @type [integer, lsp.TypeHierarchyItem][] + + local remaining = #clients + + for _, client in ipairs(clients) do + local params = util.make_position_params(win, client.offset_encoding) + client.request(method, params, function(err, result, ctx) + --- @cast result lsp.TypeHierarchyItem[]? + if err then + vim.notify(err.message, vim.log.levels.WARN) + elseif result then + for _, item in pairs(result) do + results[#results + 1] = { ctx.client_id, item } + end + end + + remaining = remaining - 1 + if remaining == 0 then + on_response(results) + end + end, bufnr) + end end --- List workspace folders. diff --git a/runtime/lua/vim/ui.lua b/runtime/lua/vim/ui.lua index b2e972d07a..899709d3cf 100644 --- a/runtime/lua/vim/ui.lua +++ b/runtime/lua/vim/ui.lua @@ -20,7 +20,8 @@ local M = {} --- end) --- ``` --- ----@param items any[] Arbitrary items +---@generic T +---@param items T[] Arbitrary items ---@param opts table Additional options --- - prompt (string|nil) --- Text of the prompt. Defaults to `Select one of:` @@ -32,7 +33,7 @@ local M = {} --- Plugins reimplementing `vim.ui.select` may wish to --- use this to infer the structure or semantics of --- `items`, or the context in which select() was called. ----@param on_choice fun(item: any|nil, idx: integer|nil) +---@param on_choice fun(item: T|nil, idx: integer|nil) --- Called once the user made a choice. --- `idx` is the 1-based index of `item` within `items`. --- `nil` if the user aborted the dialog. -- cgit From 02749de7d5ffa9b0c809f93744d179d508c31206 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 23 Oct 2024 09:32:07 +0200 Subject: vim-patch:9.1.0809: filetype: petalinux config files not recognized Problem: filetype: petalinux config files not recognized Solution: detect 'project-spec/*.conf' files as bitbake filetype (Wu, Zhenyu) References: https://www.amd.com/en/products/software/adaptive-socs-and-fpgas/embedded-software/petalinux-sdk.html closes: vim/vim#15926 https://github.com/vim/vim/commit/626b6ab48682b211c22ede5a6e63513c85f93e58 Co-authored-by: Wu, Zhenyu --- runtime/lua/vim/filetype.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 2e7c95f85c..aa74a67968 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -2112,6 +2112,7 @@ local pattern = { ['/build/conf/.*%.conf$'] = 'bitbake', ['/meta%-.*/conf/.*%.conf$'] = 'bitbake', ['/meta/conf/.*%.conf$'] = 'bitbake', + ['/project%-spec/configs/.*%.conf$'] = 'bitbake', ['/%.cabal/config$'] = 'cabalconfig', ['/cabal/config$'] = 'cabalconfig', ['/%.aws/config$'] = 'confini', -- cgit From f6f2334ac28f41994ad6480b1a19e653c659662c Mon Sep 17 00:00:00 2001 From: Maria José Solano Date: Wed, 23 Oct 2024 04:40:12 -0700 Subject: fix(defaults): error messages UX for unimpaired mappings #30884 --- runtime/lua/vim/_defaults.lua | 167 +++++++++++++++--------------------------- 1 file changed, 60 insertions(+), 107 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua index 8e850f4cd3..0f4cd08da0 100644 --- a/runtime/lua/vim/_defaults.lua +++ b/runtime/lua/vim/_defaults.lua @@ -215,173 +215,126 @@ do --- vim-unimpaired style mappings. See: https://github.com/tpope/vim-unimpaired do + ---@param lhs string + ---@param rhs function + ---@param desc string + local function create_unimpaired_mapping(lhs, rhs, desc) + vim.keymap.set('n', lhs, function() + local _, err = pcall(rhs) ---@type any, string + if err then + vim.api.nvim_err_writeln(err) + end + end, { desc = desc }) + end + -- Quickfix mappings - vim.keymap.set('n', '[q', function() + create_unimpaired_mapping('[q', function() vim.cmd.cprevious({ count = vim.v.count1 }) - end, { - desc = ':cprevious', - }) - - vim.keymap.set('n', ']q', function() + end, ':cprevious') + create_unimpaired_mapping(']q', function() vim.cmd.cnext({ count = vim.v.count1 }) - end, { - desc = ':cnext', - }) - - vim.keymap.set('n', '[Q', function() - vim.cmd.crewind({ count = vim.v.count ~= 0 and vim.v.count or nil }) - end, { - desc = ':crewind', - }) - - vim.keymap.set('n', ']Q', function() - vim.cmd.clast({ count = vim.v.count ~= 0 and vim.v.count or nil }) - end, { - desc = ':clast', - }) - - vim.keymap.set('n', '[', function() + end, ':cnext') + create_unimpaired_mapping('[', function() vim.cmd.cpfile({ count = vim.v.count1 }) - end, { - desc = ':cpfile', - }) - - vim.keymap.set('n', ']', function() + end, ':cpfile') + create_unimpaired_mapping(']', function() vim.cmd.cnfile({ count = vim.v.count1 }) - end, { - desc = ':cnfile', - }) + end, ':cnfile') -- Location list mappings - vim.keymap.set('n', '[l', function() + create_unimpaired_mapping('[l', function() vim.cmd.lprevious({ count = vim.v.count1 }) - end, { - desc = ':lprevious', - }) - - vim.keymap.set('n', ']l', function() + end, ':lprevious') + create_unimpaired_mapping(']l', function() vim.cmd.lnext({ count = vim.v.count1 }) - end, { - desc = ':lnext', - }) - - vim.keymap.set('n', '[L', function() + end, ':lnext') + create_unimpaired_mapping('[L', function() vim.cmd.lrewind({ count = vim.v.count ~= 0 and vim.v.count or nil }) - end, { - desc = ':lrewind', - }) - - vim.keymap.set('n', ']L', function() + end, ':lrewind') + create_unimpaired_mapping(']L', function() vim.cmd.llast({ count = vim.v.count ~= 0 and vim.v.count or nil }) - end, { - desc = ':llast', - }) - - vim.keymap.set('n', '[', function() + end, ':llast') + create_unimpaired_mapping('[', function() vim.cmd.lpfile({ count = vim.v.count1 }) - end, { - desc = ':lpfile', - }) - - vim.keymap.set('n', ']', function() + end, ':lpfile') + create_unimpaired_mapping(']', function() vim.cmd.lnfile({ count = vim.v.count1 }) - end, { - desc = ':lnfile', - }) + end, ':lnfile') -- Argument list - vim.keymap.set('n', '[a', function() + create_unimpaired_mapping('[a', function() vim.cmd.previous({ count = vim.v.count1 }) - end, { - desc = ':previous', - }) - - vim.keymap.set('n', ']a', function() + end, ':previous') + create_unimpaired_mapping(']a', function() -- count doesn't work with :next, must use range. See #30641. vim.cmd.next({ range = { vim.v.count1 } }) - end, { - desc = ':next', - }) - - vim.keymap.set('n', '[A', function() + end, ':next') + create_unimpaired_mapping('[A', function() if vim.v.count ~= 0 then vim.cmd.argument({ count = vim.v.count }) else vim.cmd.rewind() end - end, { - desc = ':rewind', - }) - - vim.keymap.set('n', ']A', function() + end, ':rewind') + create_unimpaired_mapping(']A', function() if vim.v.count ~= 0 then vim.cmd.argument({ count = vim.v.count }) else vim.cmd.last() end - end, { - desc = ':last', - }) + end, ':last') -- Tags - vim.keymap.set('n', '[t', function() + create_unimpaired_mapping('[t', function() -- count doesn't work with :tprevious, must use range. See #30641. vim.cmd.tprevious({ range = { vim.v.count1 } }) - end, { desc = ':tprevious' }) - - vim.keymap.set('n', ']t', function() + end, ':tprevious') + create_unimpaired_mapping(']t', function() -- count doesn't work with :tnext, must use range. See #30641. vim.cmd.tnext({ range = { vim.v.count1 } }) - end, { desc = ':tnext' }) - - vim.keymap.set('n', '[T', function() + end, ':tnext') + create_unimpaired_mapping('[T', function() -- count doesn't work with :trewind, must use range. See #30641. vim.cmd.trewind({ range = vim.v.count ~= 0 and { vim.v.count } or nil }) - end, { desc = ':trewind' }) - - vim.keymap.set('n', ']T', function() + end, ':trewind') + create_unimpaired_mapping(']T', function() -- :tlast does not accept a count, so use :trewind if count given if vim.v.count ~= 0 then vim.cmd.trewind({ range = { vim.v.count } }) else vim.cmd.tlast() end - end, { desc = ':tlast' }) - - vim.keymap.set('n', '[', function() + end, ':tlast') + create_unimpaired_mapping('[', function() -- count doesn't work with :ptprevious, must use range. See #30641. vim.cmd.ptprevious({ range = { vim.v.count1 } }) - end, { desc = ' :ptprevious' }) - - vim.keymap.set('n', ']', function() + end, ' :ptprevious') + create_unimpaired_mapping(']', function() -- count doesn't work with :ptnext, must use range. See #30641. vim.cmd.ptnext({ range = { vim.v.count1 } }) - end, { desc = ':ptnext' }) + end, ':ptnext') -- Buffers - vim.keymap.set('n', '[b', function() + create_unimpaired_mapping('[b', function() vim.cmd.bprevious({ count = vim.v.count1 }) - end, { desc = ':bprevious' }) - - vim.keymap.set('n', ']b', function() + end, ':bprevious') + create_unimpaired_mapping(']b', function() vim.cmd.bnext({ count = vim.v.count1 }) - end, { desc = ':bnext' }) - - vim.keymap.set('n', '[B', function() + end, ':bnext') + create_unimpaired_mapping('[B', function() if vim.v.count ~= 0 then vim.cmd.buffer({ count = vim.v.count }) else vim.cmd.brewind() end - end, { desc = ':brewind' }) - - vim.keymap.set('n', ']B', function() + end, ':brewind') + create_unimpaired_mapping(']B', function() if vim.v.count ~= 0 then vim.cmd.buffer({ count = vim.v.count }) else vim.cmd.blast() end - end, { desc = ':blast' }) + end, ':blast') end end -- cgit From 3a86b60032bd659c2b12e984abb40cee93568558 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Sun, 29 Sep 2024 14:07:21 +0200 Subject: docs: misc Co-authored-by: David Pedersen Co-authored-by: Gregory Anders Co-authored-by: Leo Schlosser Co-authored-by: zeertzjq --- runtime/lua/vim/_meta/vimfn.lua | 2 +- runtime/lua/vim/lsp.lua | 6 +++--- runtime/lua/vim/provider/health.lua | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index bde3101479..a751783c8f 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -5192,7 +5192,7 @@ function vim.fn.log(expr) end function vim.fn.log10(expr) end --- {expr1} must be a |List|, |String|, |Blob| or |Dictionary|. ---- When {expr1} is a |List|| or |Dictionary|, replace each +--- When {expr1} is a |List| or |Dictionary|, replace each --- item in {expr1} with the result of evaluating {expr2}. --- For a |Blob| each byte is replaced. --- For a |String|, each character, including composing diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 8a0eef418d..76658b7012 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -235,9 +235,9 @@ end --- - `name` arbitrary name for the LSP client. Should be unique per language server. --- - `cmd` command string[] or function, described at |vim.lsp.start_client()|. --- - `root_dir` path to the project root. By default this is used to decide if an existing client ---- should be re-used. The example above uses |vim.fs.root()| and |vim.fs.dirname()| to detect ---- the root by traversing the file system upwards starting from the current directory until ---- either a `pyproject.toml` or `setup.py` file is found. +--- should be re-used. The example above uses |vim.fs.root()| to detect the root by traversing +--- the file system upwards starting from the current directory until either a `pyproject.toml` +--- or `setup.py` file is found. --- - `workspace_folders` list of `{ uri:string, name: string }` tables specifying the project root --- folders used by the language server. If `nil` the property is derived from `root_dir` for --- convenience. diff --git a/runtime/lua/vim/provider/health.lua b/runtime/lua/vim/provider/health.lua index 47c2080e3c..5ecb00f49b 100644 --- a/runtime/lua/vim/provider/health.lua +++ b/runtime/lua/vim/provider/health.lua @@ -449,7 +449,7 @@ end --- Get the latest Nvim Python client (pynvim) version from PyPI. local function latest_pypi_version() local pypi_version = 'unable to get pypi response' - local pypi_response = download('https://pypi.python.org/pypi/pynvim/json') + local pypi_response = download('https://pypi.org/pypi/pynvim/json') if pypi_response ~= '' then local pcall_ok, output = pcall(vim.fn.json_decode, pypi_response) if not pcall_ok then -- cgit From 230b0c7f021a57647a658edce27fe115343f083f Mon Sep 17 00:00:00 2001 From: Tristan Knight Date: Wed, 23 Oct 2024 14:33:57 +0100 Subject: feat(stdlib): overload vim.str_byteindex, vim.str_utfindex #30735 PROBLEM: There are several limitations to vim.str_byteindex, vim.str_utfindex: 1. They throw given out-of-range indexes. An invalid (often user/lsp-provided) index doesn't feel exceptional and should be handled by the caller. `:help dev-error-patterns` suggests that `retval, errmsg` is the preferred way to handle this kind of failure. 2. They cannot accept an encoding. So LSP needs wrapper functions. #25272 3. The current signatures are not extensible. * Calling: The function currently uses a fairly opaque boolean value to indicate to identify the encoding. * Returns: The fact it can throw requires wrapping in pcall. 4. The current name doesn't follow suggestions in `:h dev-naming` and I think `get` would be suitable. SOLUTION: - Because these are performance-sensitive, don't introduce `opts`. - Introduce an "overload" that accepts `encoding:string` and `strict_indexing:bool` params. ```lua local col = vim.str_utfindex(line, encoding, [index, [no_out_of_range]]) ``` Support the old versions by dispatching on the type of argument 2, and deprecate that form. ```lua vim.str_utfindex(line) -- (utf-32 length, utf-16 length), deprecated vim.str_utfindex(line, index) -- (utf-32 index, utf-16 index), deprecated vim.str_utfindex(line, 'utf-16') -- utf-16 length vim.str_utfindex(line, 'utf-16', index) -- utf-16 index vim.str_utfindex(line, 'utf-16', math.huge) -- error: index out of range vim.str_utfindex(line, 'utf-16', math.huge, false) -- utf-16 length ``` --- runtime/lua/vim/_editor.lua | 128 +++++++++++++++++++++++++++++++++++++- runtime/lua/vim/_meta/builtin.lua | 25 -------- 2 files changed, 127 insertions(+), 26 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 58283ac64b..496bbf747c 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -68,6 +68,12 @@ vim.log = { }, } +local utfs = { + ['utf-8'] = true, + ['utf-16'] = true, + ['utf-32'] = true, +} + -- TODO(lewis6991): document that the signature is system({cmd}, [{opts},] {on_exit}) --- Runs a system command or throws an error if {cmd} cannot be run. --- @@ -714,7 +720,127 @@ function vim._on_key(buf, typed_buf) end end ---- Generates a list of possible completions for the string. +--- Convert UTF-32, UTF-16 or UTF-8 {index} to byte index. +--- If {strict_indexing} is false +--- then then an out of range index will return byte length +--- instead of throwing an error. +--- +--- Invalid UTF-8 and NUL is treated like in |vim.str_utfindex()|. +--- An {index} in the middle of a UTF-16 sequence is rounded upwards to +--- the end of that sequence. +---@param s string +---@param encoding "utf-8"|"utf-16"|"utf-32" +---@param index integer +---@param strict_indexing? boolean # default: true +---@return integer +function vim.str_byteindex(s, encoding, index, strict_indexing) + if type(encoding) == 'number' then + -- Legacy support for old API + -- Parameters: ~ + -- • {str} (`string`) + -- • {index} (`integer`) + -- • {use_utf16} (`boolean?`) + local old_index = encoding + local use_utf16 = index or false + return vim.__str_byteindex(s, old_index, use_utf16) or error('index out of range') + end + + vim.validate('s', s, 'string') + vim.validate('index', index, 'number') + + local len = #s + + if index == 0 or len == 0 then + return 0 + end + + vim.validate('encoding', encoding, function(v) + return utfs[v], 'invalid encoding' + end) + + vim.validate('strict_indexing', strict_indexing, 'boolean', true) + if strict_indexing == nil then + strict_indexing = true + end + + if encoding == 'utf-8' then + if index > len then + return strict_indexing and error('index out of range') or len + end + return index + end + return vim.__str_byteindex(s, index, encoding == 'utf-16') + or strict_indexing and error('index out of range') + or len +end + +--- Convert byte index to UTF-32, UTF-16 or UTF-8 indices. If {index} is not +--- supplied, the length of the string is used. All indices are zero-based. +--- +--- If {strict_indexing} is false then an out of range index will return string +--- length instead of throwing an error. +--- Invalid UTF-8 bytes, and embedded surrogates are counted as one code point +--- each. An {index} in the middle of a UTF-8 sequence is rounded upwards to the end of +--- that sequence. +---@param s string +---@param encoding "utf-8"|"utf-16"|"utf-32" +---@param index? integer +---@param strict_indexing? boolean # default: true +---@return integer +function vim.str_utfindex(s, encoding, index, strict_indexing) + if encoding == nil or type(encoding) == 'number' then + -- Legacy support for old API + -- Parameters: ~ + -- • {str} (`string`) + -- • {index} (`integer?`) + local old_index = encoding + local col32, col16 = vim.__str_utfindex(s, old_index) --[[@as integer,integer]] + if not col32 or not col16 then + error('index out of range') + end + -- Return (multiple): ~ + -- (`integer`) UTF-32 index + -- (`integer`) UTF-16 index + return col32, col16 + end + + vim.validate('s', s, 'string') + vim.validate('index', index, 'number', true) + if not index then + index = math.huge + strict_indexing = false + end + + if index == 0 then + return 0 + end + + vim.validate('encoding', encoding, function(v) + return utfs[v], 'invalid encoding' + end) + + vim.validate('strict_indexing', strict_indexing, 'boolean', true) + if strict_indexing == nil then + strict_indexing = true + end + + if encoding == 'utf-8' then + local len = #s + return index <= len and index or (strict_indexing and error('index out of range') or len) + end + local col32, col16 = vim.__str_utfindex(s, index) --[[@as integer?,integer?]] + local col = encoding == 'utf-16' and col16 or col32 + if col then + return col + end + if strict_indexing then + error('index out of range') + end + local max32, max16 = vim.__str_utfindex(s)--[[@as integer integer]] + return encoding == 'utf-16' and max16 or max32 +end + +--- Generates a list of possible completions for the str --- String has the pattern. --- --- 1. Can we get it to just return things in the global namespace with that name prefix diff --git a/runtime/lua/vim/_meta/builtin.lua b/runtime/lua/vim/_meta/builtin.lua index 13bd1c1294..234c75d38f 100644 --- a/runtime/lua/vim/_meta/builtin.lua +++ b/runtime/lua/vim/_meta/builtin.lua @@ -112,18 +112,6 @@ function vim.rpcrequest(channel, method, ...) end --- equal, {a} is greater than {b} or {a} is lesser than {b}, respectively. function vim.stricmp(a, b) end ---- Convert UTF-32 or UTF-16 {index} to byte index. If {use_utf16} is not ---- supplied, it defaults to false (use UTF-32). Returns the byte index. ---- ---- Invalid UTF-8 and NUL is treated like in |vim.str_utfindex()|. ---- An {index} in the middle of a UTF-16 sequence is rounded upwards to ---- the end of that sequence. ---- @param str string ---- @param index integer ---- @param use_utf16? boolean ---- @return integer -function vim.str_byteindex(str, index, use_utf16) end - --- Gets a list of the starting byte positions of each UTF-8 codepoint in the given string. --- --- Embedded NUL bytes are treated as terminating the string. @@ -173,19 +161,6 @@ function vim.str_utf_start(str, index) end --- @return integer function vim.str_utf_end(str, index) end ---- Convert byte index to UTF-32 and UTF-16 indices. If {index} is not ---- supplied, the length of the string is used. All indices are zero-based. ---- ---- Embedded NUL bytes are treated as terminating the string. Invalid UTF-8 ---- bytes, and embedded surrogates are counted as one code point each. An ---- {index} in the middle of a UTF-8 sequence is rounded upwards to the end of ---- that sequence. ---- @param str string ---- @param index? integer ---- @return integer # UTF-32 index ---- @return integer # UTF-16 index -function vim.str_utfindex(str, index) end - --- The result is a String, which is the text {str} converted from --- encoding {from} to encoding {to}. When the conversion fails `nil` is --- returned. When some characters could not be converted they -- cgit From 008782208d826a03ab046bb2cb1bdda27c6554d0 Mon Sep 17 00:00:00 2001 From: Mathias Fussenegger Date: Tue, 22 Oct 2024 20:10:39 +0200 Subject: fix(lsp): handle mixed encoding in tagfunc params Relates to https://github.com/neovim/neovim/issues/30034 --- runtime/lua/vim/lsp/_tagfunc.lua | 44 +++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 16 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/_tagfunc.lua b/runtime/lua/vim/lsp/_tagfunc.lua index 70917de48d..f75d43f373 100644 --- a/runtime/lua/vim/lsp/_tagfunc.lua +++ b/runtime/lua/vim/lsp/_tagfunc.lua @@ -1,4 +1,5 @@ local lsp = vim.lsp +local api = vim.api local util = lsp.util local ms = lsp.protocol.Methods @@ -21,11 +22,12 @@ end ---@param pattern string ---@return table[] local function query_definition(pattern) - local params = util.make_position_params() - local results_by_client, err = lsp.buf_request_sync(0, ms.textDocument_definition, params, 1000) - if err then + local bufnr = api.nvim_get_current_buf() + local clients = vim.lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_definition }) + if not next(clients) then return {} end + local win = api.nvim_get_current_win() local results = {} --- @param range lsp.Range @@ -35,23 +37,33 @@ local function query_definition(pattern) table.insert(results, mk_tag_item(pattern, range, uri, offset_encoding)) end - for client_id, lsp_results in pairs(assert(results_by_client)) do - local client = lsp.get_client_by_id(client_id) - local offset_encoding = client and client.offset_encoding or 'utf-16' - local result = lsp_results.result or {} - if result.range then -- Location - add(result.range, result.uri, offset_encoding) - else - result = result --[[@as (lsp.Location[]|lsp.LocationLink[])]] - for _, item in pairs(result) do - if item.range then -- Location - add(item.range, item.uri, offset_encoding) - else -- LocationLink - add(item.targetSelectionRange, item.targetUri, offset_encoding) + local remaining = #clients + for _, client in ipairs(clients) do + ---@param result nil|lsp.Location|lsp.Location[]|lsp.LocationLink[] + local function on_response(_, result) + if result then + local encoding = client.offset_encoding + -- single Location + if result.range then + add(result.range, result.uri, encoding) + else + for _, location in ipairs(result) do + if location.range then -- Location + add(location.range, location.uri, encoding) + else -- LocationLink + add(location.targetSelectionRange, location.targetUri, encoding) + end + end end end + remaining = remaining - 1 end + local params = util.make_position_params(win, client.offset_encoding) + client.request(ms.textDocument_definition, params, on_response, bufnr) end + vim.wait(1000, function() + return remaining == 0 + end) return results end -- cgit From ad4e14c201c2f400e721362d8581f524fdad9038 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 24 Oct 2024 09:55:23 +0100 Subject: refactor(lsp.buf): use alias for vim.lsp --- runtime/lua/vim/lsp/buf.lua | 53 +++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 26 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index fc41246588..0bb7904ca1 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -1,4 +1,5 @@ local api = vim.api +local lsp = vim.lsp local validate = vim.validate local util = require('vim.lsp.util') local npcall = vim.F.npcall @@ -23,7 +24,7 @@ local M = {} local function request(method, params, handler) validate('method', method, 'string') validate('handler', handler, 'function', true) - return vim.lsp.buf_request(0, method, params, handler) + return lsp.buf_request(0, method, params, handler) end --- Displays hover information about the symbol under the cursor in a floating @@ -35,15 +36,15 @@ end --- You can scroll the contents the same as you would any other buffer. function M.hover() local params = util.make_position_params() - request(ms.textDocument_hover, params) + lsp.buf_request(0, ms.textDocument_hover, params) end local function request_with_opts(name, params, opts) local req_handler --- @type function? if opts then req_handler = function(err, result, ctx, config) - local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) - local handler = client.handlers[name] or vim.lsp.handlers[name] + local client = assert(lsp.get_client_by_id(ctx.client_id)) + local handler = client.handlers[name] or lsp.handlers[name] handler(err, result, ctx, vim.tbl_extend('force', config or {}, opts)) end end @@ -55,9 +56,9 @@ end local function get_locations(method, opts) opts = opts or {} local bufnr = api.nvim_get_current_buf() - local clients = vim.lsp.get_clients({ method = method, bufnr = bufnr }) + local clients = lsp.get_clients({ method = method, bufnr = bufnr }) if not next(clients) then - vim.notify(vim.lsp._unsupported_method(method), vim.log.levels.WARN) + vim.notify(lsp._unsupported_method(method), vim.log.levels.WARN) return end local win = api.nvim_get_current_win() @@ -307,7 +308,7 @@ function M.format(opts) method = ms.textDocument_formatting end - local clients = vim.lsp.get_clients({ + local clients = lsp.get_clients({ id = opts.id, bufnr = bufnr, name = opts.name, @@ -344,7 +345,7 @@ function M.format(opts) end local params = set_range(client, util.make_formatting_params(opts.formatting_options)) client.request(method, params, function(...) - local handler = client.handlers[method] or vim.lsp.handlers[method] + local handler = client.handlers[method] or lsp.handlers[method] handler(...) do_format(next(clients, idx)) end, bufnr) @@ -386,7 +387,7 @@ end function M.rename(new_name, opts) opts = opts or {} local bufnr = opts.bufnr or api.nvim_get_current_buf() - local clients = vim.lsp.get_clients({ + local clients = lsp.get_clients({ bufnr = bufnr, name = opts.name, -- Clients must at least support rename, prepareRename is optional @@ -428,7 +429,7 @@ function M.rename(new_name, opts) local params = util.make_position_params(win, client.offset_encoding) params.newName = name local handler = client.handlers[ms.textDocument_rename] - or vim.lsp.handlers[ms.textDocument_rename] + or lsp.handlers[ms.textDocument_rename] client.request(ms.textDocument_rename, params, function(...) handler(...) try_use_client(next(clients, idx)) @@ -508,7 +509,7 @@ end function M.references(context, opts) validate('context', context, 'table', true) local bufnr = api.nvim_get_current_buf() - local clients = vim.lsp.get_clients({ method = ms.textDocument_references, bufnr = bufnr }) + local clients = lsp.get_clients({ method = ms.textDocument_references, bufnr = bufnr }) if not next(clients) then return end @@ -575,7 +576,7 @@ end --- @param handler? lsp.Handler --- @param bufnr? integer local function request_with_id(client_id, method, params, handler, bufnr) - local client = vim.lsp.get_client_by_id(client_id) + local client = lsp.get_client_by_id(client_id) if not client then vim.notify( string.format('Client with id=%d disappeared during call hierarchy request', client_id), @@ -654,9 +655,9 @@ end function M.typehierarchy(kind) local method = kind == 'subtypes' and ms.typeHierarchy_subtypes or ms.typeHierarchy_supertypes local bufnr = api.nvim_get_current_buf() - local clients = vim.lsp.get_clients({ bufnr = bufnr, method = method }) + local clients = lsp.get_clients({ bufnr = bufnr, method = method }) if not next(clients) then - vim.notify(vim.lsp._unsupported_method(method), vim.log.levels.WARN) + vim.notify(lsp._unsupported_method(method), vim.log.levels.WARN) return end @@ -713,7 +714,7 @@ end --- function M.list_workspace_folders() local workspace_folders = {} - for _, client in pairs(vim.lsp.get_clients({ bufnr = 0 })) do + for _, client in pairs(lsp.get_clients({ bufnr = 0 })) do for _, folder in pairs(client.workspace_folders or {}) do table.insert(workspace_folders, folder.name) end @@ -736,7 +737,7 @@ function M.add_workspace_folder(workspace_folder) return end local bufnr = api.nvim_get_current_buf() - for _, client in pairs(vim.lsp.get_clients({ bufnr = bufnr })) do + for _, client in pairs(lsp.get_clients({ bufnr = bufnr })) do client:_add_workspace_folder(workspace_folder) end end @@ -753,7 +754,7 @@ function M.remove_workspace_folder(workspace_folder) return end local bufnr = api.nvim_get_current_buf() - for _, client in pairs(vim.lsp.get_clients({ bufnr = bufnr })) do + for _, client in pairs(lsp.get_clients({ bufnr = bufnr })) do client:_remove_workspace_folder(workspace_folder) end print(workspace_folder, 'is not currently part of the workspace') @@ -916,7 +917,7 @@ local function on_code_action_results(results, opts) -- command: string -- arguments?: any[] -- - local client = assert(vim.lsp.get_client_by_id(choice.ctx.client_id)) + local client = assert(lsp.get_client_by_id(choice.ctx.client_id)) local action = choice.action local bufnr = assert(choice.ctx.bufnr, 'Must have buffer number') @@ -951,14 +952,14 @@ local function on_code_action_results(results, opts) ---@param item {action: lsp.Command|lsp.CodeAction, ctx: lsp.HandlerContext} local function format_item(item) - local clients = vim.lsp.get_clients({ bufnr = item.ctx.bufnr }) + local clients = lsp.get_clients({ bufnr = item.ctx.bufnr }) local title = item.action.title:gsub('\r\n', '\\r\\n'):gsub('\n', '\\n') if #clients == 1 then return title end - local source = vim.lsp.get_client_by_id(item.ctx.client_id).name + local source = lsp.get_client_by_id(item.ctx.client_id).name return ('%s [%s]'):format(title, source) end @@ -987,16 +988,16 @@ function M.code_action(opts) end local context = opts.context and vim.deepcopy(opts.context) or {} if not context.triggerKind then - context.triggerKind = vim.lsp.protocol.CodeActionTriggerKind.Invoked + context.triggerKind = lsp.protocol.CodeActionTriggerKind.Invoked end local mode = api.nvim_get_mode().mode local bufnr = api.nvim_get_current_buf() local win = api.nvim_get_current_win() - local clients = vim.lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_codeAction }) + local clients = lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_codeAction }) local remaining = #clients if remaining == 0 then - if next(vim.lsp.get_clients({ bufnr = bufnr })) then - vim.notify(vim.lsp._unsupported_method(ms.textDocument_codeAction), vim.log.levels.WARN) + if next(lsp.get_clients({ bufnr = bufnr })) then + vim.notify(lsp._unsupported_method(ms.textDocument_codeAction), vim.log.levels.WARN) end return end @@ -1033,8 +1034,8 @@ function M.code_action(opts) if context.diagnostics then params.context = context else - local ns_push = vim.lsp.diagnostic.get_namespace(client.id, false) - local ns_pull = vim.lsp.diagnostic.get_namespace(client.id, true) + local ns_push = lsp.diagnostic.get_namespace(client.id, false) + local ns_pull = lsp.diagnostic.get_namespace(client.id, true) local diagnostics = {} local lnum = api.nvim_win_get_cursor(0)[1] - 1 vim.list_extend(diagnostics, vim.diagnostic.get(bufnr, { namespace = ns_pull, lnum = lnum })) -- cgit From 2ee39b7eb46f091bf22dd1ba3066afff51139bdd Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 24 Oct 2024 09:58:22 +0100 Subject: refactor(lsp.buf): remove buf_request wrapper --- runtime/lua/vim/lsp/buf.lua | 32 ++++++-------------------------- 1 file changed, 6 insertions(+), 26 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 0bb7904ca1..4a48172ab1 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -7,26 +7,6 @@ local ms = require('vim.lsp.protocol').Methods local M = {} ---- Sends an async request to all active clients attached to the current ---- buffer. ---- ----@param method (string) LSP method name ----@param params (table|nil) Parameters to send to the server ----@param handler lsp.Handler? See |lsp-handler|. Follows |lsp-handler-resolution| ---- ----@return table client_request_ids Map of client-id:request-id pairs ----for all successful requests. ----@return function _cancel_all_requests Function which can be used to ----cancel all the requests. You could instead ----iterate all clients and call their `cancel_request()` methods. ---- ----@see |vim.lsp.buf_request()| -local function request(method, params, handler) - validate('method', method, 'string') - validate('handler', handler, 'function', true) - return lsp.buf_request(0, method, params, handler) -end - --- Displays hover information about the symbol under the cursor in a floating --- window. The window will be dismissed on cursor move. --- Calling the function twice will jump into the floating window @@ -48,7 +28,7 @@ local function request_with_opts(name, params, opts) handler(err, result, ctx, vim.tbl_extend('force', config or {}, opts)) end end - request(name, params, req_handler) + lsp.buf_request(0, name, params, req_handler) end ---@param method string @@ -187,7 +167,7 @@ end --- floating window. function M.signature_help() local params = util.make_position_params() - request(ms.textDocument_signatureHelp, params) + lsp.buf_request(0, ms.textDocument_signatureHelp, params) end --- Retrieves the completion items at the current cursor position. Can only be @@ -201,7 +181,7 @@ end function M.completion(context) local params = util.make_position_params() params.context = context - return request(ms.textDocument_completion, params) + return lsp.buf_request(0, ms.textDocument_completion, params) end ---@param bufnr integer @@ -609,7 +589,7 @@ end local function call_hierarchy(method) local params = util.make_position_params() --- @param result lsp.CallHierarchyItem[]? - request(ms.textDocument_prepareCallHierarchy, params, function(err, result, ctx) + lsp.buf_request(0, ms.textDocument_prepareCallHierarchy, params, function(err, result, ctx) if err then vim.notify(err.message, vim.log.levels.WARN) return @@ -794,7 +774,7 @@ end --- |hl-LspReferenceWrite| function M.document_highlight() local params = util.make_position_params() - request(ms.textDocument_documentHighlight, params) + lsp.buf_request(0, ms.textDocument_documentHighlight, params) end --- Removes document highlights from current buffer. @@ -1063,7 +1043,7 @@ function M.execute_command(command_params) arguments = command_params.arguments, workDoneToken = command_params.workDoneToken, } - request(ms.workspace_executeCommand, command_params) + lsp.buf_request(0, ms.workspace_executeCommand, command_params) end return M -- cgit From 3275ae830da97ef907ff3cfb0170706b6a430f57 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 24 Oct 2024 10:31:34 +0100 Subject: fix(lsp.protocal): improve typing of constants --- runtime/lua/vim/lsp/protocol.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 1699fff0c1..b299a8438f 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -12,6 +12,8 @@ end local sysname = vim.uv.os_uname().sysname +--- @class vim.lsp.protocol.constants +--- @nodoc local constants = { --- @enum lsp.DiagnosticSeverity DiagnosticSeverity = { @@ -314,7 +316,9 @@ local constants = { }, } --- Protocol for the Microsoft Language Server Protocol (mslsp) +--- Protocol for the Microsoft Language Server Protocol (mslsp) +--- @class vim.lsp.protocol : vim.lsp.protocol.constants +--- @nodoc local protocol = {} --- @diagnostic disable:no-unknown -- cgit From 2dcbfe78fcec5f73ce061bb24b718187b9c6b134 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 24 Oct 2024 10:18:36 +0100 Subject: fix(lsp.buf): use correct offset_encoding for all requests Problem: `lsp.buf_request` send the same params to all servers and many calls to this pass PositionalParams which depends on the clients offset_encoding. This can result with incorrect params being sent to a server. Solution: `lsp.buf_request` `params` argument can now be passed as a function which takes the client as the first argument. This is used in lsp/buf.lua to construct correct params for each client request. --- runtime/lua/vim/lsp.lua | 5 ++-- runtime/lua/vim/lsp/buf.lua | 68 ++++++++++++++++++++++++++++----------------- 2 files changed, 46 insertions(+), 27 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 76658b7012..4f13ad5721 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -854,7 +854,7 @@ api.nvim_create_autocmd('VimLeavePre', { --- ---@param bufnr (integer) Buffer handle, or 0 for current. ---@param method (string) LSP method name ----@param params table|nil Parameters to send to the server +---@param params? table|(fun(client: vim.lsp.Client, bufnr: integer): table?) Parameters to send to the server ---@param handler? lsp.Handler See |lsp-handler| --- If nil, follows resolution strategy defined in |lsp-handler-configuration| ---@param on_unsupported? fun() @@ -879,7 +879,8 @@ function lsp.buf_request(bufnr, method, params, handler, on_unsupported) if client.supports_method(method, { bufnr = bufnr }) then method_supported = true - local request_success, request_id = client.request(method, params, handler, bufnr) + local cparams = type(params) == 'function' and params(client, bufnr) or params --[[@as table?]] + local request_success, request_id = client.request(method, cparams, handler, bufnr) -- This could only fail if the client shut down in the time since we looked -- it up and we did the request, which should be rare. if request_success then diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 4a48172ab1..80ba3c648f 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -7,6 +7,19 @@ local ms = require('vim.lsp.protocol').Methods local M = {} +--- @param params? table +--- @return fun(client: vim.lsp.Client): lsp.TextDocumentPositionParams +local function client_positional_params(params) + local win = api.nvim_get_current_win() + return function(client) + local ret = util.make_position_params(win, client.offset_encoding) + if params then + ret = vim.tbl_extend('force', ret, params) + end + return ret + end +end + --- Displays hover information about the symbol under the cursor in a floating --- window. The window will be dismissed on cursor move. --- Calling the function twice will jump into the floating window @@ -15,8 +28,7 @@ local M = {} --- except that "q" dismisses the window. --- You can scroll the contents the same as you would any other buffer. function M.hover() - local params = util.make_position_params() - lsp.buf_request(0, ms.textDocument_hover, params) + lsp.buf_request(0, ms.textDocument_hover, client_positional_params()) end local function request_with_opts(name, params, opts) @@ -166,8 +178,7 @@ end --- Displays signature information about the symbol under the cursor in a --- floating window. function M.signature_help() - local params = util.make_position_params() - lsp.buf_request(0, ms.textDocument_signatureHelp, params) + lsp.buf_request(0, ms.textDocument_signatureHelp, client_positional_params()) end --- Retrieves the completion items at the current cursor position. Can only be @@ -179,9 +190,13 @@ end --- ---@see vim.lsp.protocol.CompletionTriggerKind function M.completion(context) - local params = util.make_position_params() - params.context = context - return lsp.buf_request(0, ms.textDocument_completion, params) + return lsp.buf_request( + 0, + ms.textDocument_completion, + client_positional_params({ + context = context, + }) + ) end ---@param bufnr integer @@ -587,23 +602,27 @@ end --- @param method string local function call_hierarchy(method) - local params = util.make_position_params() - --- @param result lsp.CallHierarchyItem[]? - lsp.buf_request(0, ms.textDocument_prepareCallHierarchy, params, function(err, result, ctx) - if err then - vim.notify(err.message, vim.log.levels.WARN) - return - end - if not result or vim.tbl_isempty(result) then - vim.notify('No item resolved', vim.log.levels.WARN) - return - end - local item = pick_call_hierarchy_item(result) - if not item then - return + lsp.buf_request( + 0, + ms.textDocument_prepareCallHierarchy, + client_positional_params(), + --- @param result lsp.CallHierarchyItem[]? + function(err, result, ctx) + if err then + vim.notify(err.message, vim.log.levels.WARN) + return + end + if not result or vim.tbl_isempty(result) then + vim.notify('No item resolved', vim.log.levels.WARN) + return + end + local item = pick_call_hierarchy_item(result) + if not item then + return + end + request_with_id(ctx.client_id, method, { item = item }, nil, ctx.bufnr) end - request_with_id(ctx.client_id, method, { item = item }, nil, ctx.bufnr) - end) + ) end --- Lists all the call sites of the symbol under the cursor in the @@ -773,8 +792,7 @@ end --- |hl-LspReferenceRead| --- |hl-LspReferenceWrite| function M.document_highlight() - local params = util.make_position_params() - lsp.buf_request(0, ms.textDocument_documentHighlight, params) + lsp.buf_request(0, ms.textDocument_documentHighlight, client_positional_params()) end --- Removes document highlights from current buffer. -- cgit From 3c51058d7611ef235a23ae77f994403480237700 Mon Sep 17 00:00:00 2001 From: Mathias Fussenegger Date: Thu, 24 Oct 2024 10:22:38 +0200 Subject: fix(lsp): set tagstack on jump via goto methods Follow up to https://github.com/neovim/neovim/pull/30877 Fixes https://github.com/neovim/neovim/issues/30926 --- runtime/lua/vim/lsp/buf.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 80ba3c648f..1858095918 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -54,6 +54,9 @@ local function get_locations(method, opts) return end local win = api.nvim_get_current_win() + local from = vim.fn.getpos('.') + from[1] = bufnr + local tagname = vim.fn.expand('') local remaining = #clients ---@type vim.quickfix.entry[] @@ -89,6 +92,13 @@ local function get_locations(method, opts) if #all_items == 1 then local item = all_items[1] local b = item.bufnr or vim.fn.bufadd(item.filename) + + -- Save position in jumplist + vim.cmd("normal! m'") + -- Push a new item into tagstack + local tagstack = { { tagname = tagname, from = from } } + vim.fn.settagstack(vim.fn.win_getid(win), { items = tagstack }, 't') + vim.bo[b].buflisted = true local w = opts.reuse_win and vim.fn.win_findbuf(b)[1] or win api.nvim_win_set_buf(w, b) -- cgit From c8d7d65679f832b406eb551411721cf21bae52bb Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 24 Oct 2024 11:34:17 +0100 Subject: fix(lsp): use correct method for prepareTypehierarchy Regression from #30902 --- runtime/lua/vim/lsp/buf.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 1858095918..e0c03edf1e 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -663,8 +663,9 @@ end ---@param kind "subtypes"|"supertypes" function M.typehierarchy(kind) local method = kind == 'subtypes' and ms.typeHierarchy_subtypes or ms.typeHierarchy_supertypes + local pmethod = ms.textDocument_prepareTypeHierarchy local bufnr = api.nvim_get_current_buf() - local clients = lsp.get_clients({ bufnr = bufnr, method = method }) + local clients = lsp.get_clients({ bufnr = bufnr, method = pmethod }) if not next(clients) then vim.notify(lsp._unsupported_method(method), vim.log.levels.WARN) return @@ -701,7 +702,7 @@ function M.typehierarchy(kind) for _, client in ipairs(clients) do local params = util.make_position_params(win, client.offset_encoding) - client.request(method, params, function(err, result, ctx) + client.request(pmethod, params, function(err, result, ctx) --- @cast result lsp.TypeHierarchyItem[]? if err then vim.notify(err.message, vim.log.levels.WARN) -- cgit From 39d79efa1e1e1e5c3476dee54cc2bc4abc725a8f Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 24 Oct 2024 11:20:27 +0100 Subject: fix(lsp): better multi-client support for callHierarchy Only ever display a dialogue box once. Switch from vim.fn.inpulist to vim.ui.select refactor(lsp): merge call and type hierarchy functions --- runtime/lua/vim/lsp/buf.lua | 130 ++++++++++++++++++-------------------------- 1 file changed, 53 insertions(+), 77 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index e0c03edf1e..f2d5d204f4 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -584,7 +584,7 @@ local function request_with_id(client_id, method, params, handler, bufnr) local client = lsp.get_client_by_id(client_id) if not client then vim.notify( - string.format('Client with id=%d disappeared during call hierarchy request', client_id), + string.format('Client with id=%d disappeared during hierarchy request', client_id), vim.log.levels.WARN ) return @@ -592,80 +592,33 @@ local function request_with_id(client_id, method, params, handler, bufnr) client.request(method, params, handler, bufnr) end ---- @param call_hierarchy_items lsp.CallHierarchyItem[] ---- @return lsp.CallHierarchyItem? -local function pick_call_hierarchy_item(call_hierarchy_items) - if #call_hierarchy_items == 1 then - return call_hierarchy_items[1] - end - local items = {} - for i, item in pairs(call_hierarchy_items) do - local entry = item.detail or item.name - table.insert(items, string.format('%d. %s', i, entry)) - end - local choice = vim.fn.inputlist(items) - if choice < 1 or choice > #items then - return - end - return call_hierarchy_items[choice] -end - ---- @param method string -local function call_hierarchy(method) - lsp.buf_request( - 0, - ms.textDocument_prepareCallHierarchy, - client_positional_params(), - --- @param result lsp.CallHierarchyItem[]? - function(err, result, ctx) - if err then - vim.notify(err.message, vim.log.levels.WARN) - return - end - if not result or vim.tbl_isempty(result) then - vim.notify('No item resolved', vim.log.levels.WARN) - return - end - local item = pick_call_hierarchy_item(result) - if not item then - return - end - request_with_id(ctx.client_id, method, { item = item }, nil, ctx.bufnr) - end - ) -end - ---- Lists all the call sites of the symbol under the cursor in the ---- |quickfix| window. If the symbol can resolve to multiple ---- items, the user can pick one in the |inputlist()|. -function M.incoming_calls() - call_hierarchy(ms.callHierarchy_incomingCalls) -end - ---- Lists all the items that are called by the symbol under the ---- cursor in the |quickfix| window. If the symbol can resolve to ---- multiple items, the user can pick one in the |inputlist()|. -function M.outgoing_calls() - call_hierarchy(ms.callHierarchy_outgoingCalls) -end - ---- @param item lsp.TypeHierarchyItem -local function format_type_hierarchy_item(item) +--- @param item lsp.TypeHierarchyItem|lsp.CallHierarchyItem +local function format_hierarchy_item(item) if not item.detail or #item.detail == 0 then return item.name end return string.format('%s %s', item.name, item.detail) end ---- Lists all the subtypes or supertypes of the symbol under the ---- cursor in the |quickfix| window. If the symbol can resolve to ---- multiple items, the user can pick one using |vim.ui.select()|. ----@param kind "subtypes"|"supertypes" -function M.typehierarchy(kind) - local method = kind == 'subtypes' and ms.typeHierarchy_subtypes or ms.typeHierarchy_supertypes - local pmethod = ms.textDocument_prepareTypeHierarchy +local hierarchy_methods = { + [ms.typeHierarchy_subtypes] = 'type', + [ms.typeHierarchy_supertypes] = 'type', + [ms.callHierarchy_incomingCalls] = 'call', + [ms.callHierarchy_outgoingCalls] = 'call', +} + +--- @param method string +local function hierarchy(method) + local kind = hierarchy_methods[method] + if not kind then + error('unsupported method ' .. method) + end + + local prepare_method = kind == 'type' and ms.textDocument_prepareTypeHierarchy + or ms.textDocument_prepareCallHierarchy + local bufnr = api.nvim_get_current_buf() - local clients = lsp.get_clients({ bufnr = bufnr, method = pmethod }) + local clients = lsp.get_clients({ bufnr = bufnr, method = prepare_method }) if not next(clients) then vim.notify(lsp._unsupported_method(method), vim.log.levels.WARN) return @@ -673,19 +626,19 @@ function M.typehierarchy(kind) local win = api.nvim_get_current_win() - --- @param results [integer, lsp.TypeHierarchyItem][] + --- @param results [integer, lsp.TypeHierarchyItem|lsp.CallHierarchyItem][] local function on_response(results) if #results == 0 then - vim.notify('No items resolved', vim.log.levels.INFO) + vim.notify('No item resolved', vim.log.levels.WARN) elseif #results == 1 then local client_id, item = results[1][1], results[1][2] request_with_id(client_id, method, { item = item }, nil, bufnr) else vim.ui.select(results, { - prompt = 'Select a type hierarchy item:', - kind = 'typehierarchy', + prompt = string.format('Select a %s hierarchy item:', kind), + kind = kind .. 'hierarchy', format_item = function(x) - return format_type_hierarchy_item(x[2]) + return format_hierarchy_item(x[2]) end, }, function(x) if x then @@ -696,18 +649,18 @@ function M.typehierarchy(kind) end end - local results = {} --- @type [integer, lsp.TypeHierarchyItem][] + local results = {} --- @type [integer, lsp.TypeHierarchyItem|lsp.CallHierarchyItem][] local remaining = #clients for _, client in ipairs(clients) do local params = util.make_position_params(win, client.offset_encoding) - client.request(pmethod, params, function(err, result, ctx) - --- @cast result lsp.TypeHierarchyItem[]? + --- @param result lsp.CallHierarchyItem[]|lsp.TypeHierarchyItem[]? + client.request(prepare_method, params, function(err, result, ctx) if err then vim.notify(err.message, vim.log.levels.WARN) elseif result then - for _, item in pairs(result) do + for _, item in ipairs(result) do results[#results + 1] = { ctx.client_id, item } end end @@ -720,6 +673,29 @@ function M.typehierarchy(kind) end end +--- Lists all the call sites of the symbol under the cursor in the +--- |quickfix| window. If the symbol can resolve to multiple +--- items, the user can pick one in the |inputlist()|. +function M.incoming_calls() + hierarchy(ms.callHierarchy_incomingCalls) +end + +--- Lists all the items that are called by the symbol under the +--- cursor in the |quickfix| window. If the symbol can resolve to +--- multiple items, the user can pick one in the |inputlist()|. +function M.outgoing_calls() + hierarchy(ms.callHierarchy_outgoingCalls) +end + +--- Lists all the subtypes or supertypes of the symbol under the +--- cursor in the |quickfix| window. If the symbol can resolve to +--- multiple items, the user can pick one using |vim.ui.select()|. +---@param kind "subtypes"|"supertypes" +function M.typehierarchy(kind) + local method = kind == 'subtypes' and ms.typeHierarchy_subtypes or ms.typeHierarchy_supertypes + hierarchy(method) +end + --- List workspace folders. --- function M.list_workspace_folders() -- cgit From 7a7747f1e4d96aab53ff9c52d0c3492308c22c58 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 24 Oct 2024 12:11:27 +0100 Subject: feat(lsp): deprecate execute_command with client:exec_cmd --- runtime/lua/vim/lsp/buf.lua | 5 ++++- runtime/lua/vim/lsp/client.lua | 29 +++++++++++++---------------- runtime/lua/vim/lsp/codelens.lua | 2 +- runtime/lua/vim/lsp/completion.lua | 10 +--------- 4 files changed, 19 insertions(+), 27 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index f2d5d204f4..4cd4008105 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -881,7 +881,8 @@ local function on_code_action_results(results, opts) local a_cmd = action.command if a_cmd then local command = type(a_cmd) == 'table' and a_cmd or action - client:_exec_cmd(command, ctx) + --- @cast command lsp.Command + client:exec_cmd(command, ctx) end end @@ -1037,12 +1038,14 @@ function M.code_action(opts) end end +--- @deprecated --- Executes an LSP server command. --- @param command_params lsp.ExecuteCommandParams --- @see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand function M.execute_command(command_params) validate('command', command_params.command, 'string') validate('arguments', command_params.arguments, 'table', true) + vim.deprecate('execute_command', 'client:exec_cmd', '0.12') command_params = { command = command_params.command, arguments = command_params.arguments, diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index d9d6b851d0..2718f40c96 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -859,10 +859,9 @@ end --- or via workspace/executeCommand (if supported by the server) --- --- @param command lsp.Command ---- @param context? {bufnr: integer} +--- @param context? {bufnr?: integer} --- @param handler? lsp.Handler only called if a server command ---- @param on_unsupported? function handler invoked when the command is not supported by the client. -function Client:_exec_cmd(command, context, handler, on_unsupported) +function Client:exec_cmd(command, context, handler) context = vim.deepcopy(context or {}, true) --[[@as lsp.HandlerContext]] context.bufnr = context.bufnr or api.nvim_get_current_buf() context.client_id = self.id @@ -875,25 +874,23 @@ function Client:_exec_cmd(command, context, handler, on_unsupported) local command_provider = self.server_capabilities.executeCommandProvider local commands = type(command_provider) == 'table' and command_provider.commands or {} + if not vim.list_contains(commands, cmdname) then - if on_unsupported then - on_unsupported() - else - vim.notify_once( - string.format( - 'Language server `%s` does not support command `%s`. This command may require a client extension.', - self.name, - cmdname - ), - vim.log.levels.WARN - ) - end + vim.notify_once( + string.format( + 'Language server `%s` does not support command `%s`. This command may require a client extension.', + self.name, + cmdname + ), + vim.log.levels.WARN + ) return end -- Not using command directly to exclude extra properties, -- see https://github.com/python-lsp/python-lsp-server/issues/146 + --- @type lsp.ExecuteCommandParams local params = { - command = command.command, + command = cmdname, arguments = command.arguments, } self.request(ms.workspace_executeCommand, params, handler, context.bufnr) diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index c1b6bfb28c..d8c6c27a98 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -48,7 +48,7 @@ local function execute_lens(lens, bufnr, client_id) local client = vim.lsp.get_client_by_id(client_id) assert(client, 'Client is required to execute lens, client_id=' .. client_id) - client:_exec_cmd(lens.command, { bufnr = bufnr }, function(...) + client:exec_cmd(lens.command, { bufnr = bufnr }, function(...) vim.lsp.handlers[ms.workspace_executeCommand](...) M.refresh() end) diff --git a/runtime/lua/vim/lsp/completion.lua b/runtime/lua/vim/lsp/completion.lua index 3df506d23a..e36d329dc5 100644 --- a/runtime/lua/vim/lsp/completion.lua +++ b/runtime/lua/vim/lsp/completion.lua @@ -548,15 +548,7 @@ local function on_complete_done() local command = completion_item.command if command then - client:_exec_cmd(command, { bufnr = bufnr }, nil, function() - vim.lsp.log.warn( - string.format( - 'Language server `%s` does not support command `%s`. This command may require a client extension.', - client.name, - command.command - ) - ) - end) + client:exec_cmd(command, { bufnr = bufnr }) end end -- cgit From 54249d051c3e0b97321939e6a3ae3d2e83971ce7 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 24 Oct 2024 15:43:38 +0100 Subject: feat(lsp): deprecate vim.lsp.buf.completion Use `vim.lsp.completion.trigger()` instead' --- runtime/lua/vim/lsp/buf.lua | 2 ++ runtime/lua/vim/lsp/handlers.lua | 1 + 2 files changed, 3 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 4cd4008105..5f50e361c4 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -191,6 +191,7 @@ function M.signature_help() lsp.buf_request(0, ms.textDocument_signatureHelp, client_positional_params()) end +--- @deprecated --- Retrieves the completion items at the current cursor position. Can only be --- called in Insert mode. --- @@ -200,6 +201,7 @@ end --- ---@see vim.lsp.protocol.CompletionTriggerKind function M.completion(context) + vim.depends('vim.lsp.buf.completion', 'vim.lsp.commpletion.trigger', '0.12') return lsp.buf_request( 0, ms.textDocument_completion, diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 37cb07025c..a905f76fda 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -317,6 +317,7 @@ M[ms.textDocument_formatting] = function(_, result, ctx, _) util.apply_text_edits(result, ctx.bufnr, client.offset_encoding) end +--- @deprecated --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion M[ms.textDocument_completion] = function(_, result, _, _) if vim.tbl_isempty(result or {}) then -- cgit From 01739d4673eecad0631f874ba279c5c362aa1766 Mon Sep 17 00:00:00 2001 From: Maria José Solano Date: Fri, 25 Oct 2024 06:45:27 -0700 Subject: fix(defaults): missing ]Q/[Q unimpaired mappings #30943 --- runtime/lua/vim/_defaults.lua | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua index 0f4cd08da0..1b1114b764 100644 --- a/runtime/lua/vim/_defaults.lua +++ b/runtime/lua/vim/_defaults.lua @@ -234,6 +234,12 @@ do create_unimpaired_mapping(']q', function() vim.cmd.cnext({ count = vim.v.count1 }) end, ':cnext') + create_unimpaired_mapping('[Q', function() + vim.cmd.crewind({ count = vim.v.count ~= 0 and vim.v.count or nil }) + end, ':crewind') + create_unimpaired_mapping(']Q', function() + vim.cmd.clast({ count = vim.v.count ~= 0 and vim.v.count or nil }) + end, ':clast') create_unimpaired_mapping('[', function() vim.cmd.cpfile({ count = vim.v.count1 }) end, ':cpfile') -- cgit From 25b53b593ef6f229fbec5b3dc205a7539579d13a Mon Sep 17 00:00:00 2001 From: Tristan Knight Date: Sat, 26 Oct 2024 15:38:25 +0100 Subject: refactor(lsp): drop str_byteindex/str_utfindex wrappers #30915 * deprecate old signatures * move to new str_byteindex/str_utfindex signature * use single-underscore name (double-underscore is reserved for Lua itself) --- runtime/lua/vim/_editor.lua | 28 ++++++++---- runtime/lua/vim/lsp.lua | 2 +- runtime/lua/vim/lsp/completion.lua | 2 +- runtime/lua/vim/lsp/diagnostic.lua | 24 ++--------- runtime/lua/vim/lsp/inlay_hint.lua | 14 ++---- runtime/lua/vim/lsp/semantic_tokens.lua | 14 ++---- runtime/lua/vim/lsp/sync.lua | 59 +++++--------------------- runtime/lua/vim/lsp/util.lua | 75 +++------------------------------ 8 files changed, 47 insertions(+), 171 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 496bbf747c..c6aa303124 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -545,7 +545,7 @@ function vim.region(bufnr, pos1, pos2, regtype, inclusive) -- TODO: handle double-width characters if regtype:byte() == 22 then local bufline = vim.api.nvim_buf_get_lines(bufnr, pos1[1], pos1[1] + 1, true)[1] - pos1[2] = vim.str_utfindex(bufline, pos1[2]) + pos1[2] = vim.str_utfindex(bufline, 'utf-32', pos1[2]) end local region = {} @@ -557,14 +557,14 @@ function vim.region(bufnr, pos1, pos2, regtype, inclusive) c2 = c1 + tonumber(regtype:sub(2)) -- and adjust for non-ASCII characters local bufline = vim.api.nvim_buf_get_lines(bufnr, l, l + 1, true)[1] - local utflen = vim.str_utfindex(bufline, #bufline) + local utflen = vim.str_utfindex(bufline, 'utf-32', #bufline) if c1 <= utflen then - c1 = assert(tonumber(vim.str_byteindex(bufline, c1))) + c1 = assert(tonumber(vim.str_byteindex(bufline, 'utf-32', c1))) else c1 = #bufline + 1 end if c2 <= utflen then - c2 = assert(tonumber(vim.str_byteindex(bufline, c2))) + c2 = assert(tonumber(vim.str_byteindex(bufline, 'utf-32', c2))) else c2 = #bufline + 1 end @@ -740,9 +740,14 @@ function vim.str_byteindex(s, encoding, index, strict_indexing) -- • {str} (`string`) -- • {index} (`integer`) -- • {use_utf16} (`boolean?`) + vim.deprecate( + 'vim.str_byteindex', + 'vim.str_byteindex(s, encoding, index, strict_indexing)', + '1.0' + ) local old_index = encoding local use_utf16 = index or false - return vim.__str_byteindex(s, old_index, use_utf16) or error('index out of range') + return vim._str_byteindex(s, old_index, use_utf16) or error('index out of range') end vim.validate('s', s, 'string') @@ -769,7 +774,7 @@ function vim.str_byteindex(s, encoding, index, strict_indexing) end return index end - return vim.__str_byteindex(s, index, encoding == 'utf-16') + return vim._str_byteindex(s, index, encoding == 'utf-16') or strict_indexing and error('index out of range') or len end @@ -793,8 +798,13 @@ function vim.str_utfindex(s, encoding, index, strict_indexing) -- Parameters: ~ -- • {str} (`string`) -- • {index} (`integer?`) + vim.deprecate( + 'vim.str_utfindex', + 'vim.str_utfindex(s, encoding, index, strict_indexing)', + '1.0' + ) local old_index = encoding - local col32, col16 = vim.__str_utfindex(s, old_index) --[[@as integer,integer]] + local col32, col16 = vim._str_utfindex(s, old_index) --[[@as integer,integer]] if not col32 or not col16 then error('index out of range') end @@ -828,7 +838,7 @@ function vim.str_utfindex(s, encoding, index, strict_indexing) local len = #s return index <= len and index or (strict_indexing and error('index out of range') or len) end - local col32, col16 = vim.__str_utfindex(s, index) --[[@as integer?,integer?]] + local col32, col16 = vim._str_utfindex(s, index) --[[@as integer?,integer?]] local col = encoding == 'utf-16' and col16 or col32 if col then return col @@ -836,7 +846,7 @@ function vim.str_utfindex(s, encoding, index, strict_indexing) if strict_indexing then error('index out of range') end - local max32, max16 = vim.__str_utfindex(s)--[[@as integer integer]] + local max32, max16 = vim._str_utfindex(s)--[[@as integer integer]] return encoding == 'utf-16' and max16 or max32 end diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 4f13ad5721..125238a8e9 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1049,7 +1049,7 @@ function lsp.formatexpr(opts) if client.supports_method(ms.textDocument_rangeFormatting) then local params = util.make_formatting_params() local end_line = vim.fn.getline(end_lnum) --[[@as string]] - local end_col = util._str_utfindex_enc(end_line, nil, client.offset_encoding) + local end_col = vim.str_utfindex(end_line, client.offset_encoding) --- @cast params +lsp.DocumentRangeFormattingParams params.range = { start = { diff --git a/runtime/lua/vim/lsp/completion.lua b/runtime/lua/vim/lsp/completion.lua index e36d329dc5..10086fa49e 100644 --- a/runtime/lua/vim/lsp/completion.lua +++ b/runtime/lua/vim/lsp/completion.lua @@ -315,7 +315,7 @@ local function adjust_start_col(lnum, line, items, encoding) end end if min_start_char then - return lsp.util._str_byteindex_enc(line, min_start_char, encoding) + return vim.str_byteindex(line, encoding, min_start_char, false) else return nil end diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index bf72222536..c59e2db901 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -33,25 +33,6 @@ local function severity_vim_to_lsp(severity) return severity end ----@param lines string[]? ----@param lnum integer ----@param col integer ----@param offset_encoding string ----@return integer -local function line_byte_from_position(lines, lnum, col, offset_encoding) - if not lines or offset_encoding == 'utf-8' then - return col - end - - local line = lines[lnum + 1] - local ok, result = pcall(vim.str_byteindex, line, col, offset_encoding == 'utf-16') - if ok then - return result --- @type integer - end - - return col -end - ---@param bufnr integer ---@return string[]? local function get_buf_lines(bufnr) @@ -118,12 +99,13 @@ local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id) ) message = diagnostic.message.value end + local line = buf_lines and buf_lines[start.line + 1] or '' --- @type vim.Diagnostic return { lnum = start.line, - col = line_byte_from_position(buf_lines, start.line, start.character, offset_encoding), + col = vim.str_byteindex(line, offset_encoding, start.character, false), end_lnum = _end.line, - end_col = line_byte_from_position(buf_lines, _end.line, _end.character, offset_encoding), + end_col = vim.str_byteindex(line, offset_encoding, _end.character, false), severity = severity_lsp_to_vim(diagnostic.severity), message = message, source = diagnostic.source, diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua index 61d119e653..e5892928cf 100644 --- a/runtime/lua/vim/lsp/inlay_hint.lua +++ b/runtime/lua/vim/lsp/inlay_hint.lua @@ -70,20 +70,12 @@ function M.on_inlayhint(err, result, ctx, _) end local lines = api.nvim_buf_get_lines(bufnr, 0, -1, false) - ---@param position lsp.Position - ---@return integer - local function pos_to_byte(position) - local col = position.character - if col > 0 then - local line = lines[position.line + 1] or '' - return util._str_byteindex_enc(line, col, client.offset_encoding) - end - return col - end for _, hint in ipairs(result) do local lnum = hint.position.line - hint.position.character = pos_to_byte(hint.position) + local line = lines and lines[lnum + 1] or '' + hint.position.character = + vim.str_byteindex(line, client.offset_encoding, hint.position.character, false) table.insert(new_lnum_hints[lnum], hint) end diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index 0f6e45c330..d680522592 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -137,16 +137,10 @@ local function tokens_to_ranges(data, bufnr, client, request) local token_type = token_types[data[i + 3] + 1] local modifiers = modifiers_from_number(data[i + 4], token_modifiers) - local function _get_byte_pos(col) - if col > 0 then - local buf_line = lines[line + 1] or '' - return util._str_byteindex_enc(buf_line, col, client.offset_encoding) - end - return col - end - - local start_col = _get_byte_pos(start_char) - local end_col = _get_byte_pos(start_char + data[i + 2]) + local end_char = start_char + data[i + 2] + local buf_line = lines and lines[line + 1] or '' + local start_col = vim.str_byteindex(buf_line, client.offset_encoding, start_char, false) + local end_col = vim.str_byteindex(buf_line, client.offset_encoding, end_char, false) if token_type then ranges[#ranges + 1] = { diff --git a/runtime/lua/vim/lsp/sync.lua b/runtime/lua/vim/lsp/sync.lua index bdfe8d51b8..3df45ebff0 100644 --- a/runtime/lua/vim/lsp/sync.lua +++ b/runtime/lua/vim/lsp/sync.lua @@ -48,45 +48,6 @@ local str_utfindex = vim.str_utfindex local str_utf_start = vim.str_utf_start local str_utf_end = vim.str_utf_end --- Given a line, byte idx, and offset_encoding convert to the --- utf-8, utf-16, or utf-32 index. ----@param line string the line to index into ----@param byte integer the byte idx ----@param offset_encoding string utf-8|utf-16|utf-32|nil (default: utf-8) ----@return integer utf_idx for the given encoding -local function byte_to_utf(line, byte, offset_encoding) - -- convert to 0 based indexing for str_utfindex - byte = byte - 1 - - local utf_idx, _ --- @type integer, integer - -- Convert the byte range to utf-{8,16,32} and convert 1-based (lua) indexing to 0-based - if offset_encoding == 'utf-16' then - _, utf_idx = str_utfindex(line, byte) - elseif offset_encoding == 'utf-32' then - utf_idx, _ = str_utfindex(line, byte) - else - utf_idx = byte - end - - -- convert to 1 based indexing - return utf_idx + 1 -end - ----@param line string ----@param offset_encoding string ----@return integer -local function compute_line_length(line, offset_encoding) - local length, _ --- @type integer, integer - if offset_encoding == 'utf-16' then - _, length = str_utfindex(line) - elseif offset_encoding == 'utf-32' then - length, _ = str_utfindex(line) - else - length = #line - end - return length -end - -- Given a line, byte idx, alignment, and offset_encoding convert to the aligned -- utf-8 index and either the utf-16, or utf-32 index. ---@param line string the line to index into @@ -101,7 +62,7 @@ local function align_end_position(line, byte, offset_encoding) char = byte -- Called in the case of extending an empty line "" -> "a" elseif byte == #line + 1 then - char = compute_line_length(line, offset_encoding) + 1 + char = str_utfindex(line, offset_encoding) + 1 else -- Modifying line, find the nearest utf codepoint local offset = str_utf_start(line, byte) @@ -111,9 +72,10 @@ local function align_end_position(line, byte, offset_encoding) byte = byte + str_utf_end(line, byte) + 1 end if byte <= #line then - char = byte_to_utf(line, byte, offset_encoding) + --- Convert to 0 based for input, and from 0 based for output + char = str_utfindex(line, offset_encoding, byte - 1) + 1 else - char = compute_line_length(line, offset_encoding) + 1 + char = str_utfindex(line, offset_encoding) + 1 end -- Extending line, find the nearest utf codepoint for the last valid character end @@ -153,7 +115,7 @@ local function compute_start_range( if line then line_idx = firstline - 1 byte_idx = #line + 1 - char_idx = compute_line_length(line, offset_encoding) + 1 + char_idx = str_utfindex(line, offset_encoding) + 1 else line_idx = firstline byte_idx = 1 @@ -190,10 +152,11 @@ local function compute_start_range( char_idx = 1 elseif start_byte_idx == #prev_line + 1 then byte_idx = start_byte_idx - char_idx = compute_line_length(prev_line, offset_encoding) + 1 + char_idx = str_utfindex(prev_line, offset_encoding) + 1 else byte_idx = start_byte_idx + str_utf_start(prev_line, start_byte_idx) - char_idx = byte_to_utf(prev_line, byte_idx, offset_encoding) + --- Convert to 0 based for input, and from 0 based for output + char_idx = vim.str_utfindex(prev_line, offset_encoding, byte_idx - 1) + 1 end -- Return the start difference (shared for new and prev lines) @@ -230,7 +193,7 @@ local function compute_end_range( return { line_idx = lastline - 1, byte_idx = #prev_line + 1, - char_idx = compute_line_length(prev_line, offset_encoding) + 1, + char_idx = str_utfindex(prev_line, offset_encoding) + 1, }, { line_idx = 1, byte_idx = 1, char_idx = 1 } end -- If firstline == new_lastline, the first change occurred on a line that was deleted. @@ -376,7 +339,7 @@ local function compute_range_length(lines, start_range, end_range, offset_encodi local start_line = lines[start_range.line_idx] local range_length --- @type integer if start_line and #start_line > 0 then - range_length = compute_line_length(start_line, offset_encoding) + range_length = str_utfindex(start_line, offset_encoding) - start_range.char_idx + 1 + line_ending_length @@ -389,7 +352,7 @@ local function compute_range_length(lines, start_range, end_range, offset_encodi for idx = start_range.line_idx + 1, end_range.line_idx - 1 do -- Length full line plus newline character if #lines[idx] > 0 then - range_length = range_length + compute_line_length(lines[idx], offset_encoding) + #line_ending + range_length = range_length + str_utfindex(lines[idx], offset_encoding) + #line_ending else range_length = range_length + line_ending_length end diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 2e9c71cf38..9646f4d571 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -116,71 +116,6 @@ local function create_window_without_focus() return new end ---- Convert byte index to `encoding` index. ---- Convenience wrapper around vim.str_utfindex ----@param line string line to be indexed ----@param index integer? byte index (utf-8), or `nil` for length ----@param encoding 'utf-8'|'utf-16'|'utf-32'? defaults to utf-16 ----@return integer `encoding` index of `index` in `line` -function M._str_utfindex_enc(line, index, encoding) - local len32, len16 = vim.str_utfindex(line) - if not encoding then - encoding = 'utf-16' - end - if encoding == 'utf-8' then - if index then - return index - else - return #line - end - elseif encoding == 'utf-16' then - if not index or index > len16 then - return len16 - end - local _, col16 = vim.str_utfindex(line, index) - return col16 - elseif encoding == 'utf-32' then - if not index or index > len32 then - return len32 - end - local col32, _ = vim.str_utfindex(line, index) - return col32 - else - error('Invalid encoding: ' .. vim.inspect(encoding)) - end -end - ---- Convert UTF index to `encoding` index. ---- Convenience wrapper around vim.str_byteindex ----Alternative to vim.str_byteindex that takes an encoding. ----@param line string line to be indexed ----@param index integer UTF index ----@param encoding string utf-8|utf-16|utf-32| defaults to utf-16 ----@return integer byte (utf-8) index of `encoding` index `index` in `line` -function M._str_byteindex_enc(line, index, encoding) - -- LSP spec: if character > line length, default to the line length. - -- https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#position - local len8 = #line - if not encoding then - encoding = 'utf-16' - end - if encoding == 'utf-8' then - if index and index <= len8 then - return index - else - return len8 - end - end - local len32, len16 = vim.str_utfindex(line) - if encoding == 'utf-16' then - return index <= len16 and vim.str_byteindex(line, index, true) or len8 - elseif encoding == 'utf-32' then - return index <= len32 and vim.str_byteindex(line, index) or len8 - else - error('Invalid encoding: ' .. vim.inspect(encoding)) - end -end - --- Replaces text in a range with new text. --- --- CAUTION: Changes in-place! @@ -352,7 +287,7 @@ local function get_line_byte_from_position(bufnr, position, offset_encoding) -- character if col > 0 then local line = get_line(bufnr, position.line) or '' - return M._str_byteindex_enc(line, col, offset_encoding) + return vim.str_byteindex(line, offset_encoding, col, false) end return col end @@ -1787,8 +1722,8 @@ function M.locations_to_items(locations, offset_encoding) local end_row = end_pos.line local line = lines[row] or '' local end_line = lines[end_row] or '' - local col = M._str_byteindex_enc(line, pos.character, offset_encoding) - local end_col = M._str_byteindex_enc(end_line, end_pos.character, offset_encoding) + local col = vim.str_byteindex(line, offset_encoding, pos.character, false) + local end_col = vim.str_byteindex(end_line, offset_encoding, end_pos.character, false) items[#items + 1] = { filename = filename, @@ -1911,7 +1846,7 @@ local function make_position_param(window, offset_encoding) return { line = 0, character = 0 } end - col = M._str_utfindex_enc(line, col, offset_encoding) + col = vim.str_utfindex(line, offset_encoding, col, false) return { line = row, character = col } end @@ -2092,7 +2027,7 @@ function M.character_offset(buf, row, col, offset_encoding) ) offset_encoding = vim.lsp.get_clients({ bufnr = buf })[1].offset_encoding end - return M._str_utfindex_enc(line, col, offset_encoding) + return vim.str_utfindex(line, offset_encoding, col, false) end --- Helper function to return nested values in language server settings -- cgit From f60a1f90599df0b773a107232f4005d6dea194b0 Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Fri, 25 Oct 2024 14:09:29 -0700 Subject: docs(treesitter): specify predicate boolean return value --- runtime/lua/vim/treesitter/query.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index 4614967799..1677e8d364 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -626,7 +626,7 @@ local directive_handlers = { --- Adds a new predicate to be used in queries --- ---@param name string Name of the predicate, without leading # ----@param handler fun(match: table, pattern: integer, source: integer|string, predicate: any[], metadata: vim.treesitter.query.TSMetadata) +---@param handler fun(match: table, pattern: integer, source: integer|string, predicate: any[], metadata: vim.treesitter.query.TSMetadata): boolean? --- - see |vim.treesitter.query.add_directive()| for argument meanings ---@param opts? vim.treesitter.query.add_predicate.Opts function M.add_predicate(name, handler, opts) -- cgit From 123c0b6b4e2d3f0665774b70b6bec79afbef4509 Mon Sep 17 00:00:00 2001 From: Maria José Solano Date: Sat, 26 Oct 2024 14:06:15 -0700 Subject: docs(lsp): document alternative for vim.lsp.util.jump_to_location --- runtime/lua/vim/lsp/util.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 9646f4d571..41f93e5374 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -957,7 +957,7 @@ end --- Jumps to a location. --- ----@deprecated +---@deprecated use `vim.lsp.util.show_document` with `{focus=true}` instead ---@param location lsp.Location|lsp.LocationLink ---@param offset_encoding 'utf-8'|'utf-16'|'utf-32'? ---@param reuse_win boolean? Jump to existing window if buffer is already open. -- cgit From adf7c98d607b362dd3b25d6d0bee0d1212c472dd Mon Sep 17 00:00:00 2001 From: Gregory Anders Date: Sun, 27 Oct 2024 11:35:45 -0500 Subject: fix(lsp): compare URI instead of workspace folder name (#30962) The workspace folder name is meant to be a human-readable name which is only used in the UI. Comparing the name against root_dir is thus not a valid comparison. Instead, we should compare the workspace folder's URI against the root dir URI. --- runtime/lua/vim/lsp.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 125238a8e9..a41a3f1585 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -189,9 +189,10 @@ local function reuse_client_default(client, config) end if config.root_dir then + local root = vim.uri_from_fname(config.root_dir) for _, dir in ipairs(client.workspace_folders or {}) do -- note: do not need to check client.root_dir since that should be client.workspace_folders[1] - if config.root_dir == dir.name then + if root == dir.uri then return true end end -- cgit From a9e725b26e08f494a9300d8f63acd3efa9c2da82 Mon Sep 17 00:00:00 2001 From: Donatas Date: Sun, 27 Oct 2024 18:36:39 +0200 Subject: feat(diagnostics)!: sort underline severity_sort (#30898) feat(diagnostics)!: sort underline with severity_sort BREAKING CHANGE: underline will be applied with a higher value than `vim.hl.priorities.diagnostics` --- runtime/lua/vim/diagnostic.lua | 49 +++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 20 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 392db5b800..299f34c921 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -71,9 +71,9 @@ local M = {} --- (default: `false`) --- @field update_in_insert? boolean --- ---- Sort diagnostics by severity. This affects the order in which signs and ---- virtual text are displayed. When true, higher severities are displayed ---- before lower severities (e.g. ERROR is displayed before WARN). +--- Sort diagnostics by severity. This affects the order in which signs, +--- virtual text, and highlights are displayed. When true, higher severities are +--- displayed before lower severities (e.g. ERROR is displayed before WARN). --- Options: --- - {reverse}? (boolean) Reverse sort order --- (default: `false`) @@ -657,6 +657,28 @@ local function save_extmarks(namespace, bufnr) api.nvim_buf_get_extmarks(bufnr, namespace, 0, -1, { details = true }) end +--- Create a function that converts a diagnostic severity to an extmark priority. +--- @param priority integer Base priority +--- @param opts vim.diagnostic.OptsResolved +--- @return fun(severity: vim.diagnostic.Severity): integer +local function severity_to_extmark_priority(priority, opts) + if opts.severity_sort then + if type(opts.severity_sort) == 'table' and opts.severity_sort.reverse then + return function(severity) + return priority + (severity - vim.diagnostic.severity.ERROR) + end + end + + return function(severity) + return priority + (vim.diagnostic.severity.HINT - severity) + end + end + + return function() + return priority + end +end + --- @type table local registered_autocmds = {} @@ -1352,22 +1374,7 @@ M.handlers.signs = { -- 10 is the default sign priority when none is explicitly specified local priority = opts.signs and opts.signs.priority or 10 - local get_priority --- @type function - if opts.severity_sort then - if type(opts.severity_sort) == 'table' and opts.severity_sort.reverse then - get_priority = function(severity) - return priority + (severity - vim.diagnostic.severity.ERROR) - end - else - get_priority = function(severity) - return priority + (vim.diagnostic.severity.HINT - severity) - end - end - else - get_priority = function() - return priority - end - end + local get_priority = severity_to_extmark_priority(priority, opts) local ns = M.get_namespace(namespace) if not ns.user_data.sign_ns then @@ -1478,6 +1485,8 @@ M.handlers.underline = { end local underline_ns = ns.user_data.underline_ns + local get_priority = severity_to_extmark_priority(vim.hl.priorities.diagnostics, opts) + for _, diagnostic in ipairs(diagnostics) do --- @type string? local higroup = underline_highlight_map[assert(diagnostic.severity)] @@ -1504,7 +1513,7 @@ M.handlers.underline = { higroup, { diagnostic.lnum, diagnostic.col }, { diagnostic.end_lnum, diagnostic.end_col }, - { priority = vim.hl.priorities.diagnostics } + { priority = get_priority(diagnostic.severity) } ) end save_extmarks(underline_ns, bufnr) -- cgit From 0086ee90dd2f058f48bbaceef842bb0d8923cc34 Mon Sep 17 00:00:00 2001 From: Maria José Solano Date: Mon, 28 Oct 2024 06:14:56 -0700 Subject: fix(lsp): list all workspace folders in healthcheck #30966 --- runtime/lua/vim/lsp/health.lua | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/health.lua b/runtime/lua/vim/lsp/health.lua index 18066a84db..0d314108fe 100644 --- a/runtime/lua/vim/lsp/health.lua +++ b/runtime/lua/vim/lsp/health.lua @@ -39,12 +39,27 @@ local function check_active_clients() elseif type(client.config.cmd) == 'function' then cmd = tostring(client.config.cmd) end + local dirs_info ---@type string + if client.workspace_folders and #client.workspace_folders > 1 then + dirs_info = string.format( + ' Workspace folders:\n %s', + vim + .iter(client.workspace_folders) + ---@param folder lsp.WorkspaceFolder + :map(function(folder) + return folder.name + end) + :join('\n ') + ) + else + dirs_info = string.format( + ' Root directory: %s', + client.root_dir and vim.fn.fnamemodify(client.root_dir, ':~') + ) or nil + end report_info(table.concat({ string.format('%s (id: %d)', client.name, client.id), - string.format( - ' Root directory: %s', - client.root_dir and vim.fn.fnamemodify(client.root_dir, ':~') or nil - ), + dirs_info, string.format(' Command: %s', cmd), string.format(' Settings: %s', vim.inspect(client.settings, { newline = '\n ' })), string.format( -- cgit From 0b7cc014fc0ed8d0765e8504ff4a8ff7a84dac42 Mon Sep 17 00:00:00 2001 From: Nikita Revenco <154856872+nikitarevenco@users.noreply.github.com> Date: Mon, 28 Oct 2024 13:29:29 +0000 Subject: feat(defaults): map gO to LSP document_symbol #30781 --- runtime/lua/vim/_defaults.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua index 1b1114b764..03b2803f3e 100644 --- a/runtime/lua/vim/_defaults.lua +++ b/runtime/lua/vim/_defaults.lua @@ -157,7 +157,7 @@ do --- client is attached. If no client is attached, or if a server does not support a capability, an --- error message is displayed rather than exhibiting different behavior. --- - --- See |grr|, |grn|, |gra|, |gri|, |i_CTRL-S|. + --- See |grr|, |grn|, |gra|, |gri|, |gO|, |i_CTRL-S|. do vim.keymap.set('n', 'grn', function() vim.lsp.buf.rename() @@ -175,6 +175,10 @@ do vim.lsp.buf.implementation() end, { desc = 'vim.lsp.buf.implementation()' }) + vim.keymap.set('n', 'gO', function() + vim.lsp.buf.document_symbol() + end, { desc = 'vim.lsp.buf.document_symbol()' }) + vim.keymap.set('i', '', function() vim.lsp.buf.signature_help() end, { desc = 'vim.lsp.buf.signature_help()' }) -- cgit From 378d9135e7ac0f91a4944be816dc9f693d5078af Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 28 Oct 2024 15:14:15 +0800 Subject: vim-patch:9.1.0810: cannot easily adjust the |:find| command Problem: cannot easily adjust the |:find| command Solution: Add support for the 'findexpr' option (Yegappan Lakshmanan) closes: vim/vim#15901 closes: vim/vim#15905 https://github.com/vim/vim/commit/aeb1c97db5b9de4f4903e7f288f2aa5ad6c49440 Co-authored-by: Yegappan Lakshmanan --- runtime/lua/vim/_meta/options.lua | 51 +++++++++++++++++++++++++++++++++++++++ runtime/lua/vim/_meta/vvars.lua | 3 ++- 2 files changed, 53 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index 00f7554832..8d5601ff6a 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -2294,6 +2294,57 @@ vim.wo.fcs = vim.wo.fillchars vim.go.fillchars = vim.o.fillchars vim.go.fcs = vim.go.fillchars +--- Expression that is evaluated to obtain the filename(s) for the `:find` +--- command. When this option is empty, the internal `file-searching` +--- mechanism is used. +--- +--- While evaluating the expression, the `v:fname` variable is set to the +--- argument of the `:find` command. +--- +--- The expression is evaluated only once per `:find` command invocation. +--- The expression can process all the directories specified in 'path'. +--- +--- If a match is found, the expression should return a `List` containing +--- one or more file names. If a match is not found, the expression +--- should return an empty List. +--- +--- If any errors are encountered during the expression evaluation, an +--- empty List is used as the return value. +--- +--- Using a function call without arguments is faster `expr-option-function` +--- +--- It is not allowed to change text or jump to another window while +--- evaluating 'findexpr' `textlock`. +--- +--- This option cannot be set from a `modeline` or in the `sandbox`, for +--- security reasons. +--- +--- Examples: +--- +--- ```vim +--- " Use glob() +--- func FindExprGlob() +--- return glob(v:fname, v:false, v:true) +--- endfunc +--- set findexpr=FindExprGlob() +--- +--- " Use the 'git ls-files' output +--- func FindGitFiles() +--- let fnames = systemlist('git ls-files') +--- return fnames->filter('v:val =~? v:fname') +--- endfunc +--- set findexpr=FindGitFiles() +--- ``` +--- +--- +--- @type string +vim.o.findexpr = "" +vim.o.fexpr = vim.o.findexpr +vim.bo.findexpr = vim.o.findexpr +vim.bo.fexpr = vim.bo.findexpr +vim.go.findexpr = vim.o.findexpr +vim.go.fexpr = vim.go.findexpr + --- When writing a file and this option is on, at the end of file --- will be restored if missing. Turn this option off if you want to --- preserve the situation from the original file. diff --git a/runtime/lua/vim/_meta/vvars.lua b/runtime/lua/vim/_meta/vvars.lua index e00402ab3f..cba200101b 100644 --- a/runtime/lua/vim/_meta/vvars.lua +++ b/runtime/lua/vim/_meta/vvars.lua @@ -267,7 +267,8 @@ vim.v.fcs_choice = ... vim.v.fcs_reason = ... --- When evaluating 'includeexpr': the file name that was ---- detected. Empty otherwise. +--- detected. When evaluating 'findexpr': the argument passed to +--- the `:find` command. Empty otherwise. --- @type string vim.v.fname = ... -- cgit From 60b3ccd850ca038af6fc0a19cad12578f63d67ec Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 29 Oct 2024 07:02:04 +0800 Subject: vim-patch:9.1.0821: 'findexpr' completion doesn't set v:fname to cmdline argument Problem: 'findexpr' completion doesn't set v:fname to cmdline argument. Solution: Set v:fname to the cmdline argument as-is (zeertzjq). closes: vim/vim#15934 https://github.com/vim/vim/commit/20e045f78148c0ef0143c33ffe686fee72d29376 --- runtime/lua/vim/_meta/options.lua | 7 ++++++- runtime/lua/vim/_meta/vvars.lua | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index 8d5601ff6a..710f82bf21 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -2304,6 +2304,10 @@ vim.go.fcs = vim.go.fillchars --- The expression is evaluated only once per `:find` command invocation. --- The expression can process all the directories specified in 'path'. --- +--- The expression may be evaluated for command-line completion as well, +--- in which case the `v:cmdcomplete` variable will be set to `v:true`, +--- otherwise it will be set to `v:false`. +--- --- If a match is found, the expression should return a `List` containing --- one or more file names. If a match is not found, the expression --- should return an empty List. @@ -2324,7 +2328,8 @@ vim.go.fcs = vim.go.fillchars --- ```vim --- " Use glob() --- func FindExprGlob() ---- return glob(v:fname, v:false, v:true) +--- let pat = v:cmdcomplete ? $'{v:fname}*' : v:fname +--- return glob(pat, v:false, v:true) --- endfunc --- set findexpr=FindExprGlob() --- diff --git a/runtime/lua/vim/_meta/vvars.lua b/runtime/lua/vim/_meta/vvars.lua index cba200101b..b104356334 100644 --- a/runtime/lua/vim/_meta/vvars.lua +++ b/runtime/lua/vim/_meta/vvars.lua @@ -44,6 +44,11 @@ vim.v.cmdarg = ... --- @type integer vim.v.cmdbang = ... +--- When evaluating 'findexpr': if 'findexpr' is used for cmdline +--- completion the value is `v:true`, otherwise it is `v:false`. +--- @type boolean +vim.v.cmdcomplete = ... + --- The current locale setting for collation order of the runtime --- environment. This allows Vim scripts to be aware of the --- current locale encoding. Technical: it's the value of -- cgit From 1471dfc85964a9d95c216619d715bfe449d97c19 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 24 Oct 2024 16:44:09 +0100 Subject: refactor(lsp): buf_request_all --- runtime/lua/vim/lsp.lua | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 4f13ad5721..4065785c5a 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -915,35 +915,29 @@ end --- ---@param bufnr (integer) Buffer handle, or 0 for current. ---@param method (string) LSP method name ----@param params (table|nil) Parameters to send to the server ----@param handler fun(results: table) (function) +---@param params? table|(fun(client: vim.lsp.Client, bufnr: integer): table?) Parameters to send to the server +---@param handler lsp.MultiHandler (function) --- Handler called after all requests are completed. Server results are passed as --- a `client_id:result` map. ---@return function cancel Function that cancels all requests. function lsp.buf_request_all(bufnr, method, params, handler) - local results = {} --- @type table - local result_count = 0 - local expected_result_count = 0 - - local set_expected_result_count = once(function() - for _, client in ipairs(lsp.get_clients({ bufnr = bufnr })) do - if client.supports_method(method, { bufnr = bufnr }) then - expected_result_count = expected_result_count + 1 - end + local results = {} --- @type table + local remaining --- @type integer? + + local _, cancel = lsp.buf_request(bufnr, method, params, function(err, result, ctx, config) + if not remaining then + -- Calculate as late as possible in case a client is removed during the request + remaining = #lsp.get_clients({ bufnr = bufnr, method = method }) end - end) - local function _sync_handler(err, result, ctx) - results[ctx.client_id] = { error = err, result = result } - result_count = result_count + 1 - set_expected_result_count() + -- The error key is deprecated and will be removed in 0.13 + results[ctx.client_id] = { err = err, error = err, result = result } + remaining = remaining - 1 - if result_count >= expected_result_count then - handler(results) + if remaining == 0 then + handler(results, ctx, config) end - end - - local _, cancel = lsp.buf_request(bufnr, method, params, _sync_handler) + end) return cancel end -- cgit From 8260e4860b27a54a061bd8e2a9da23069993953a Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 24 Oct 2024 16:47:41 +0100 Subject: feat(lsp)!: multiple client support for vim.lsp.buf.hover() Deprecate `vim.lsp.handlers.hover` and `vim.lsp.handlers['textDocument/hover']` --- runtime/lua/vim/lsp.lua | 4 ++- runtime/lua/vim/lsp/_meta.lua | 1 + runtime/lua/vim/lsp/buf.lua | 77 ++++++++++++++++++++++++++++++++++++++-- runtime/lua/vim/lsp/handlers.lua | 5 ++- 4 files changed, 83 insertions(+), 4 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 4065785c5a..685dcae851 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -915,7 +915,9 @@ end --- ---@param bufnr (integer) Buffer handle, or 0 for current. ---@param method (string) LSP method name ----@param params? table|(fun(client: vim.lsp.Client, bufnr: integer): table?) Parameters to send to the server +---@param params? table|(fun(client: vim.lsp.Client, bufnr: integer): table?) Parameters to send to the server. +--- Can also be passed as a function that returns the params table for cases where +--- parameters are specific to the client. ---@param handler lsp.MultiHandler (function) --- Handler called after all requests are completed. Server results are passed as --- a `client_id:result` map. diff --git a/runtime/lua/vim/lsp/_meta.lua b/runtime/lua/vim/lsp/_meta.lua index be3222828d..589a49c003 100644 --- a/runtime/lua/vim/lsp/_meta.lua +++ b/runtime/lua/vim/lsp/_meta.lua @@ -2,6 +2,7 @@ error('Cannot require a meta file') ---@alias lsp.Handler fun(err: lsp.ResponseError?, result: any, context: lsp.HandlerContext, config?: table): ...any +---@alias lsp.MultiHandler fun(results: table, context: lsp.HandlerContext, config?: table): ...any ---@class lsp.HandlerContext ---@field method string diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 5f50e361c4..dd06b1b6c0 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -20,6 +20,9 @@ local function client_positional_params(params) end end +--- @class vim.lsp.buf.hover.Opts : vim.lsp.util.open_floating_preview.Opts +--- @field silent? boolean + --- Displays hover information about the symbol under the cursor in a floating --- window. The window will be dismissed on cursor move. --- Calling the function twice will jump into the floating window @@ -27,8 +30,78 @@ end --- In the floating window, all commands and mappings are available as usual, --- except that "q" dismisses the window. --- You can scroll the contents the same as you would any other buffer. -function M.hover() - lsp.buf_request(0, ms.textDocument_hover, client_positional_params()) +--- @param config? vim.lsp.buf.hover.Opts +function M.hover(config) + config = config or {} + config.focus_id = ms.textDocument_hover + + lsp.buf_request_all(0, ms.textDocument_hover, client_positional_params(), function(results, ctx) + if api.nvim_get_current_buf() ~= ctx.bufnr then + -- Ignore result since buffer changed. This happens for slow language servers. + return + end + + -- Filter errors from results + local results1 = {} --- @type table + + for client_id, resp in pairs(results) do + local err, result = resp.err, resp.result + if err then + lsp.log.error(err.code, err.message) + elseif result then + results1[client_id] = result + end + end + + if #results1 == 0 then + if config.silent ~= true then + vim.notify('No information available') + end + return + end + + local contents = {} --- @type string[] + + local nresults = #vim.tbl_keys(results1) + + local format = 'markdown' + + for client_id, result in pairs(results1) do + if nresults > 1 then + -- Show client name if there are multiple clients + contents[#contents + 1] = string.format('# %s', lsp.get_client_by_id(client_id).name) + end + if type(result.contents) == 'table' and result.contents.kind == 'plaintext' then + if #results1 == 1 then + format = 'plaintext' + contents = vim.split(result.contents.value or '', '\n', { trimempty = true }) + else + -- Surround plaintext with ``` to get correct formatting + contents[#contents + 1] = '```' + vim.list_extend( + contents, + vim.split(result.contents.value or '', '\n', { trimempty = true }) + ) + contents[#contents + 1] = '```' + end + else + vim.list_extend(contents, util.convert_input_to_markdown_lines(result.contents)) + end + contents[#contents + 1] = '---' + end + + -- Remove last linebreak ('---') + contents[#contents] = nil + + if vim.tbl_isempty(contents) then + if config.silent ~= true then + vim.notify('No information available') + end + return + end + + lsp.util.open_floating_preview(contents, format, config) + end) end local function request_with_opts(name, params, opts) diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index a905f76fda..ad827cfd34 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -317,7 +317,7 @@ M[ms.textDocument_formatting] = function(_, result, ctx, _) util.apply_text_edits(result, ctx.bufnr, client.offset_encoding) end ---- @deprecated +--- @deprecated remove in 0.13 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion M[ms.textDocument_completion] = function(_, result, _, _) if vim.tbl_isempty(result or {}) then @@ -334,6 +334,7 @@ M[ms.textDocument_completion] = function(_, result, _, _) vim.fn.complete(textMatch + 1, matches) end +--- @deprecated --- |lsp-handler| for the method "textDocument/hover" --- --- ```lua @@ -384,7 +385,9 @@ function M.hover(_, result, ctx, config) return util.open_floating_preview(contents, format, config) end +--- @deprecated remove in 0.13 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover +--- @diagnostic disable-next-line: deprecated M[ms.textDocument_hover] = M.hover local sig_help_ns = api.nvim_create_namespace('vim_lsp_signature_help') -- cgit From 4c7f5032af6baca61764cde5122765b2ec684e4a Mon Sep 17 00:00:00 2001 From: Kai Moschcau Date: Tue, 29 Oct 2024 15:36:58 +0100 Subject: docs(options): shell-powershell #30969 `-NonInteractive` at least somewhat hints to pwsh/powershell, that shell sessions created from :! are not interactive, though even that is not foolproof, because powershell is weird. `$PSStyle.OutputRendering='plaintext'` causes pwsh/powershell to omit ANSI escape sequences in its output. --- runtime/lua/vim/_meta/options.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index 710f82bf21..5e5b6b5ed1 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -5434,7 +5434,7 @@ vim.go.sdf = vim.go.shadafile --- --- ```vim --- let &shell = executable('pwsh') ? 'pwsh' : 'powershell' ---- let &shellcmdflag = '-NoLogo -ExecutionPolicy RemoteSigned -Command [Console]::InputEncoding=[Console]::OutputEncoding=[System.Text.UTF8Encoding]::new();$PSDefaultParameterValues[''Out-File:Encoding'']=''utf8'';Remove-Alias -Force -ErrorAction SilentlyContinue tee;' +--- let &shellcmdflag = '-NoLogo -NonInteractive -ExecutionPolicy RemoteSigned -Command [Console]::InputEncoding=[Console]::OutputEncoding=[System.Text.UTF8Encoding]::new();$PSDefaultParameterValues[''Out-File:Encoding'']=''utf8'';$PSStyle.OutputRendering=''plaintext'';Remove-Alias -Force -ErrorAction SilentlyContinue tee;' --- let &shellredir = '2>&1 | %%{ "$_" } | Out-File %s; exit $LastExitCode' --- let &shellpipe = '2>&1 | %%{ "$_" } | tee %s; exit $LastExitCode' --- set shellquote= shellxquote= -- cgit From ff93cccbc1ce4bb217d503e2ac5f81f9023d4d43 Mon Sep 17 00:00:00 2001 From: Gregory Anders Date: Tue, 29 Oct 2024 10:06:14 -0500 Subject: fix(defaults): omit extraneous info from unimpaired mapping errors (#30983) --- runtime/lua/vim/_defaults.lua | 204 +++++++++++++++++++++++------------------- 1 file changed, 111 insertions(+), 93 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua index 03b2803f3e..d3b7bda871 100644 --- a/runtime/lua/vim/_defaults.lua +++ b/runtime/lua/vim/_defaults.lua @@ -219,132 +219,150 @@ do --- vim-unimpaired style mappings. See: https://github.com/tpope/vim-unimpaired do - ---@param lhs string - ---@param rhs function - ---@param desc string - local function create_unimpaired_mapping(lhs, rhs, desc) - vim.keymap.set('n', lhs, function() - local _, err = pcall(rhs) ---@type any, string - if err then - vim.api.nvim_err_writeln(err) - end - end, { desc = desc }) + --- Execute a command and print errors without a stacktrace. + --- @param opts table Arguments to |nvim_cmd()| + local function cmd(opts) + local _, err = pcall(vim.api.nvim_cmd, opts, {}) + if err then + vim.api.nvim_err_writeln(err:sub(#'Vim:' + 1)) + end end -- Quickfix mappings - create_unimpaired_mapping('[q', function() - vim.cmd.cprevious({ count = vim.v.count1 }) - end, ':cprevious') - create_unimpaired_mapping(']q', function() - vim.cmd.cnext({ count = vim.v.count1 }) - end, ':cnext') - create_unimpaired_mapping('[Q', function() - vim.cmd.crewind({ count = vim.v.count ~= 0 and vim.v.count or nil }) - end, ':crewind') - create_unimpaired_mapping(']Q', function() - vim.cmd.clast({ count = vim.v.count ~= 0 and vim.v.count or nil }) - end, ':clast') - create_unimpaired_mapping('[', function() - vim.cmd.cpfile({ count = vim.v.count1 }) - end, ':cpfile') - create_unimpaired_mapping(']', function() - vim.cmd.cnfile({ count = vim.v.count1 }) - end, ':cnfile') + vim.keymap.set('n', '[q', function() + cmd({ cmd = 'cprevious', count = vim.v.count1 }) + end, { desc = ':cprevious' }) + + vim.keymap.set('n', ']q', function() + cmd({ cmd = 'cnext', count = vim.v.count1 }) + end, { desc = ':cnext' }) + + vim.keymap.set('n', '[Q', function() + cmd({ cmd = 'crewind', count = vim.v.count ~= 0 and vim.v.count or nil }) + end, { desc = ':crewind' }) + + vim.keymap.set('n', ']Q', function() + cmd({ cmd = 'clast', count = vim.v.count ~= 0 and vim.v.count or nil }) + end, { desc = ':clast' }) + + vim.keymap.set('n', '[', function() + cmd({ cmd = 'cpfile', count = vim.v.count1 }) + end, { desc = ':cpfile' }) + + vim.keymap.set('n', ']', function() + cmd({ cmd = 'cnfile', count = vim.v.count1 }) + end, { desc = ':cnfile' }) -- Location list mappings - create_unimpaired_mapping('[l', function() - vim.cmd.lprevious({ count = vim.v.count1 }) - end, ':lprevious') - create_unimpaired_mapping(']l', function() - vim.cmd.lnext({ count = vim.v.count1 }) - end, ':lnext') - create_unimpaired_mapping('[L', function() - vim.cmd.lrewind({ count = vim.v.count ~= 0 and vim.v.count or nil }) - end, ':lrewind') - create_unimpaired_mapping(']L', function() - vim.cmd.llast({ count = vim.v.count ~= 0 and vim.v.count or nil }) - end, ':llast') - create_unimpaired_mapping('[', function() - vim.cmd.lpfile({ count = vim.v.count1 }) - end, ':lpfile') - create_unimpaired_mapping(']', function() - vim.cmd.lnfile({ count = vim.v.count1 }) - end, ':lnfile') + vim.keymap.set('n', '[l', function() + cmd({ cmd = 'lprevious', count = vim.v.count1 }) + end, { desc = ':lprevious' }) + + vim.keymap.set('n', ']l', function() + cmd({ cmd = 'lnext', count = vim.v.count1 }) + end, { desc = ':lnext' }) + + vim.keymap.set('n', '[L', function() + cmd({ cmd = 'lrewind', count = vim.v.count ~= 0 and vim.v.count or nil }) + end, { desc = ':lrewind' }) + + vim.keymap.set('n', ']L', function() + cmd({ cmd = 'llast', count = vim.v.count ~= 0 and vim.v.count or nil }) + end, { desc = ':llast' }) + + vim.keymap.set('n', '[', function() + cmd({ cmd = 'lpfile', count = vim.v.count1 }) + end, { desc = ':lpfile' }) + + vim.keymap.set('n', ']', function() + cmd({ cmd = 'lnfile', count = vim.v.count1 }) + end, { desc = ':lnfile' }) -- Argument list - create_unimpaired_mapping('[a', function() - vim.cmd.previous({ count = vim.v.count1 }) - end, ':previous') - create_unimpaired_mapping(']a', function() + vim.keymap.set('n', '[a', function() + cmd({ cmd = 'previous', count = vim.v.count1 }) + end, { desc = ':previous' }) + + vim.keymap.set('n', ']a', function() -- count doesn't work with :next, must use range. See #30641. - vim.cmd.next({ range = { vim.v.count1 } }) - end, ':next') - create_unimpaired_mapping('[A', function() + cmd({ cmd = 'next', range = { vim.v.count1 } }) + end, { desc = ':next' }) + + vim.keymap.set('n', '[A', function() if vim.v.count ~= 0 then - vim.cmd.argument({ count = vim.v.count }) + cmd({ cmd = 'argument', count = vim.v.count }) else - vim.cmd.rewind() + cmd({ cmd = 'rewind' }) end - end, ':rewind') - create_unimpaired_mapping(']A', function() + end, { desc = ':rewind' }) + + vim.keymap.set('n', ']A', function() if vim.v.count ~= 0 then - vim.cmd.argument({ count = vim.v.count }) + cmd({ cmd = 'argument', count = vim.v.count }) else - vim.cmd.last() + cmd({ cmd = 'last' }) end - end, ':last') + end, { desc = ':last' }) -- Tags - create_unimpaired_mapping('[t', function() + vim.keymap.set('n', '[t', function() -- count doesn't work with :tprevious, must use range. See #30641. - vim.cmd.tprevious({ range = { vim.v.count1 } }) - end, ':tprevious') - create_unimpaired_mapping(']t', function() + cmd({ cmd = 'tprevious', range = { vim.v.count1 } }) + end, { desc = ':tprevious' }) + + vim.keymap.set('n', ']t', function() -- count doesn't work with :tnext, must use range. See #30641. - vim.cmd.tnext({ range = { vim.v.count1 } }) - end, ':tnext') - create_unimpaired_mapping('[T', function() + cmd({ cmd = 'tnext', range = { vim.v.count1 } }) + end, { desc = ':tnext' }) + + vim.keymap.set('n', '[T', function() -- count doesn't work with :trewind, must use range. See #30641. - vim.cmd.trewind({ range = vim.v.count ~= 0 and { vim.v.count } or nil }) - end, ':trewind') - create_unimpaired_mapping(']T', function() + cmd({ cmd = 'trewind', range = vim.v.count ~= 0 and { vim.v.count } or nil }) + end, { desc = ':trewind' }) + + vim.keymap.set('n', ']T', function() -- :tlast does not accept a count, so use :trewind if count given if vim.v.count ~= 0 then - vim.cmd.trewind({ range = { vim.v.count } }) + cmd({ cmd = 'trewind', range = { vim.v.count } }) else - vim.cmd.tlast() + cmd({ cmd = 'tlast' }) end - end, ':tlast') - create_unimpaired_mapping('[', function() + end, { desc = ':tlast' }) + + vim.keymap.set('n', '[', function() -- count doesn't work with :ptprevious, must use range. See #30641. - vim.cmd.ptprevious({ range = { vim.v.count1 } }) - end, ' :ptprevious') - create_unimpaired_mapping(']', function() + cmd({ cmd = 'ptprevious', range = { vim.v.count1 } }) + end, { desc = ' :ptprevious' }) + + vim.keymap.set('n', ']', function() -- count doesn't work with :ptnext, must use range. See #30641. - vim.cmd.ptnext({ range = { vim.v.count1 } }) - end, ':ptnext') + cmd({ cmd = 'ptnext', range = { vim.v.count1 } }) + end, { desc = ':ptnext' }) -- Buffers - create_unimpaired_mapping('[b', function() - vim.cmd.bprevious({ count = vim.v.count1 }) - end, ':bprevious') - create_unimpaired_mapping(']b', function() - vim.cmd.bnext({ count = vim.v.count1 }) - end, ':bnext') - create_unimpaired_mapping('[B', function() + vim.keymap.set('n', '[b', function() + cmd({ cmd = 'bprevious', count = vim.v.count1 }) + end, { desc = ':bprevious' }) + + vim.keymap.set('n', ']b', function() + cmd({ cmd = 'bnext', count = vim.v.count1 }) + end, { desc = ':bnext' }) + + vim.keymap.set('n', '[B', function() if vim.v.count ~= 0 then - vim.cmd.buffer({ count = vim.v.count }) + cmd({ cmd = 'buffer', count = vim.v.count }) else - vim.cmd.brewind() + cmd({ cmd = 'brewind' }) end - end, ':brewind') - create_unimpaired_mapping(']B', function() + end, { desc = ':brewind' }) + + vim.keymap.set('n', ']B', function() if vim.v.count ~= 0 then - vim.cmd.buffer({ count = vim.v.count }) + cmd({ cmd = 'buffer', count = vim.v.count }) else - vim.cmd.blast() + cmd({ cmd = 'blast' }) end - end, ':blast') + end, { desc = ':blast' }) end end -- cgit From b4599acbf8e30aa1234bc4041546f960dcc3a238 Mon Sep 17 00:00:00 2001 From: notomo <18519692+notomo@users.noreply.github.com> Date: Thu, 31 Oct 2024 00:57:33 +0900 Subject: fix(lsp): correct hover result handling (#30995) Problem: vim.lsp.buf.hover() displays "No information available" when client_id is not 1. Solution: use vim.tbl_isempty(tbl) instead of #tbl==0 --- runtime/lua/vim/lsp/buf.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index dd06b1b6c0..4818359fb3 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -53,7 +53,7 @@ function M.hover(config) end end - if #results1 == 0 then + if vim.tbl_isempty(results1) then if config.silent ~= true then vim.notify('No information available') end -- cgit From 82e5066a7f8606dc677271049ead4829eb57935a Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Thu, 31 Oct 2024 09:50:10 +0100 Subject: vim-patch:9.1.0823: filetype: Zephyr overlay files not recognized Problem: filetype: Zephyr overlay files not recognized Solution: detect '*.overlay' files as dts filetype, include syntax tests for DTS files (Xudong Zheng) Reference: https://docs.zephyrproject.org/latest/build/dts/howtos.html closes: vim/vim#15963 https://github.com/vim/vim/commit/a68bd6f089239a51ba90026b18109707e601b107 Co-authored-by: Xudong Zheng <7pkvm5aw@slicealias.com> --- runtime/lua/vim/filetype.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index aa74a67968..f6928c6428 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -402,6 +402,7 @@ local extension = { dtso = 'dts', its = 'dts', keymap = 'dts', + overlay = 'dts', dylan = 'dylan', intr = 'dylanintr', lid = 'dylanlid', -- cgit From 1d4ba8c1edba064421b34c1197c3470a09798218 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 31 Oct 2024 11:28:02 +0000 Subject: fix: another round of type annotation fixes --- runtime/lua/vim/diagnostic.lua | 48 +++++++++++++++++-------------------- runtime/lua/vim/filetype/detect.lua | 3 ++- runtime/lua/vim/glob.lua | 2 +- runtime/lua/vim/shared.lua | 20 ++++++++++++---- runtime/lua/vim/termcap.lua | 2 +- runtime/lua/vim/treesitter.lua | 2 +- runtime/lua/vim/ui.lua | 8 +++++-- 7 files changed, 49 insertions(+), 36 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 299f34c921..4fb8c6a686 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -282,6 +282,11 @@ M.severity = { [2] = 'WARN', [3] = 'INFO', [4] = 'HINT', + --- Mappings from qflist/loclist error types to severities + E = 1, + W = 2, + I = 3, + N = 4, } --- @alias vim.diagnostic.SeverityInt 1|2|3|4 @@ -289,12 +294,6 @@ M.severity = { --- See |diagnostic-severity| and |vim.diagnostic.get()| --- @alias vim.diagnostic.SeverityFilter vim.diagnostic.Severity|vim.diagnostic.Severity[]|{min:vim.diagnostic.Severity,max:vim.diagnostic.Severity} --- Mappings from qflist/loclist error types to severities -M.severity.E = M.severity.ERROR -M.severity.W = M.severity.WARN -M.severity.I = M.severity.INFO -M.severity.N = M.severity.HINT - --- @type vim.diagnostic.Opts local global_diagnostic_options = { signs = true, @@ -891,14 +890,14 @@ local function next_diagnostic(search_forward, opts) if opts.win_id then vim.deprecate('opts.win_id', 'opts.winid', '0.13') opts.winid = opts.win_id - opts.win_id = nil + opts.win_id = nil --- @diagnostic disable-line end -- Support deprecated cursor_position alias if opts.cursor_position then vim.deprecate('opts.cursor_position', 'opts.pos', '0.13') opts.pos = opts.cursor_position - opts.cursor_position = nil + opts.cursor_position = nil --- @diagnostic disable-line end local winid = opts.winid or api.nvim_get_current_win() @@ -979,7 +978,7 @@ local function goto_diagnostic(diagnostic, opts) if opts.win_id then vim.deprecate('opts.win_id', 'opts.winid', '0.13') opts.winid = opts.win_id - opts.win_id = nil + opts.win_id = nil --- @diagnostic disable-line end local winid = opts.winid or api.nvim_get_current_win() @@ -992,8 +991,9 @@ local function goto_diagnostic(diagnostic, opts) vim.cmd('normal! zv') end) - if opts.float then - local float_opts = type(opts.float) == 'table' and opts.float or {} + local float_opts = opts.float + if float_opts then + float_opts = type(float_opts) == 'table' and float_opts or {} vim.schedule(function() M.open_float(vim.tbl_extend('keep', float_opts, { bufnr = api.nvim_win_get_buf(winid), @@ -1317,7 +1317,7 @@ function M.jump(opts) if opts.cursor_position then vim.deprecate('opts.cursor_position', 'opts.pos', '0.13') opts.pos = opts.cursor_position - opts.cursor_position = nil + opts.cursor_position = nil --- @diagnostic disable-line end local diag = nil @@ -1488,14 +1488,9 @@ M.handlers.underline = { local get_priority = severity_to_extmark_priority(vim.hl.priorities.diagnostics, opts) for _, diagnostic in ipairs(diagnostics) do - --- @type string? + -- Default to error if we don't have a highlight associated local higroup = underline_highlight_map[assert(diagnostic.severity)] - - if higroup == nil then - -- Default to error if we don't have a highlight associated - -- TODO(lewis6991): this is always nil since underline_highlight_map only has integer keys - higroup = underline_highlight_map.Error - end + or underline_highlight_map[vim.diagnostic.severity.ERROR] if diagnostic._tags then -- TODO(lewis6991): we should be able to stack these. @@ -2115,9 +2110,10 @@ function M.enable(enable, filter) enable = enable == nil and true or enable local bufnr = filter.bufnr + local ns_id = filter.ns_id - if bufnr == nil then - if filter.ns_id == nil then + if not bufnr then + if not ns_id then diagnostic_disabled = ( enable -- Enable everything by setting diagnostic_disabled to an empty table. @@ -2131,12 +2127,12 @@ function M.enable(enable, filter) }) ) else - local ns = M.get_namespace(filter.ns_id) + local ns = M.get_namespace(ns_id) ns.disabled = not enable end else bufnr = get_bufnr(bufnr) - if filter.ns_id == nil then + if not ns_id then diagnostic_disabled[bufnr] = (not enable) and true or nil else if type(diagnostic_disabled[bufnr]) ~= 'table' then @@ -2146,14 +2142,14 @@ function M.enable(enable, filter) diagnostic_disabled[bufnr] = {} end end - diagnostic_disabled[bufnr][filter.ns_id] = (not enable) and true or nil + diagnostic_disabled[bufnr][ns_id] = (not enable) and true or nil end end if enable then - M.show(filter.ns_id, bufnr) + M.show(ns_id, bufnr) else - M.hide(filter.ns_id, bufnr) + M.hide(ns_id, bufnr) end end diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 1cc81b177f..1b9e28efad 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -1908,7 +1908,7 @@ local function match_from_hashbang(contents, path, dispatch_extension) end for k, v in pairs(patterns_hashbang) do - local ft = type(v) == 'table' and v[1] or v + local ft = type(v) == 'table' and v[1] or v --[[@as string]] local opts = type(v) == 'table' and v[2] or {} if opts.vim_regex and matchregex(name, k) or name:find(k) then return ft @@ -2080,6 +2080,7 @@ local function match_from_text(contents, path) return ft end else + --- @cast k string local opts = type(v) == 'table' and v[2] or {} if opts.start_lnum and opts.end_lnum then assert( diff --git a/runtime/lua/vim/glob.lua b/runtime/lua/vim/glob.lua index 22073b15c8..4f86d5e1ca 100644 --- a/runtime/lua/vim/glob.lua +++ b/runtime/lua/vim/glob.lua @@ -48,7 +48,7 @@ function M.to_lpeg(pattern) end -- luacheck: push ignore s - local function cut(s, idx, match) + local function cut(_s, idx, match) return idx, match end -- luacheck: pop diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 94a194cde9..1a3b33c885 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -782,7 +782,8 @@ end do --- @alias vim.validate.Validator - --- | type|'callable' + --- | type + --- | 'callable' --- | (type|'callable')[] --- | fun(v:any):boolean, string? @@ -1170,11 +1171,13 @@ function vim._defer_deprecated_module(old_name, new_name) ---@param k string __index = function(_, k) vim.deprecate(old_name, new_name, '2.0.0', nil, false) + --- @diagnostic disable-next-line:no-unknown local target = require(new_name) return target[k] end, __call = function(self) vim.deprecate(old_name, new_name, '2.0.0', nil, false) + --- @diagnostic disable-next-line:no-unknown local target = require(new_name) return target(self) end, @@ -1217,11 +1220,14 @@ local state_restore_order = { 'bo', 'wo', 'go', 'env' } --- @param context vim.context.mods --- @return vim.context.state local get_context_state = function(context) + --- @type vim.context.state local res = { bo = {}, env = {}, go = {}, wo = {} } -- Use specific order from possibly most to least intrusive for _, scope in ipairs(scope_order) do - for name, _ in pairs(context[scope] or {}) do + for name, _ in + pairs(context[scope] or {} --[[@as table]]) + do local sc = scope == 'o' and scope_map[vim.api.nvim_get_option_info2(name, {}).scope] or scope -- Do not override already set state and fall back to `vim.NIL` for @@ -1315,7 +1321,10 @@ function vim._with(context, f) -- Apply some parts of the context in specific order -- NOTE: triggers `OptionSet` event for _, scope in ipairs(scope_order) do - for name, context_value in pairs(context[scope] or {}) do + for name, context_value in + pairs(context[scope] or {} --[[@as table]]) + do + --- @diagnostic disable-next-line:no-unknown vim[scope][name] = context_value end end @@ -1326,7 +1335,10 @@ function vim._with(context, f) -- Restore relevant cached values in specific order, global scope last -- NOTE: triggers `OptionSet` event for _, scope in ipairs(state_restore_order) do - for name, cached_value in pairs(state[scope]) do + for name, cached_value in + pairs(state[scope] --[[@as table]]) + do + --- @diagnostic disable-next-line:no-unknown vim[scope][name] = cached_value end end diff --git a/runtime/lua/vim/termcap.lua b/runtime/lua/vim/termcap.lua index 2c36561587..4aa41bba9b 100644 --- a/runtime/lua/vim/termcap.lua +++ b/runtime/lua/vim/termcap.lua @@ -38,7 +38,7 @@ function M.query(caps, cb) local k, rest = resp:match('^\027P1%+r(%x+)(.*)$') if k and rest then local cap = vim.text.hexdecode(k) - if not pending[cap] then + if not cap or not pending[cap] then -- Received a response for a capability we didn't request. This can happen if there are -- multiple concurrent XTGETTCAP requests return diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 9f6dc53932..dca89f413c 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -165,7 +165,7 @@ function M.get_node_range(node_or_range) if type(node_or_range) == 'table' then return unpack(node_or_range) else - return node_or_range:range() + return node_or_range:range(false) end end diff --git a/runtime/lua/vim/ui.lua b/runtime/lua/vim/ui.lua index 899709d3cf..cd159f0172 100644 --- a/runtime/lua/vim/ui.lua +++ b/runtime/lua/vim/ui.lua @@ -43,7 +43,9 @@ function M.select(items, opts, on_choice) opts = opts or {} local choices = { opts.prompt or 'Select one of:' } local format_item = opts.format_item or tostring - for i, item in ipairs(items) do + for i, item in + ipairs(items --[[@as any[] ]]) + do table.insert(choices, string.format('%d: %s', i, format_item(item))) end local choice = vim.fn.inputlist(choices) @@ -204,7 +206,9 @@ function M._get_urls() if vim.treesitter.node_contains(node, range) then local url = metadata[id] and metadata[id].url if url and match[url] then - for _, n in ipairs(match[url]) do + for _, n in + ipairs(match[url] --[[@as TSNode[] ]]) + do urls[#urls + 1] = vim.treesitter.get_node_text(n, bufnr, { metadata = metadata[url] }) end -- cgit From 03118c46abab81b12c903db88245552bad1b4861 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 31 Oct 2024 14:15:09 +0000 Subject: refactor(loader): remove unused _topmods --- runtime/lua/vim/loader.lua | 6 ------ 1 file changed, 6 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/loader.lua b/runtime/lua/vim/loader.lua index e86d33bf53..75adee344d 100644 --- a/runtime/lua/vim/loader.lua +++ b/runtime/lua/vim/loader.lua @@ -59,8 +59,6 @@ local Loader = { VERSION = 4, ---@type table> _indexed = {}, - ---@type table - _topmods = {}, _loadfile = loadfile, ---@type LoaderStats _stats = { @@ -466,10 +464,6 @@ function Loader.lsmod(path) end if topname then Loader._indexed[path][topname] = { modpath = modpath, modname = topname } - Loader._topmods[topname] = Loader._topmods[topname] or {} - if not vim.list_contains(Loader._topmods[topname], path) then - table.insert(Loader._topmods[topname], path) - end end end end -- cgit From f8fc6cb157555903b524422d4149132ad6ae267b Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 31 Oct 2024 14:16:46 +0000 Subject: perf(loader): reduce calls to Loader.cache_file --- runtime/lua/vim/loader.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/loader.lua b/runtime/lua/vim/loader.lua index 75adee344d..526bbc6c5c 100644 --- a/runtime/lua/vim/loader.lua +++ b/runtime/lua/vim/loader.lua @@ -125,11 +125,10 @@ function Loader.cache_file(name) end --- Saves the cache entry for a given module or file ----@param name string module name or filename +---@param cname string cache filename ---@param entry CacheEntry ---@private -function Loader.write(name, entry) - local cname = Loader.cache_file(name) +function Loader.write(cname, entry) local f = assert(uv.fs_open(cname, 'w', 438)) local header = { Loader.VERSION, @@ -156,11 +155,10 @@ local function readfile(path, mode) end --- Loads the cache entry for a given module or file ----@param name string module name or filename +---@param cname string cache filename ---@return CacheEntry? ---@private -function Loader.read(name) - local cname = Loader.cache_file(name) +function Loader.read(cname) local data = readfile(cname, 438) if data then local zero = data:find('\0', 1, true) @@ -268,7 +266,9 @@ function Loader.load(modpath, opts) return Loader._loadfile(modpath, opts.mode, opts.env) end - local entry = Loader.read(modpath) + local cname = Loader.cache_file(modpath) + + local entry = Loader.read(cname) if entry and Loader.eq(entry.hash, hash) then -- found in cache and up to date chunk, err = load(entry.chunk --[[@as string]], '@' .. modpath, opts.mode, opts.env) @@ -281,7 +281,7 @@ function Loader.load(modpath, opts) chunk, err = Loader._loadfile(modpath, opts.mode, opts.env) if chunk then entry.chunk = string.dump(chunk) - Loader.write(modpath, entry) + Loader.write(cname, entry) end return chunk, err end -- cgit From 53536be62a9efe8e641214a3950b904e108bfe28 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 31 Oct 2024 14:18:51 +0000 Subject: refactor(loader): simplify Loader.write/read --- runtime/lua/vim/loader.lua | 85 +++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 42 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/loader.lua b/runtime/lua/vim/loader.lua index 526bbc6c5c..904036806c 100644 --- a/runtime/lua/vim/loader.lua +++ b/runtime/lua/vim/loader.lua @@ -126,18 +126,19 @@ end --- Saves the cache entry for a given module or file ---@param cname string cache filename ----@param entry CacheEntry +---@param hash CacheHash +---@param chunk function ---@private -function Loader.write(cname, entry) +function Loader.write(cname, hash, chunk) local f = assert(uv.fs_open(cname, 'w', 438)) local header = { Loader.VERSION, - entry.hash.size, - entry.hash.mtime.sec, - entry.hash.mtime.nsec, + hash.size, + hash.mtime.sec, + hash.mtime.nsec, } uv.fs_write(f, table.concat(header, ',') .. '\0') - uv.fs_write(f, entry.chunk) + uv.fs_write(f, string.dump(chunk)) uv.fs_close(f) end @@ -156,29 +157,34 @@ end --- Loads the cache entry for a given module or file ---@param cname string cache filename ----@return CacheEntry? +---@return CacheHash? hash +---@return string? chunk ---@private function Loader.read(cname) local data = readfile(cname, 438) - if data then - local zero = data:find('\0', 1, true) - if not zero then - return - end + if not data then + return + end - ---@type integer[]|{[0]:integer} - local header = vim.split(data:sub(1, zero - 1), ',') - if tonumber(header[1]) ~= Loader.VERSION then - return - end - return { - hash = { - size = tonumber(header[2]), - mtime = { sec = tonumber(header[3]), nsec = tonumber(header[4]) }, - }, - chunk = data:sub(zero + 1), - } + local zero = data:find('\0', 1, true) + if not zero then + return end + + ---@type integer[]|{[0]:integer} + local header = vim.split(data:sub(1, zero - 1), ',') + if tonumber(header[1]) ~= Loader.VERSION then + return + end + + local hash = { + size = tonumber(header[2]), + mtime = { sec = tonumber(header[3]), nsec = tonumber(header[4]) }, + } + + local chunk = data:sub(zero + 1) + + return hash, chunk end --- The `package.loaders` loader for Lua files using the cache. @@ -236,15 +242,15 @@ end --- * file size --- * mtime in seconds --- * mtime in nanoseconds ----@param h1 CacheHash ----@param h2 CacheHash +---@param a? CacheHash +---@param b? CacheHash ---@private -function Loader.eq(h1, h2) - return h1 - and h2 - and h1.size == h2.size - and h1.mtime.sec == h2.mtime.sec - and h1.mtime.nsec == h2.mtime.nsec +function Loader.eq(a, b) + return a + and b + and a.size == b.size + and a.mtime.sec == b.mtime.sec + and a.mtime.nsec == b.mtime.nsec end --- Loads the given module path using the cache @@ -258,9 +264,6 @@ end function Loader.load(modpath, opts) opts = opts or {} local hash = Loader.get_hash(modpath) - ---@type function?, string? - local chunk, err - if not hash then -- trigger correct error return Loader._loadfile(modpath, opts.mode, opts.env) @@ -268,20 +271,18 @@ function Loader.load(modpath, opts) local cname = Loader.cache_file(modpath) - local entry = Loader.read(cname) - if entry and Loader.eq(entry.hash, hash) then + local e_hash, e_chunk = Loader.read(cname) + if Loader.eq(e_hash, hash) and e_chunk then -- found in cache and up to date - chunk, err = load(entry.chunk --[[@as string]], '@' .. modpath, opts.mode, opts.env) + local chunk, err = load(e_chunk, '@' .. modpath, opts.mode, opts.env) if not (err and err:find('cannot load incompatible bytecode', 1, true)) then return chunk, err end end - entry = { hash = hash, modpath = modpath } - chunk, err = Loader._loadfile(modpath, opts.mode, opts.env) + local chunk, err = Loader._loadfile(modpath, opts.mode, opts.env) if chunk then - entry.chunk = string.dump(chunk) - Loader.write(cname, entry) + Loader.write(cname, hash, chunk) end return chunk, err end -- cgit From ab2f2461b5ec2fd3a9c0924cd7dcfaa6028b8d21 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 31 Oct 2024 14:25:00 +0000 Subject: refactor(loader): simplify Loader.loader_lib --- runtime/lua/vim/loader.lua | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/loader.lua b/runtime/lua/vim/loader.lua index 904036806c..21662668e3 100644 --- a/runtime/lua/vim/loader.lua +++ b/runtime/lua/vim/loader.lua @@ -205,25 +205,27 @@ function Loader.loader(modname) return ("\n\tcache_loader: module '%s' not found"):format(modname) end +local is_win = vim.fn.has('win32') == 1 + --- The `package.loaders` loader for libs ---@param modname string module name ---@return string|function ---@private function Loader.loader_lib(modname) - local is_win = vim.fn.has('win32') == 1 - local ret = M.find(modname, { patterns = is_win and { '.dll' } or { '.so' } })[1] - if ret then - -- Making function name in Lua 5.1 (see src/loadlib.c:mkfuncname) is - -- a) strip prefix up to and including the first dash, if any - -- b) replace all dots by underscores - -- c) prepend "luaopen_" - -- So "foo-bar.baz" should result in "luaopen_bar_baz" - local dash = modname:find('-', 1, true) - local funcname = dash and modname:sub(dash + 1) or modname - local chunk, err = package.loadlib(ret.modpath, 'luaopen_' .. funcname:gsub('%.', '_')) - return chunk or error(err) + local ret = M.find(modname, { patterns = { is_win and '.dll' or '.so' } })[1] + if not ret then + return ("\n\tcache_loader_lib: module '%s' not found"):format(modname) end - return ("\n\tcache_loader_lib: module '%s' not found"):format(modname) + + -- Making function name in Lua 5.1 (see src/loadlib.c:mkfuncname) is + -- a) strip prefix up to and including the first dash, if any + -- b) replace all dots by underscores + -- c) prepend "luaopen_" + -- So "foo-bar.baz" should result in "luaopen_bar_baz" + local dash = modname:find('-', 1, true) + local funcname = dash and modname:sub(dash + 1) or modname + local chunk, err = package.loadlib(ret.modpath, 'luaopen_' .. funcname:gsub('%.', '_')) + return chunk or error(err) end --- `loadfile` using the cache -- cgit From 7ccdd9235a362f75b6d242af8ca0db9c21f968f7 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 31 Oct 2024 14:30:14 +0000 Subject: refactor(loader): inline Loader.load into Loader.loadfile --- runtime/lua/vim/loader.lua | 60 ++++++++++++++++------------------------------ 1 file changed, 21 insertions(+), 39 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/loader.lua b/runtime/lua/vim/loader.lua index 21662668e3..c839707e1d 100644 --- a/runtime/lua/vim/loader.lua +++ b/runtime/lua/vim/loader.lua @@ -231,13 +231,30 @@ end --- `loadfile` using the cache --- Note this has the mode and env arguments which is supported by LuaJIT and is 5.1 compatible. ---@param filename? string ----@param _mode? "b"|"t"|"bt" +---@param mode? "b"|"t"|"bt" ---@param env? table ---@return function?, string? error_message ---@private -function Loader.loadfile(filename, _mode, env) - -- ignore mode, since we byte-compile the Lua source files - return Loader.load(normalize(filename), { env = env }) +function Loader.loadfile(filename, mode, env) + local modpath = normalize(filename) + local hash = Loader.get_hash(modpath) + local cname = Loader.cache_file(modpath) + if hash then + local e_hash, e_chunk = Loader.read(cname) + if Loader.eq(e_hash, hash) and e_chunk then + -- found in cache and up to date + local chunk, err = load(e_chunk, '@' .. modpath, mode, env) + if not (err and err:find('cannot load incompatible bytecode', 1, true)) then + return chunk, err + end + end + end + + local chunk, err = Loader._loadfile(modpath, mode, env) + if chunk then + Loader.write(cname, hash, chunk) + end + return chunk, err end --- Checks whether two cache hashes are the same based on: @@ -255,40 +272,6 @@ function Loader.eq(a, b) and a.mtime.nsec == b.mtime.nsec end ---- Loads the given module path using the cache ----@param modpath string ----@param opts? {mode?: "b"|"t"|"bt", env?:table} (table|nil) Options for loading the module: ---- - mode: (string) the mode to load the module with. "b"|"t"|"bt" (defaults to `nil`) ---- - env: (table) the environment to load the module in. (defaults to `nil`) ----@see |luaL_loadfile()| ----@return function?, string? error_message ----@private -function Loader.load(modpath, opts) - opts = opts or {} - local hash = Loader.get_hash(modpath) - if not hash then - -- trigger correct error - return Loader._loadfile(modpath, opts.mode, opts.env) - end - - local cname = Loader.cache_file(modpath) - - local e_hash, e_chunk = Loader.read(cname) - if Loader.eq(e_hash, hash) and e_chunk then - -- found in cache and up to date - local chunk, err = load(e_chunk, '@' .. modpath, opts.mode, opts.env) - if not (err and err:find('cannot load incompatible bytecode', 1, true)) then - return chunk, err - end - end - - local chunk, err = Loader._loadfile(modpath, opts.mode, opts.env) - if chunk then - Loader.write(cname, hash, chunk) - end - return chunk, err -end - --- Finds Lua modules for the given module name. --- --- @since 0 @@ -501,7 +484,6 @@ function M._profile(opts) Loader.loader = Loader.track('loader', Loader.loader) Loader.loader_lib = Loader.track('loader_lib', Loader.loader_lib) Loader.loadfile = Loader.track('loadfile', Loader.loadfile) - Loader.load = Loader.track('load', Loader.load) M.find = Loader.track('find', M.find) Loader.lsmod = Loader.track('lsmod', Loader.lsmod) -- cgit From 4a0010e267abd0798915961687da5dcb603e69e0 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 31 Oct 2024 14:33:58 +0000 Subject: refactor(loader): rename types --- runtime/lua/vim/loader.lua | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/loader.lua b/runtime/lua/vim/loader.lua index c839707e1d..c63eb2c850 100644 --- a/runtime/lua/vim/loader.lua +++ b/runtime/lua/vim/loader.lua @@ -7,8 +7,8 @@ local loaders = package.loaders local M = {} ----@alias CacheHash {mtime: {nsec: integer, sec: integer}, size: integer, type?: string} ----@alias CacheEntry {hash:CacheHash, chunk:string} +---@alias vim.loader.CacheHash {mtime: {nsec: integer, sec: integer}, size: integer, type?: string} +---@alias vim.loader.CacheEntry {hash:vim.loader.CacheHash, chunk:string} --- @class vim.loader.find.Opts --- @inlinedoc @@ -54,7 +54,7 @@ M.enabled = false ---@field private _rtp string[] ---@field private _rtp_pure string[] ---@field private _rtp_key string ----@field private _hashes? table +---@field private _hashes? table local Loader = { VERSION = 4, ---@type table> @@ -67,11 +67,11 @@ local Loader = { } --- @param path string ---- @return CacheHash +--- @return vim.loader.CacheHash --- @private function Loader.get_hash(path) if not Loader._hashes then - return uv.fs_stat(path) --[[@as CacheHash]] + return uv.fs_stat(path) --[[@as vim.loader.CacheHash]] end if not Loader._hashes[path] then @@ -126,7 +126,7 @@ end --- Saves the cache entry for a given module or file ---@param cname string cache filename ----@param hash CacheHash +---@param hash vim.loader.CacheHash ---@param chunk function ---@private function Loader.write(cname, hash, chunk) @@ -157,7 +157,7 @@ end --- Loads the cache entry for a given module or file ---@param cname string cache filename ----@return CacheHash? hash +---@return vim.loader.CacheHash? hash ---@return string? chunk ---@private function Loader.read(cname) @@ -261,8 +261,8 @@ end --- * file size --- * mtime in seconds --- * mtime in nanoseconds ----@param a? CacheHash ----@param b? CacheHash +---@param a? vim.loader.CacheHash +---@param b? vim.loader.CacheHash ---@private function Loader.eq(a, b) return a -- cgit From 8d68d883a9656fd7cf7be1db7ecf4e6ba674995c Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 31 Oct 2024 14:48:28 +0000 Subject: refactor(loader): remove Loader table and use locals --- runtime/lua/vim/loader.lua | 254 +++++++++++++++++++++------------------------ 1 file changed, 120 insertions(+), 134 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/loader.lua b/runtime/lua/vim/loader.lua index c63eb2c850..d22a943762 100644 --- a/runtime/lua/vim/loader.lua +++ b/runtime/lua/vim/loader.lua @@ -4,6 +4,9 @@ local uri_encode = vim.uri_encode --- @type function --- @type (fun(modename: string): fun()|string)[] local loaders = package.loaders +local _loadfile = loadfile + +local VERSION = 4 local M = {} @@ -42,7 +45,7 @@ local M = {} --- The fs_stat of the module path. Won't be returned for `modname="*"` --- @field stat? uv.uv_fs_t ----@alias LoaderStats table +---@alias vim.loader.Stats table ---@nodoc M.path = vim.fn.stdpath('cache') .. '/luac' @@ -50,76 +53,69 @@ M.path = vim.fn.stdpath('cache') .. '/luac' ---@nodoc M.enabled = false ----@class (private) Loader ----@field private _rtp string[] ----@field private _rtp_pure string[] ----@field private _rtp_key string ----@field private _hashes? table -local Loader = { - VERSION = 4, - ---@type table> - _indexed = {}, - _loadfile = loadfile, - ---@type LoaderStats - _stats = { - find = { total = 0, time = 0, not_found = 0 }, - }, -} +---@type vim.loader.Stats +local stats = { find = { total = 0, time = 0, not_found = 0 } } + +--- @type table? +local hashes + +---@type table> +local indexed = {} --- @param path string --- @return vim.loader.CacheHash ---- @private -function Loader.get_hash(path) - if not Loader._hashes then +local function get_hash(path) + if not hashes then return uv.fs_stat(path) --[[@as vim.loader.CacheHash]] end - if not Loader._hashes[path] then + if not hashes[path] then -- Note we must never save a stat for a non-existent path. -- For non-existent paths fs_stat() will return nil. - Loader._hashes[path] = uv.fs_stat(path) + hashes[path] = uv.fs_stat(path) end - return Loader._hashes[path] + return hashes[path] end local function normalize(path) return fs.normalize(path, { expand_env = false, _fast = true }) end +local rtp_cached = {} --- @type string[] +local rtp_cache_key --- @type string? + --- Gets the rtp excluding after directories. --- The result is cached, and will be updated if the runtime path changes. --- When called from a fast event, the cached value will be returned. --- @return string[] rtp, boolean updated ----@private -function Loader.get_rtp() +local function get_rtp() if vim.in_fast_event() then - return (Loader._rtp or {}), false + return (rtp_cached or {}), false end local updated = false local key = vim.go.rtp - if key ~= Loader._rtp_key then - Loader._rtp = {} + if key ~= rtp_cache_key then + rtp_cached = {} for _, path in ipairs(vim.api.nvim_get_runtime_file('', true)) do path = normalize(path) -- skip after directories if path:sub(-6, -1) ~= '/after' - and not (Loader._indexed[path] and vim.tbl_isempty(Loader._indexed[path])) + and not (indexed[path] and vim.tbl_isempty(indexed[path])) then - Loader._rtp[#Loader._rtp + 1] = path + rtp_cached[#rtp_cached + 1] = path end end updated = true - Loader._rtp_key = key + rtp_cache_key = key end - return Loader._rtp, updated + return rtp_cached, updated end --- Returns the cache file name ---@param name string can be a module name, or a file name ---@return string file_name ----@private -function Loader.cache_file(name) +local function cache_filename(name) local ret = ('%s/%s'):format(M.path, uri_encode(name, 'rfc2396')) return ret:sub(-4) == '.lua' and (ret .. 'c') or (ret .. '.luac') end @@ -128,11 +124,10 @@ end ---@param cname string cache filename ---@param hash vim.loader.CacheHash ---@param chunk function ----@private -function Loader.write(cname, hash, chunk) +local function write_cachefile(cname, hash, chunk) local f = assert(uv.fs_open(cname, 'w', 438)) local header = { - Loader.VERSION, + VERSION, hash.size, hash.mtime.sec, hash.mtime.nsec, @@ -159,8 +154,7 @@ end ---@param cname string cache filename ---@return vim.loader.CacheHash? hash ---@return string? chunk ----@private -function Loader.read(cname) +local function read_cachefile(cname) local data = readfile(cname, 438) if not data then return @@ -173,7 +167,7 @@ function Loader.read(cname) ---@type integer[]|{[0]:integer} local header = vim.split(data:sub(1, zero - 1), ',') - if tonumber(header[1]) ~= Loader.VERSION then + if tonumber(header[1]) ~= VERSION then return end @@ -190,18 +184,17 @@ end --- The `package.loaders` loader for Lua files using the cache. ---@param modname string module name ---@return string|function ----@private -function Loader.loader(modname) - Loader._hashes = {} +local function loader_cached(modname) + hashes = {} local ret = M.find(modname)[1] if ret then -- Make sure to call the global loadfile so we respect any augmentations done elsewhere. -- E.g. profiling local chunk, err = loadfile(ret.modpath) - Loader._hashes = nil + hashes = nil return chunk or error(err) end - Loader._hashes = nil + hashes = nil return ("\n\tcache_loader: module '%s' not found"):format(modname) end @@ -210,8 +203,7 @@ local is_win = vim.fn.has('win32') == 1 --- The `package.loaders` loader for libs ---@param modname string module name ---@return string|function ----@private -function Loader.loader_lib(modname) +local function loader_lib_cached(modname) local ret = M.find(modname, { patterns = { is_win and '.dll' or '.so' } })[1] if not ret then return ("\n\tcache_loader_lib: module '%s' not found"):format(modname) @@ -228,20 +220,33 @@ function Loader.loader_lib(modname) return chunk or error(err) end +--- Checks whether two cache hashes are the same based on: +--- * file size +--- * mtime in seconds +--- * mtime in nanoseconds +---@param a? vim.loader.CacheHash +---@param b? vim.loader.CacheHash +local function hash_eq(a, b) + return a + and b + and a.size == b.size + and a.mtime.sec == b.mtime.sec + and a.mtime.nsec == b.mtime.nsec +end + --- `loadfile` using the cache --- Note this has the mode and env arguments which is supported by LuaJIT and is 5.1 compatible. ---@param filename? string ---@param mode? "b"|"t"|"bt" ---@param env? table ---@return function?, string? error_message ----@private -function Loader.loadfile(filename, mode, env) +local function loadfile_cached(filename, mode, env) local modpath = normalize(filename) - local hash = Loader.get_hash(modpath) - local cname = Loader.cache_file(modpath) + local hash = get_hash(modpath) + local cname = cache_filename(modpath) if hash then - local e_hash, e_chunk = Loader.read(cname) - if Loader.eq(e_hash, hash) and e_chunk then + local e_hash, e_chunk = read_cachefile(cname) + if hash_eq(e_hash, hash) and e_chunk then -- found in cache and up to date local chunk, err = load(e_chunk, '@' .. modpath, mode, env) if not (err and err:find('cannot load incompatible bytecode', 1, true)) then @@ -250,26 +255,38 @@ function Loader.loadfile(filename, mode, env) end end - local chunk, err = Loader._loadfile(modpath, mode, env) + local chunk, err = _loadfile(modpath, mode, env) if chunk then - Loader.write(cname, hash, chunk) + write_cachefile(cname, hash, chunk) end return chunk, err end ---- Checks whether two cache hashes are the same based on: ---- * file size ---- * mtime in seconds ---- * mtime in nanoseconds ----@param a? vim.loader.CacheHash ----@param b? vim.loader.CacheHash ----@private -function Loader.eq(a, b) - return a - and b - and a.size == b.size - and a.mtime.sec == b.mtime.sec - and a.mtime.nsec == b.mtime.nsec +--- Return the top-level \`/lua/*` modules for this path +---@param path string path to check for top-level Lua modules +local function lsmod(path) + if not indexed[path] then + indexed[path] = {} + for name, t in fs.dir(path .. '/lua') do + local modpath = path .. '/lua/' .. name + -- HACK: type is not always returned due to a bug in luv + t = t or get_hash(modpath).type + ---@type string + local topname + local ext = name:sub(-4) + if ext == '.lua' or ext == '.dll' then + topname = name:sub(1, -5) + elseif name:sub(-3) == '.so' then + topname = name:sub(1, -4) + elseif t == 'link' or t == 'directory' then + topname = name + end + if topname then + indexed[path][topname] = { modpath = modpath, modname = topname } + end + end + end + return indexed[path] end --- Finds Lua modules for the given module name. @@ -318,17 +335,17 @@ function M.find(modname, opts) local function _find(paths) for _, path in ipairs(paths) do if topmod == '*' then - for _, r in pairs(Loader.lsmod(path)) do + for _, r in pairs(lsmod(path)) do results[#results + 1] = r if not continue() then return end end - elseif Loader.lsmod(path)[topmod] then + elseif lsmod(path)[topmod] then for _, pattern in ipairs(patterns) do local modpath = path .. pattern - Loader._stats.find.stat = (Loader._stats.find.stat or 0) + 1 - local hash = Loader.get_hash(modpath) + stats.find.stat = (stats.find.stat or 0) + 1 + local hash = get_hash(modpath) if hash then results[#results + 1] = { modpath = modpath, stat = hash, modname = modname } if not continue() then @@ -342,9 +359,9 @@ function M.find(modname, opts) -- always check the rtp first if opts.rtp ~= false then - _find(Loader._rtp or {}) + _find(rtp_cached or {}) if continue() then - local rtp, updated = Loader.get_rtp() + local rtp, updated = get_rtp() if updated then _find(rtp) end @@ -358,7 +375,7 @@ function M.find(modname, opts) if #results == 0 then -- module not found - Loader._stats.find.not_found = Loader._stats.find.not_found + 1 + stats.find.not_found = stats.find.not_found + 1 end return results @@ -371,14 +388,14 @@ end ---@param path string? path to reset function M.reset(path) if path then - Loader._indexed[normalize(path)] = nil + indexed[normalize(path)] = nil else - Loader._indexed = {} + indexed = {} end -- Path could be a directory so just clear all the hashes. - if Loader._hashes then - Loader._hashes = {} + if hashes then + hashes = {} end end @@ -395,11 +412,11 @@ function M.enable() end M.enabled = true vim.fn.mkdir(vim.fn.fnamemodify(M.path, ':p'), 'p') - _G.loadfile = Loader.loadfile + _G.loadfile = loadfile_cached -- add Lua loader - table.insert(loaders, 2, Loader.loader) + table.insert(loaders, 2, loader_cached) -- add libs loader - table.insert(loaders, 3, Loader.loader_lib) + table.insert(loaders, 3, loader_lib_cached) -- remove Nvim loader for l, loader in ipairs(loaders) do if loader == vim._load_package then @@ -419,55 +436,26 @@ function M.disable() return end M.enabled = false - _G.loadfile = Loader._loadfile + _G.loadfile = _loadfile for l, loader in ipairs(loaders) do - if loader == Loader.loader or loader == Loader.loader_lib then + if loader == loader_cached or loader == loader_lib_cached then table.remove(loaders, l) end end table.insert(loaders, 2, vim._load_package) end ---- Return the top-level \`/lua/*` modules for this path ----@param path string path to check for top-level Lua modules ----@private -function Loader.lsmod(path) - if not Loader._indexed[path] then - Loader._indexed[path] = {} - for name, t in fs.dir(path .. '/lua') do - local modpath = path .. '/lua/' .. name - -- HACK: type is not always returned due to a bug in luv - t = t or Loader.get_hash(modpath).type - ---@type string - local topname - local ext = name:sub(-4) - if ext == '.lua' or ext == '.dll' then - topname = name:sub(1, -5) - elseif name:sub(-3) == '.so' then - topname = name:sub(1, -4) - elseif t == 'link' or t == 'directory' then - topname = name - end - if topname then - Loader._indexed[path][topname] = { modpath = modpath, modname = topname } - end - end - end - return Loader._indexed[path] -end - --- Tracks the time spent in a function --- @generic F: function --- @param f F --- @return F ---- @private -function Loader.track(stat, f) +local function track(stat, f) return function(...) local start = vim.uv.hrtime() local r = { f(...) } - Loader._stats[stat] = Loader._stats[stat] or { total = 0, time = 0 } - Loader._stats[stat].total = Loader._stats[stat].total + 1 - Loader._stats[stat].time = Loader._stats[stat].time + uv.hrtime() - start + stats[stat] = stats[stat] or { total = 0, time = 0 } + stats[stat].total = stats[stat].total + 1 + stats[stat].time = stats[stat].time + uv.hrtime() - start return unpack(r, 1, table.maxn(r)) end end @@ -476,28 +464,29 @@ end ---@field loaders? boolean Add profiling to the loaders --- Debug function that wraps all loaders and tracks stats +--- Must be called before vim.loader.enable() ---@private ---@param opts vim.loader._profile.Opts? function M._profile(opts) - Loader.get_rtp = Loader.track('get_rtp', Loader.get_rtp) - Loader.read = Loader.track('read', Loader.read) - Loader.loader = Loader.track('loader', Loader.loader) - Loader.loader_lib = Loader.track('loader_lib', Loader.loader_lib) - Loader.loadfile = Loader.track('loadfile', Loader.loadfile) - M.find = Loader.track('find', M.find) - Loader.lsmod = Loader.track('lsmod', Loader.lsmod) + get_rtp = track('get_rtp', get_rtp) + read_cachefile = track('read', read_cachefile) + loader_cached = track('loader', loader_cached) + loader_lib_cached = track('loader_lib', loader_lib_cached) + loadfile_cached = track('loadfile', loadfile_cached) + M.find = track('find', M.find) + lsmod = track('lsmod', lsmod) if opts and opts.loaders then for l, loader in pairs(loaders) do local loc = debug.getinfo(loader, 'Sn').source:sub(2) - loaders[l] = Loader.track('loader ' .. l .. ': ' .. loc, loader) + loaders[l] = track('loader ' .. l .. ': ' .. loc, loader) end end end --- Prints all cache stats ---@param opts? {print?:boolean} ----@return LoaderStats +---@return vim.loader.Stats ---@private function M._inspect(opts) if opts and opts.print then @@ -505,20 +494,17 @@ function M._inspect(opts) return math.floor(nsec / 1e6 * 1000 + 0.5) / 1000 .. 'ms' end local chunks = {} ---@type string[][] - ---@type string[] - local stats = vim.tbl_keys(Loader._stats) - table.sort(stats) - for _, stat in ipairs(stats) do + for _, stat in vim.spairs(stats) do vim.list_extend(chunks, { { '\n' .. stat .. '\n', 'Title' }, { '* total: ' }, - { tostring(Loader._stats[stat].total) .. '\n', 'Number' }, + { tostring(stat.total) .. '\n', 'Number' }, { '* time: ' }, - { ms(Loader._stats[stat].time) .. '\n', 'Bold' }, + { ms(stat.time) .. '\n', 'Bold' }, { '* avg time: ' }, - { ms(Loader._stats[stat].time / Loader._stats[stat].total) .. '\n', 'Bold' }, + { ms(stat.time / stat.total) .. '\n', 'Bold' }, }) - for k, v in pairs(Loader._stats[stat]) do + for k, v in pairs(stat) do if not vim.list_contains({ 'time', 'total' }, k) then chunks[#chunks + 1] = { '* ' .. k .. ':' .. string.rep(' ', 9 - #k) } chunks[#chunks + 1] = { tostring(v) .. '\n', 'Number' } @@ -527,7 +513,7 @@ function M._inspect(opts) end vim.api.nvim_echo(chunks, true, {}) end - return Loader._stats + return stats end return M -- cgit From 086e598a6ea4df20c093e3c6c83fead51b530aa0 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 31 Oct 2024 14:54:59 +0000 Subject: refactor(loader): use the term stat instead of hash --- runtime/lua/vim/loader.lua | 52 +++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 26 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/loader.lua b/runtime/lua/vim/loader.lua index d22a943762..52bd6926fc 100644 --- a/runtime/lua/vim/loader.lua +++ b/runtime/lua/vim/loader.lua @@ -43,7 +43,7 @@ local M = {} --- @field modname string --- --- The fs_stat of the module path. Won't be returned for `modname="*"` ---- @field stat? uv.uv_fs_t +--- @field stat? uv.fs_stat.result ---@alias vim.loader.Stats table @@ -56,25 +56,25 @@ M.enabled = false ---@type vim.loader.Stats local stats = { find = { total = 0, time = 0, not_found = 0 } } ---- @type table? -local hashes +--- @type table? +local fs_stat_cache ---@type table> local indexed = {} --- @param path string ---- @return vim.loader.CacheHash -local function get_hash(path) - if not hashes then - return uv.fs_stat(path) --[[@as vim.loader.CacheHash]] +--- @return uv.fs_stat.result? +local function fs_stat_cached(path) + if not fs_stat_cache then + return uv.fs_stat(path) end - if not hashes[path] then + if not fs_stat_cache[path] then -- Note we must never save a stat for a non-existent path. -- For non-existent paths fs_stat() will return nil. - hashes[path] = uv.fs_stat(path) + fs_stat_cache[path] = uv.fs_stat(path) end - return hashes[path] + return fs_stat_cache[path] end local function normalize(path) @@ -143,8 +143,8 @@ end local function readfile(path, mode) local f = uv.fs_open(path, 'r', mode) if f then - local hash = assert(uv.fs_fstat(f)) - local data = uv.fs_read(f, hash.size, 0) --[[@as string?]] + local size = assert(uv.fs_fstat(f)).size + local data = uv.fs_read(f, size, 0) --[[@as string?]] uv.fs_close(f) return data end @@ -185,16 +185,16 @@ end ---@param modname string module name ---@return string|function local function loader_cached(modname) - hashes = {} + fs_stat_cache = {} local ret = M.find(modname)[1] if ret then -- Make sure to call the global loadfile so we respect any augmentations done elsewhere. -- E.g. profiling local chunk, err = loadfile(ret.modpath) - hashes = nil + fs_stat_cache = nil return chunk or error(err) end - hashes = nil + fs_stat_cache = nil return ("\n\tcache_loader: module '%s' not found"):format(modname) end @@ -242,11 +242,11 @@ end ---@return function?, string? error_message local function loadfile_cached(filename, mode, env) local modpath = normalize(filename) - local hash = get_hash(modpath) + local stat = fs_stat_cached(modpath) local cname = cache_filename(modpath) - if hash then + if stat then local e_hash, e_chunk = read_cachefile(cname) - if hash_eq(e_hash, hash) and e_chunk then + if hash_eq(e_hash, stat) and e_chunk then -- found in cache and up to date local chunk, err = load(e_chunk, '@' .. modpath, mode, env) if not (err and err:find('cannot load incompatible bytecode', 1, true)) then @@ -256,8 +256,8 @@ local function loadfile_cached(filename, mode, env) end local chunk, err = _loadfile(modpath, mode, env) - if chunk then - write_cachefile(cname, hash, chunk) + if chunk and stat then + write_cachefile(cname, stat, chunk) end return chunk, err end @@ -270,7 +270,7 @@ local function lsmod(path) for name, t in fs.dir(path .. '/lua') do local modpath = path .. '/lua/' .. name -- HACK: type is not always returned due to a bug in luv - t = t or get_hash(modpath).type + t = t or fs_stat_cached(modpath).type ---@type string local topname local ext = name:sub(-4) @@ -345,9 +345,9 @@ function M.find(modname, opts) for _, pattern in ipairs(patterns) do local modpath = path .. pattern stats.find.stat = (stats.find.stat or 0) + 1 - local hash = get_hash(modpath) - if hash then - results[#results + 1] = { modpath = modpath, stat = hash, modname = modname } + local stat = fs_stat_cached(modpath) + if stat then + results[#results + 1] = { modpath = modpath, stat = stat, modname = modname } if not continue() then return end @@ -394,8 +394,8 @@ function M.reset(path) end -- Path could be a directory so just clear all the hashes. - if hashes then - hashes = {} + if fs_stat_cache then + fs_stat_cache = {} end end -- cgit From be04bbf781741255941c58ac3c1f5b0b7eedca32 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 31 Oct 2024 14:57:45 +0000 Subject: refactor(loader): format annotations --- runtime/lua/vim/loader.lua | 86 +++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 43 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/loader.lua b/runtime/lua/vim/loader.lua index 52bd6926fc..0cce0ab21d 100644 --- a/runtime/lua/vim/loader.lua +++ b/runtime/lua/vim/loader.lua @@ -10,8 +10,8 @@ local VERSION = 4 local M = {} ----@alias vim.loader.CacheHash {mtime: {nsec: integer, sec: integer}, size: integer, type?: string} ----@alias vim.loader.CacheEntry {hash:vim.loader.CacheHash, chunk:string} +--- @alias vim.loader.CacheHash {mtime: {nsec: integer, sec: integer}, size: integer, type?: string} +--- @alias vim.loader.CacheEntry {hash:vim.loader.CacheHash, chunk:string} --- @class vim.loader.find.Opts --- @inlinedoc @@ -45,21 +45,21 @@ local M = {} --- The fs_stat of the module path. Won't be returned for `modname="*"` --- @field stat? uv.fs_stat.result ----@alias vim.loader.Stats table +--- @alias vim.loader.Stats table ----@nodoc +--- @private M.path = vim.fn.stdpath('cache') .. '/luac' ----@nodoc +--- @private M.enabled = false ----@type vim.loader.Stats +--- @type vim.loader.Stats local stats = { find = { total = 0, time = 0, not_found = 0 } } --- @type table? local fs_stat_cache ----@type table> +--- @type table> local indexed = {} --- @param path string @@ -113,17 +113,17 @@ local function get_rtp() end --- Returns the cache file name ----@param name string can be a module name, or a file name ----@return string file_name +--- @param name string can be a module name, or a file name +--- @return string file_name local function cache_filename(name) local ret = ('%s/%s'):format(M.path, uri_encode(name, 'rfc2396')) return ret:sub(-4) == '.lua' and (ret .. 'c') or (ret .. '.luac') end --- Saves the cache entry for a given module or file ----@param cname string cache filename ----@param hash vim.loader.CacheHash ----@param chunk function +--- @param cname string cache filename +--- @param hash vim.loader.CacheHash +--- @param chunk function local function write_cachefile(cname, hash, chunk) local f = assert(uv.fs_open(cname, 'w', 438)) local header = { @@ -144,16 +144,16 @@ local function readfile(path, mode) local f = uv.fs_open(path, 'r', mode) if f then local size = assert(uv.fs_fstat(f)).size - local data = uv.fs_read(f, size, 0) --[[@as string?]] + local data = uv.fs_read(f, size, 0) uv.fs_close(f) return data end end --- Loads the cache entry for a given module or file ----@param cname string cache filename ----@return vim.loader.CacheHash? hash ----@return string? chunk +--- @param cname string cache filename +--- @return vim.loader.CacheHash? hash +--- @return string? chunk local function read_cachefile(cname) local data = readfile(cname, 438) if not data then @@ -165,7 +165,7 @@ local function read_cachefile(cname) return end - ---@type integer[]|{[0]:integer} + --- @type integer[]|{[0]:integer} local header = vim.split(data:sub(1, zero - 1), ',') if tonumber(header[1]) ~= VERSION then return @@ -182,8 +182,8 @@ local function read_cachefile(cname) end --- The `package.loaders` loader for Lua files using the cache. ----@param modname string module name ----@return string|function +--- @param modname string module name +--- @return string|function local function loader_cached(modname) fs_stat_cache = {} local ret = M.find(modname)[1] @@ -201,8 +201,8 @@ end local is_win = vim.fn.has('win32') == 1 --- The `package.loaders` loader for libs ----@param modname string module name ----@return string|function +--- @param modname string module name +--- @return string|function local function loader_lib_cached(modname) local ret = M.find(modname, { patterns = { is_win and '.dll' or '.so' } })[1] if not ret then @@ -224,8 +224,8 @@ end --- * file size --- * mtime in seconds --- * mtime in nanoseconds ----@param a? vim.loader.CacheHash ----@param b? vim.loader.CacheHash +--- @param a? vim.loader.CacheHash +--- @param b? vim.loader.CacheHash local function hash_eq(a, b) return a and b @@ -236,10 +236,10 @@ end --- `loadfile` using the cache --- Note this has the mode and env arguments which is supported by LuaJIT and is 5.1 compatible. ----@param filename? string ----@param mode? "b"|"t"|"bt" ----@param env? table ----@return function?, string? error_message +--- @param filename? string +--- @param mode? "b"|"t"|"bt" +--- @param env? table +--- @return function?, string? error_message local function loadfile_cached(filename, mode, env) local modpath = normalize(filename) local stat = fs_stat_cached(modpath) @@ -263,7 +263,7 @@ local function loadfile_cached(filename, mode, env) end --- Return the top-level \`/lua/*` modules for this path ----@param path string path to check for top-level Lua modules +--- @param path string path to check for top-level Lua modules local function lsmod(path) if not indexed[path] then indexed[path] = {} @@ -271,7 +271,7 @@ local function lsmod(path) local modpath = path .. '/lua/' .. name -- HACK: type is not always returned due to a bug in luv t = t or fs_stat_cached(modpath).type - ---@type string + --- @type string local topname local ext = name:sub(-4) if ext == '.lua' or ext == '.dll' then @@ -293,9 +293,9 @@ end --- --- @since 0 --- ----@param modname string Module name, or `"*"` to find the top-level modules instead ----@param opts? vim.loader.find.Opts Options for finding a module: ----@return vim.loader.ModuleInfo[] +--- @param modname string Module name, or `"*"` to find the top-level modules instead +--- @param opts? vim.loader.find.Opts Options for finding a module: +--- @return vim.loader.ModuleInfo[] function M.find(modname, opts) opts = opts or {} @@ -321,7 +321,7 @@ function M.find(modname, opts) patterns[p] = '/lua/' .. basename .. pattern end - ---@type vim.loader.ModuleInfo[] + --- @type vim.loader.ModuleInfo[] local results = {} -- Only continue if we haven't found anything yet or we want to find all @@ -331,7 +331,7 @@ function M.find(modname, opts) -- Checks if the given paths contain the top-level module. -- If so, it tries to find the module path for the given module name. - ---@param paths string[] + --- @param paths string[] local function _find(paths) for _, path in ipairs(paths) do if topmod == '*' then @@ -385,7 +385,7 @@ end --- --- @since 0 --- ----@param path string? path to reset +--- @param path string? path to reset function M.reset(path) if path then indexed[normalize(path)] = nil @@ -460,13 +460,13 @@ local function track(stat, f) end end ----@class (private) vim.loader._profile.Opts ----@field loaders? boolean Add profiling to the loaders +--- @class (private) vim.loader._profile.Opts +--- @field loaders? boolean Add profiling to the loaders --- Debug function that wraps all loaders and tracks stats --- Must be called before vim.loader.enable() ----@private ----@param opts vim.loader._profile.Opts? +--- @private +--- @param opts vim.loader._profile.Opts? function M._profile(opts) get_rtp = track('get_rtp', get_rtp) read_cachefile = track('read', read_cachefile) @@ -485,15 +485,15 @@ function M._profile(opts) end --- Prints all cache stats ----@param opts? {print?:boolean} ----@return vim.loader.Stats ----@private +--- @param opts? {print?:boolean} +--- @return vim.loader.Stats +--- @private function M._inspect(opts) if opts and opts.print then local function ms(nsec) return math.floor(nsec / 1e6 * 1000 + 0.5) / 1000 .. 'ms' end - local chunks = {} ---@type string[][] + local chunks = {} --- @type string[][] for _, stat in vim.spairs(stats) do vim.list_extend(chunks, { { '\n' .. stat .. '\n', 'Title' }, -- cgit From b34e137e43d359c8db4fb76028dea3b410842aff Mon Sep 17 00:00:00 2001 From: errael Date: Thu, 31 Oct 2024 18:11:15 -0700 Subject: feat(lua): allow vim.on_key() callback to consume the key (#30939) --- runtime/lua/vim/_editor.lua | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index c6aa303124..3167721a70 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -651,7 +651,7 @@ do end end -local on_key_cbs = {} --- @type table +local on_key_cbs = {} --- @type table --- Adds Lua function {fn} with namespace id {ns_id} as a listener to every, --- yes every, input key. @@ -664,34 +664,37 @@ local on_key_cbs = {} --- @type table --- it won't be invoked for those keys. ---@note {fn} will not be cleared by |nvim_buf_clear_namespace()| --- ----@param fn fun(key: string, typed: string)? Function invoked for every input key, +---@param fn nil|fun(key: string, typed: string): string? Function invoked for every input key, --- after mappings have been applied but before further processing. Arguments --- {key} and {typed} are raw keycodes, where {key} is the key after mappings --- are applied, and {typed} is the key(s) before mappings are applied. --- {typed} may be empty if {key} is produced by non-typed key(s) or by the --- same typed key(s) that produced a previous {key}. ---- When {fn} is `nil` and {ns_id} is specified, the callback associated with ---- namespace {ns_id} is removed. +--- If {fn} returns an empty string, {key} is discarded/ignored. +--- When {fn} is `nil`, the callback associated with namespace {ns_id} is removed. ---@param ns_id integer? Namespace ID. If nil or 0, generates and returns a --- new |nvim_create_namespace()| id. +---@param opts table? Optional parameters --- ---@see |keytrans()| --- ---@return integer Namespace id associated with {fn}. Or count of all callbacks ---if on_key() is called without arguments. -function vim.on_key(fn, ns_id) +function vim.on_key(fn, ns_id, opts) if fn == nil and ns_id == nil then return vim.tbl_count(on_key_cbs) end vim.validate('fn', fn, 'callable', true) vim.validate('ns_id', ns_id, 'number', true) + vim.validate('opts', opts, 'table', true) + opts = opts or {} if ns_id == nil or ns_id == 0 then ns_id = vim.api.nvim_create_namespace('') end - on_key_cbs[ns_id] = fn + on_key_cbs[ns_id] = fn and { fn, opts } return ns_id end @@ -700,12 +703,23 @@ end function vim._on_key(buf, typed_buf) local failed_ns_ids = {} local failed_messages = {} + local discard = false for k, v in pairs(on_key_cbs) do - local ok, err_msg = pcall(v, buf, typed_buf) + local ok, rv = pcall(v[1], buf, typed_buf) + if ok and rv ~= nil then + if type(rv) == 'string' and #rv == 0 then + discard = true + -- break -- Without break deliver to all callbacks even when it eventually discards. + -- "break" does not make sense unless callbacks are sorted by ???. + else + ok = false + rv = 'return string must be empty' + end + end if not ok then vim.on_key(nil, k) table.insert(failed_ns_ids, k) - table.insert(failed_messages, err_msg) + table.insert(failed_messages, rv) end end @@ -718,6 +732,7 @@ function vim._on_key(buf, typed_buf) ) ) end + return discard end --- Convert UTF-32, UTF-16 or UTF-8 {index} to byte index. -- cgit From f54266dbed6f1a4cb4fad3486a722a25070d7feb Mon Sep 17 00:00:00 2001 From: nikolightsaber <103886134+nikolightsaber@users.noreply.github.com> Date: Fri, 1 Nov 2024 10:03:09 +0100 Subject: fix(lsp): hover border type can be string (#31013) Border type can also be a string as defined in `api-win_config` Co-authored-by: Nikolai Devolder --- runtime/lua/vim/lsp/util.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 41f93e5374..763cd940c3 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1485,7 +1485,7 @@ end --- --- offset to add to `row` --- @field offset_y? integer ---- @field border? (string|[string,string])[] override `border` +--- @field border? string|(string|[string,string])[] override `border` --- @field zindex? integer override `zindex`, defaults to 50 --- @field title? string --- @field title_pos? 'left'|'center'|'right' -- cgit From 9b357e30fdd0a575480182872331fdb87e9cc331 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 29 Oct 2024 09:36:02 +0000 Subject: feat(lsp)!: remove client-server handlers from vim.lsp.handlers - Partition the handlers in vim.lsp.handlers as: - client to server response handlers (RCS) - server to client request handlers (RSC) - server to client notification handlers (NSC) Note use string indexes instead of protocol.methods for improved typing in LuaLS (tip: use hover on RCS, RSC or NSC). --- runtime/lua/vim/lsp.lua | 1 + runtime/lua/vim/lsp/_meta.lua | 4 +- runtime/lua/vim/lsp/buf.lua | 66 +++++++++++++- runtime/lua/vim/lsp/codelens.lua | 2 +- runtime/lua/vim/lsp/diagnostic.lua | 83 +++--------------- runtime/lua/vim/lsp/handlers.lua | 173 ++++++++++++++++++++++--------------- runtime/lua/vim/lsp/inlay_hint.lua | 4 +- runtime/lua/vim/lsp/protocol.lua | 104 +++++++++++++++++++++- 8 files changed, 288 insertions(+), 149 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index a8ae283f64..42a0ccc3d4 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1165,6 +1165,7 @@ function lsp.for_each_buffer_client(bufnr, fn) end end +--- @deprecated --- Function to manage overriding defaults for LSP handlers. ---@param handler (lsp.Handler) See |lsp-handler| ---@param override_config (table) Table containing the keys to override behavior of the {handler} diff --git a/runtime/lua/vim/lsp/_meta.lua b/runtime/lua/vim/lsp/_meta.lua index 589a49c003..bf693ccc57 100644 --- a/runtime/lua/vim/lsp/_meta.lua +++ b/runtime/lua/vim/lsp/_meta.lua @@ -1,8 +1,8 @@ ---@meta error('Cannot require a meta file') ----@alias lsp.Handler fun(err: lsp.ResponseError?, result: any, context: lsp.HandlerContext, config?: table): ...any ----@alias lsp.MultiHandler fun(results: table, context: lsp.HandlerContext, config?: table): ...any +---@alias lsp.Handler fun(err: lsp.ResponseError?, result: any, context: lsp.HandlerContext): ...any +---@alias lsp.MultiHandler fun(results: table, context: lsp.HandlerContext): ...any ---@class lsp.HandlerContext ---@field method string diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 4818359fb3..152226a757 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -258,10 +258,72 @@ function M.implementation(opts) get_locations(ms.textDocument_implementation, opts) end +local sig_help_ns = api.nvim_create_namespace('vim_lsp_signature_help') + +--- @class vim.lsp.buf.signature_help.Opts : vim.lsp.util.open_floating_preview.Opts +--- @field silent? boolean + +-- TODO(lewis6991): support multiple clients --- Displays signature information about the symbol under the cursor in a --- floating window. -function M.signature_help() - lsp.buf_request(0, ms.textDocument_signatureHelp, client_positional_params()) +--- @param config? vim.lsp.buf.signature_help.Opts +function M.signature_help(config) + local method = ms.textDocument_signatureHelp + + config = config or {} + config.focus_id = method + + lsp.buf_request(0, method, client_positional_params(), function(err, result, ctx) + local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) + + if err then + vim.notify( + client.name .. ': ' .. tostring(err.code) .. ': ' .. err.message, + vim.log.levels.ERROR + ) + api.nvim_command('redraw') + return + end + + if api.nvim_get_current_buf() ~= ctx.bufnr then + -- Ignore result since buffer changed. This happens for slow language servers. + return + end + + -- When use `autocmd CompleteDone lua vim.lsp.buf.signature_help()` to call signatureHelp handler + -- If the completion item doesn't have signatures It will make noise. Change to use `print` that can use `` to ignore + if not result or not result.signatures or not result.signatures[1] then + if config.silent ~= true then + print('No signature help available') + end + return + end + + local triggers = + vim.tbl_get(client.server_capabilities, 'signatureHelpProvider', 'triggerCharacters') + + local ft = vim.bo[ctx.bufnr].filetype + local lines, hl = util.convert_signature_help_to_markdown_lines(result, ft, triggers) + if not lines or vim.tbl_isempty(lines) then + if config.silent ~= true then + print('No signature help available') + end + return + end + + local fbuf = util.open_floating_preview(lines, 'markdown', config) + + -- Highlight the active parameter. + if hl then + vim.hl.range( + fbuf, + sig_help_ns, + 'LspSignatureActiveParameter', + { hl[1], hl[2] }, + { hl[3], hl[4] } + ) + end + end) end --- @deprecated diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index d8c6c27a98..fdbdda695a 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -261,7 +261,7 @@ end ---@param err lsp.ResponseError? ---@param result lsp.CodeLens[] ---@param ctx lsp.HandlerContext -function M.on_codelens(err, result, ctx, _) +function M.on_codelens(err, result, ctx) if err then active_refreshes[assert(ctx.bufnr)] = nil log.error('codelens', err) diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index c59e2db901..8fd30c7668 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -9,14 +9,6 @@ local augroup = api.nvim_create_augroup('vim_lsp_diagnostic', {}) local DEFAULT_CLIENT_ID = -1 -local function get_client_id(client_id) - if client_id == nil then - client_id = DEFAULT_CLIENT_ID - end - - return client_id -end - ---@param severity lsp.DiagnosticSeverity local function severity_lsp_to_vim(severity) if type(severity) == 'string' then @@ -218,8 +210,7 @@ end --- @param client_id? integer --- @param diagnostics vim.Diagnostic[] --- @param is_pull boolean ---- @param config? vim.diagnostic.Opts -local function handle_diagnostics(uri, client_id, diagnostics, is_pull, config) +local function handle_diagnostics(uri, client_id, diagnostics, is_pull) local fname = vim.uri_to_fname(uri) if #diagnostics == 0 and vim.fn.bufexists(fname) == 0 then @@ -231,91 +222,39 @@ local function handle_diagnostics(uri, client_id, diagnostics, is_pull, config) return end - client_id = get_client_id(client_id) - local namespace = M.get_namespace(client_id, is_pull) - - if config then - --- @cast config table - for _, opt in pairs(config) do - convert_severity(opt) - end - -- Persist configuration to ensure buffer reloads use the same - -- configuration. To make lsp.with configuration work (See :help - -- lsp-handler-configuration) - vim.diagnostic.config(config, namespace) + if client_id == nil then + client_id = DEFAULT_CLIENT_ID end + local namespace = M.get_namespace(client_id, is_pull) + vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)) end --- |lsp-handler| for the method "textDocument/publishDiagnostics" --- ---- See |vim.diagnostic.config()| for configuration options. Handler-specific ---- configuration can be set using |vim.lsp.with()|: ---- ---- ```lua ---- vim.lsp.handlers["textDocument/publishDiagnostics"] = vim.lsp.with( ---- vim.lsp.diagnostic.on_publish_diagnostics, { ---- -- Enable underline, use default values ---- underline = true, ---- -- Enable virtual text, override spacing to 4 ---- virtual_text = { ---- spacing = 4, ---- }, ---- -- Use a function to dynamically turn signs off ---- -- and on, using buffer local variables ---- signs = function(namespace, bufnr) ---- return vim.b[bufnr].show_signs == true ---- end, ---- -- Disable a feature ---- update_in_insert = false, ---- } ---- ) ---- ``` +--- See |vim.diagnostic.config()| for configuration options. --- ---@param _ lsp.ResponseError? ---@param result lsp.PublishDiagnosticsParams ---@param ctx lsp.HandlerContext ----@param config? vim.diagnostic.Opts Configuration table (see |vim.diagnostic.config()|). -function M.on_publish_diagnostics(_, result, ctx, config) - handle_diagnostics(result.uri, ctx.client_id, result.diagnostics, false, config) +function M.on_publish_diagnostics(_, result, ctx) + handle_diagnostics(result.uri, ctx.client_id, result.diagnostics, false) end --- |lsp-handler| for the method "textDocument/diagnostic" --- ---- See |vim.diagnostic.config()| for configuration options. Handler-specific ---- configuration can be set using |vim.lsp.with()|: ---- ---- ```lua ---- vim.lsp.handlers["textDocument/diagnostic"] = vim.lsp.with( ---- vim.lsp.diagnostic.on_diagnostic, { ---- -- Enable underline, use default values ---- underline = true, ---- -- Enable virtual text, override spacing to 4 ---- virtual_text = { ---- spacing = 4, ---- }, ---- -- Use a function to dynamically turn signs off ---- -- and on, using buffer local variables ---- signs = function(namespace, bufnr) ---- return vim.b[bufnr].show_signs == true ---- end, ---- -- Disable a feature ---- update_in_insert = false, ---- } ---- ) ---- ``` +--- See |vim.diagnostic.config()| for configuration options. --- ---@param _ lsp.ResponseError? ---@param result lsp.DocumentDiagnosticReport ---@param ctx lsp.HandlerContext ----@param config vim.diagnostic.Opts Configuration table (see |vim.diagnostic.config()|). -function M.on_diagnostic(_, result, ctx, config) +function M.on_diagnostic(_, result, ctx) if result == nil or result.kind == 'unchanged' then return end - handle_diagnostics(ctx.params.textDocument.uri, ctx.client_id, result.items, true, config) + handle_diagnostics(ctx.params.textDocument.uri, ctx.client_id, result.items, true) end --- Clear push diagnostics and diagnostic cache. diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index ad827cfd34..2b7aefe0e1 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -5,10 +5,21 @@ local util = require('vim.lsp.util') local api = vim.api local completion = require('vim.lsp.completion') ---- @type table +--- @type table local M = {} --- FIXME: DOC: Expose in vimdocs +--- @deprecated +--- Client to server response handlers. +--- @type table +local RCS = {} + +--- Server to client request handlers. +--- @type table +local RSC = {} + +--- Server to client notification handlers. +--- @type table +local NSC = {} --- Writes to error buffer. ---@param ... string Will be concatenated before being written @@ -18,14 +29,15 @@ local function err_message(...) end --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand -M[ms.workspace_executeCommand] = function(_, _, _, _) +RCS[ms.workspace_executeCommand] = function(_, _, _) -- Error handling is done implicitly by wrapping all handlers; see end of this file end --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#progress ---@param params lsp.ProgressParams ---@param ctx lsp.HandlerContext -M[ms.dollar_progress] = function(_, params, ctx) +---@diagnostic disable-next-line:no-unknown +RSC[ms.dollar_progress] = function(_, params, ctx) local client = vim.lsp.get_client_by_id(ctx.client_id) if not client then err_message('LSP[id=', tostring(ctx.client_id), '] client has shut down during progress update') @@ -59,26 +71,26 @@ M[ms.dollar_progress] = function(_, params, ctx) end --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_workDoneProgress_create ----@param result lsp.WorkDoneProgressCreateParams +---@param params lsp.WorkDoneProgressCreateParams ---@param ctx lsp.HandlerContext -M[ms.window_workDoneProgress_create] = function(_, result, ctx) +RSC[ms.window_workDoneProgress_create] = function(_, params, ctx) local client = vim.lsp.get_client_by_id(ctx.client_id) if not client then err_message('LSP[id=', tostring(ctx.client_id), '] client has shut down during progress update') return vim.NIL end - client.progress:push(result) + client.progress:push(params) return vim.NIL end --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showMessageRequest ----@param result lsp.ShowMessageRequestParams -M[ms.window_showMessageRequest] = function(_, result) - local actions = result.actions or {} +---@param params lsp.ShowMessageRequestParams +RSC[ms.window_showMessageRequest] = function(_, params) + local actions = params.actions or {} local co, is_main = coroutine.running() if co and not is_main then local opts = { - prompt = result.message .. ': ', + prompt = params.message .. ': ', format_item = function(action) return (action.title:gsub('\r\n', '\\r\\n')):gsub('\n', '\\n') end, @@ -92,7 +104,7 @@ M[ms.window_showMessageRequest] = function(_, result) end) return coroutine.yield() else - local option_strings = { result.message, '\nRequest Actions:' } + local option_strings = { params.message, '\nRequest Actions:' } for i, action in ipairs(actions) do local title = action.title:gsub('\r\n', '\\r\\n') title = title:gsub('\n', '\\n') @@ -108,19 +120,19 @@ M[ms.window_showMessageRequest] = function(_, result) end --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_registerCapability ---- @param result lsp.RegistrationParams -M[ms.client_registerCapability] = function(_, result, ctx) +--- @param params lsp.RegistrationParams +RSC[ms.client_registerCapability] = function(_, params, ctx) local client_id = ctx.client_id local client = assert(vim.lsp.get_client_by_id(client_id)) - client.dynamic_capabilities:register(result.registrations) + client.dynamic_capabilities:register(params.registrations) for bufnr, _ in pairs(client.attached_buffers) do vim.lsp._set_defaults(client, bufnr) end ---@type string[] local unsupported = {} - for _, reg in ipairs(result.registrations) do + for _, reg in ipairs(params.registrations) do if reg.method == ms.workspace_didChangeWatchedFiles then vim.lsp._watchfiles.register(reg, ctx) elseif not client.dynamic_capabilities:supports_registration(reg.method) then @@ -139,13 +151,13 @@ M[ms.client_registerCapability] = function(_, result, ctx) end --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_unregisterCapability ---- @param result lsp.UnregistrationParams -M[ms.client_unregisterCapability] = function(_, result, ctx) +--- @param params lsp.UnregistrationParams +RSC[ms.client_unregisterCapability] = function(_, params, ctx) local client_id = ctx.client_id local client = assert(vim.lsp.get_client_by_id(client_id)) - client.dynamic_capabilities:unregister(result.unregisterations) + client.dynamic_capabilities:unregister(params.unregisterations) - for _, unreg in ipairs(result.unregisterations) do + for _, unreg in ipairs(params.unregisterations) do if unreg.method == ms.workspace_didChangeWatchedFiles then vim.lsp._watchfiles.unregister(unreg, ctx) end @@ -153,20 +165,20 @@ M[ms.client_unregisterCapability] = function(_, result, ctx) return vim.NIL end +-- TODO(lewis6991): Do we need to notify other servers? --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit -M[ms.workspace_applyEdit] = function(_, workspace_edit, ctx) +RSC[ms.workspace_applyEdit] = function(_, params, ctx) assert( - workspace_edit, + params, 'workspace/applyEdit must be called with `ApplyWorkspaceEditParams`. Server is violating the specification' ) -- TODO(ashkan) Do something more with label? local client_id = ctx.client_id local client = assert(vim.lsp.get_client_by_id(client_id)) - if workspace_edit.label then - print('Workspace edit', workspace_edit.label) + if params.label then + print('Workspace edit', params.label) end - local status, result = - pcall(util.apply_workspace_edit, workspace_edit.edit, client.offset_encoding) + local status, result = pcall(util.apply_workspace_edit, params.edit, client.offset_encoding) return { applied = status, failureReason = result, @@ -182,8 +194,8 @@ local function lookup_section(table, section) end --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_configuration ---- @param result lsp.ConfigurationParams -M[ms.workspace_configuration] = function(_, result, ctx) +--- @param params lsp.ConfigurationParams +RSC[ms.workspace_configuration] = function(_, params, ctx) local client_id = ctx.client_id local client = vim.lsp.get_client_by_id(client_id) if not client then @@ -194,12 +206,12 @@ M[ms.workspace_configuration] = function(_, result, ctx) ) return end - if not result.items then + if not params.items then return {} end local response = {} - for _, item in ipairs(result.items) do + for _, item in ipairs(params.items) do if item.section then local value = lookup_section(client.settings, item.section) -- For empty sections with no explicit '' key, return settings as is @@ -216,7 +228,7 @@ M[ms.workspace_configuration] = function(_, result, ctx) end --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_workspaceFolders -M[ms.workspace_workspaceFolders] = function(_, _, ctx) +RSC[ms.workspace_workspaceFolders] = function(_, _, ctx) local client_id = ctx.client_id local client = vim.lsp.get_client_by_id(client_id) if not client then @@ -226,19 +238,22 @@ M[ms.workspace_workspaceFolders] = function(_, _, ctx) return client.workspace_folders or vim.NIL end -M[ms.textDocument_publishDiagnostics] = function(...) +NSC[ms.textDocument_publishDiagnostics] = function(...) return vim.lsp.diagnostic.on_publish_diagnostics(...) end -M[ms.textDocument_diagnostic] = function(...) +--- @private +RCS[ms.textDocument_diagnostic] = function(...) return vim.lsp.diagnostic.on_diagnostic(...) end -M[ms.textDocument_codeLens] = function(...) +--- @private +RCS[ms.textDocument_codeLens] = function(...) return vim.lsp.codelens.on_codelens(...) end -M[ms.textDocument_inlayHint] = function(...) +--- @private +RCS[ms.textDocument_inlayHint] = function(...) return vim.lsp.inlay_hint.on_inlayhint(...) end @@ -251,6 +266,7 @@ end ---@param title_fn fun(ctx: lsp.HandlerContext): string Function to call to generate list title ---@return lsp.Handler local function response_to_list(map_result, entity, title_fn) + --- @diagnostic disable-next-line:redundant-parameter return function(_, result, ctx, config) if not result or vim.tbl_isempty(result) then vim.notify('No ' .. entity .. ' found') @@ -274,8 +290,9 @@ local function response_to_list(map_result, entity, title_fn) end end +--- @deprecated remove in 0.13 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol -M[ms.textDocument_documentSymbol] = response_to_list( +RCS[ms.textDocument_documentSymbol] = response_to_list( util.symbols_to_items, 'document symbols', function(ctx) @@ -284,13 +301,15 @@ M[ms.textDocument_documentSymbol] = response_to_list( end ) +--- @deprecated remove in 0.13 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_symbol -M[ms.workspace_symbol] = response_to_list(util.symbols_to_items, 'symbols', function(ctx) +RCS[ms.workspace_symbol] = response_to_list(util.symbols_to_items, 'symbols', function(ctx) return string.format("Symbols matching '%s'", ctx.params.query) end) +--- @deprecated remove in 0.13 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rename -M[ms.textDocument_rename] = function(_, result, ctx, _) +RCS[ms.textDocument_rename] = function(_, result, ctx) if not result then vim.notify("Language server couldn't provide rename result", vim.log.levels.INFO) return @@ -299,8 +318,9 @@ M[ms.textDocument_rename] = function(_, result, ctx, _) util.apply_workspace_edit(result, client.offset_encoding) end +--- @deprecated remove in 0.13 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rangeFormatting -M[ms.textDocument_rangeFormatting] = function(_, result, ctx, _) +RCS[ms.textDocument_rangeFormatting] = function(_, result, ctx) if not result then return end @@ -308,8 +328,9 @@ M[ms.textDocument_rangeFormatting] = function(_, result, ctx, _) util.apply_text_edits(result, ctx.bufnr, client.offset_encoding) end +--- @deprecated remove in 0.13 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting -M[ms.textDocument_formatting] = function(_, result, ctx, _) +RCS[ms.textDocument_formatting] = function(_, result, ctx) if not result then return end @@ -319,7 +340,7 @@ end --- @deprecated remove in 0.13 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion -M[ms.textDocument_completion] = function(_, result, _, _) +RCS[ms.textDocument_completion] = function(_, result, _) if vim.tbl_isempty(result or {}) then return end @@ -355,6 +376,7 @@ end --- - border: (default=nil) --- - Add borders to the floating window --- - See |vim.lsp.util.open_floating_preview()| for more options. +--- @diagnostic disable-next-line:redundant-parameter function M.hover(_, result, ctx, config) config = config or {} config.focus_id = ctx.method @@ -388,10 +410,11 @@ end --- @deprecated remove in 0.13 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover --- @diagnostic disable-next-line: deprecated -M[ms.textDocument_hover] = M.hover +RCS[ms.textDocument_hover] = M.hover local sig_help_ns = api.nvim_create_namespace('vim_lsp_signature_help') +--- @deprecated remove in 0.13 --- |lsp-handler| for the method "textDocument/signatureHelp". --- --- The active parameter is highlighted with |hl-LspSignatureActiveParameter|. @@ -412,6 +435,7 @@ local sig_help_ns = api.nvim_create_namespace('vim_lsp_signature_help') --- - border: (default=nil) --- - Add borders to the floating window --- - See |vim.lsp.util.open_floating_preview()| for more options +--- @diagnostic disable-next-line:redundant-parameter function M.signature_help(_, result, ctx, config) config = config or {} config.focus_id = ctx.method @@ -452,11 +476,14 @@ function M.signature_help(_, result, ctx, config) return fbuf, fwin end +--- @deprecated remove in 0.13 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp -M[ms.textDocument_signatureHelp] = M.signature_help +--- @diagnostic disable-next-line:deprecated +RCS[ms.textDocument_signatureHelp] = M.signature_help +--- @deprecated remove in 0.13 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentHighlight -M[ms.textDocument_documentHighlight] = function(_, result, ctx, _) +RCS[ms.textDocument_documentHighlight] = function(_, result, ctx) if not result then return end @@ -499,11 +526,13 @@ local function make_call_hierarchy_handler(direction) end end +--- @deprecated remove in 0.13 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#callHierarchy_incomingCalls -M[ms.callHierarchy_incomingCalls] = make_call_hierarchy_handler('from') +RCS[ms.callHierarchy_incomingCalls] = make_call_hierarchy_handler('from') +--- @deprecated remove in 0.13 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#callHierarchy_outgoingCalls -M[ms.callHierarchy_outgoingCalls] = make_call_hierarchy_handler('to') +RCS[ms.callHierarchy_outgoingCalls] = make_call_hierarchy_handler('to') --- Displays type hierarchy in the quickfix window. local function make_type_hierarchy_handler() @@ -538,17 +567,19 @@ local function make_type_hierarchy_handler() end end +--- @deprecated remove in 0.13 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#typeHierarchy_incomingCalls -M[ms.typeHierarchy_subtypes] = make_type_hierarchy_handler() +RCS[ms.typeHierarchy_subtypes] = make_type_hierarchy_handler() +--- @deprecated remove in 0.13 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#typeHierarchy_outgoingCalls -M[ms.typeHierarchy_supertypes] = make_type_hierarchy_handler() +RCS[ms.typeHierarchy_supertypes] = make_type_hierarchy_handler() --- @see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_logMessage ---- @param result lsp.LogMessageParams -M[ms.window_logMessage] = function(_, result, ctx, _) - local message_type = result.type - local message = result.message +--- @param params lsp.LogMessageParams +NSC['window/logMessage'] = function(_, params, ctx) + local message_type = params.type + local message = params.message local client_id = ctx.client_id local client = vim.lsp.get_client_by_id(client_id) local client_name = client and client.name or string.format('id=%d', client_id) @@ -564,14 +595,14 @@ M[ms.window_logMessage] = function(_, result, ctx, _) else log.debug(message) end - return result + return params end --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showMessage ---- @param result lsp.ShowMessageParams -M[ms.window_showMessage] = function(_, result, ctx, _) - local message_type = result.type - local message = result.message +--- @param params lsp.ShowMessageParams +NSC['window/showMessage'] = function(_, params, ctx) + local message_type = params.type + local message = params.message local client_id = ctx.client_id local client = vim.lsp.get_client_by_id(client_id) local client_name = client and client.name or string.format('id=%d', client_id) @@ -585,15 +616,16 @@ M[ms.window_showMessage] = function(_, result, ctx, _) local message_type_name = protocol.MessageType[message_type] api.nvim_out_write(string.format('LSP[%s][%s] %s\n', client_name, message_type_name, message)) end - return result + return params end +--- @private --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showDocument ---- @param result lsp.ShowDocumentParams -M[ms.window_showDocument] = function(_, result, ctx, _) - local uri = result.uri +--- @param params lsp.ShowDocumentParams +RSC[ms.window_showDocument] = function(_, params, ctx) + local uri = params.uri - if result.external then + if params.external then -- TODO(lvimuser): ask the user for confirmation local cmd, err = vim.ui.open(uri) local ret = cmd and cmd:wait(2000) or nil @@ -621,35 +653,39 @@ M[ms.window_showDocument] = function(_, result, ctx, _) local location = { uri = uri, - range = result.selection, + range = params.selection, } local success = util.show_document(location, client.offset_encoding, { reuse_win = true, - focus = result.takeFocus, + focus = params.takeFocus, }) return { success = success or false } end ---@see https://microsoft.github.io/language-server-protocol/specification/#workspace_inlayHint_refresh -M[ms.workspace_inlayHint_refresh] = function(err, result, ctx, config) - return vim.lsp.inlay_hint.on_refresh(err, result, ctx, config) +RSC[ms.workspace_inlayHint_refresh] = function(err, result, ctx) + return vim.lsp.inlay_hint.on_refresh(err, result, ctx) end ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#semanticTokens_refreshRequest -M[ms.workspace_semanticTokens_refresh] = function(err, result, ctx, _config) +RSC[ms.workspace_semanticTokens_refresh] = function(err, result, ctx) return vim.lsp.semantic_tokens._refresh(err, result, ctx) end +--- @nodoc +--- @type table +M = vim.tbl_extend('force', M, RSC, NSC, RCS) + -- Add boilerplate error validation and logging for all of these. for k, fn in pairs(M) do + --- @diagnostic disable-next-line:redundant-parameter M[k] = function(err, result, ctx, config) if log.trace() then log.trace('default_handler', ctx.method, { err = err, result = result, ctx = vim.inspect(ctx), - config = config, }) end @@ -670,6 +706,7 @@ for k, fn in pairs(M) do return end + --- @diagnostic disable-next-line:redundant-parameter return fn(err, result, ctx, config) end end diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua index e5892928cf..ca7bc3b022 100644 --- a/runtime/lua/vim/lsp/inlay_hint.lua +++ b/runtime/lua/vim/lsp/inlay_hint.lua @@ -37,7 +37,7 @@ local augroup = api.nvim_create_augroup('vim_lsp_inlayhint', {}) ---@param result lsp.InlayHint[]? ---@param ctx lsp.HandlerContext ---@private -function M.on_inlayhint(err, result, ctx, _) +function M.on_inlayhint(err, result, ctx) if err then log.error('inlayhint', err) return @@ -87,7 +87,7 @@ end --- |lsp-handler| for the method `workspace/inlayHint/refresh` ---@param ctx lsp.HandlerContext ---@private -function M.on_refresh(err, _, ctx, _) +function M.on_refresh(err, _, ctx) if err then return vim.NIL end diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index b299a8438f..98965d0e20 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -618,10 +618,110 @@ function protocol.resolve_capabilities(server_capabilities) return server_capabilities end +-- Generated by gen_lsp.lua, keep at end of file. +--- @alias vim.lsp.protocol.Method.ClientToServer +--- | 'callHierarchy/incomingCalls', +--- | 'callHierarchy/outgoingCalls', +--- | 'codeAction/resolve', +--- | 'codeLens/resolve', +--- | 'completionItem/resolve', +--- | 'documentLink/resolve', +--- | '$/setTrace', +--- | 'exit', +--- | 'initialize', +--- | 'initialized', +--- | 'inlayHint/resolve', +--- | 'notebookDocument/didChange', +--- | 'notebookDocument/didClose', +--- | 'notebookDocument/didOpen', +--- | 'notebookDocument/didSave', +--- | 'shutdown', +--- | 'textDocument/codeAction', +--- | 'textDocument/codeLens', +--- | 'textDocument/colorPresentation', +--- | 'textDocument/completion', +--- | 'textDocument/declaration', +--- | 'textDocument/definition', +--- | 'textDocument/diagnostic', +--- | 'textDocument/didChange', +--- | 'textDocument/didClose', +--- | 'textDocument/didOpen', +--- | 'textDocument/didSave', +--- | 'textDocument/documentColor', +--- | 'textDocument/documentHighlight', +--- | 'textDocument/documentLink', +--- | 'textDocument/documentSymbol', +--- | 'textDocument/foldingRange', +--- | 'textDocument/formatting', +--- | 'textDocument/hover', +--- | 'textDocument/implementation', +--- | 'textDocument/inlayHint', +--- | 'textDocument/inlineCompletion', +--- | 'textDocument/inlineValue', +--- | 'textDocument/linkedEditingRange', +--- | 'textDocument/moniker', +--- | 'textDocument/onTypeFormatting', +--- | 'textDocument/prepareCallHierarchy', +--- | 'textDocument/prepareRename', +--- | 'textDocument/prepareTypeHierarchy', +--- | 'textDocument/rangeFormatting', +--- | 'textDocument/rangesFormatting', +--- | 'textDocument/references', +--- | 'textDocument/rename', +--- | 'textDocument/selectionRange', +--- | 'textDocument/semanticTokens/full', +--- | 'textDocument/semanticTokens/full/delta', +--- | 'textDocument/semanticTokens/range', +--- | 'textDocument/signatureHelp', +--- | 'textDocument/typeDefinition', +--- | 'textDocument/willSave', +--- | 'textDocument/willSaveWaitUntil', +--- | 'typeHierarchy/subtypes', +--- | 'typeHierarchy/supertypes', +--- | 'window/workDoneProgress/cancel', +--- | 'workspaceSymbol/resolve', +--- | 'workspace/diagnostic', +--- | 'workspace/didChangeConfiguration', +--- | 'workspace/didChangeWatchedFiles', +--- | 'workspace/didChangeWorkspaceFolders', +--- | 'workspace/didCreateFiles', +--- | 'workspace/didDeleteFiles', +--- | 'workspace/didRenameFiles', +--- | 'workspace/executeCommand', +--- | 'workspace/symbol', +--- | 'workspace/willCreateFiles', +--- | 'workspace/willDeleteFiles', +--- | 'workspace/willRenameFiles', + +--- @alias vim.lsp.protocol.Method.ServerToClient +--- | 'client/registerCapability', +--- | 'client/unregisterCapability', +--- | '$/logTrace', +--- | 'telemetry/event', +--- | 'textDocument/publishDiagnostics', +--- | 'window/logMessage', +--- | 'window/showDocument', +--- | 'window/showMessage', +--- | 'window/showMessageRequest', +--- | 'window/workDoneProgress/create', +--- | 'workspace/applyEdit', +--- | 'workspace/codeLens/refresh', +--- | 'workspace/configuration', +--- | 'workspace/diagnostic/refresh', +--- | 'workspace/foldingRange/refresh', +--- | 'workspace/inlayHint/refresh', +--- | 'workspace/inlineValue/refresh', +--- | 'workspace/semanticTokens/refresh', +--- | 'workspace/workspaceFolders', + +--- @alias vim.lsp.protocol.Method +--- | vim.lsp.protocol.Method.ClientToServer +--- | vim.lsp.protocol.Method.ServerToClient + -- Generated by gen_lsp.lua, keep at end of file. --- ----@enum vim.lsp.protocol.Methods ----@see https://microsoft.github.io/language-server-protocol/specification/#metaModel +--- @enum vim.lsp.protocol.Methods +--- @see https://microsoft.github.io/language-server-protocol/specification/#metaModel --- LSP method names. protocol.Methods = { --- A request to resolve the incoming calls for a given `CallHierarchyItem`. -- cgit From 7bf3a616e18205d353d9c4a44e8c2f885d700129 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 1 Nov 2024 18:22:08 +0800 Subject: vim-patch:b5e7da1: runtime(doc): mention 'iskeyword' at :h charclass() (#31026) fixes: vim/vim#15965 https://github.com/vim/vim/commit/b5e7da1f27241f7d770d342009e2fb443e45e6ce Co-authored-by: Christian Brabandt --- runtime/lua/vim/_meta/vimfn.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index a751783c8f..7548d1beb2 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -809,7 +809,7 @@ function vim.fn.char2nr(string, utf8) end --- The character class is one of: --- 0 blank --- 1 punctuation ---- 2 word character +--- 2 word character (depends on 'iskeyword') --- 3 emoji --- other specific Unicode class --- The class is used in patterns and word motions. -- cgit From 3688a333544251c887d78e6501eec55f0fb685f8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 2 Nov 2024 10:11:06 +0800 Subject: fix(lua): show stacktrace for error in vim.on_key() callback (#31021) --- runtime/lua/vim/_editor.lua | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 3167721a70..cbab2a0165 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -701,11 +701,13 @@ end --- Executes the on_key callbacks. ---@private function vim._on_key(buf, typed_buf) - local failed_ns_ids = {} - local failed_messages = {} + local failed = {} ---@type [integer, string][] local discard = false for k, v in pairs(on_key_cbs) do - local ok, rv = pcall(v[1], buf, typed_buf) + local fn = v[1] + local ok, rv = xpcall(function() + return fn(buf, typed_buf) + end, debug.traceback) if ok and rv ~= nil then if type(rv) == 'string' and #rv == 0 then discard = true @@ -718,19 +720,16 @@ function vim._on_key(buf, typed_buf) end if not ok then vim.on_key(nil, k) - table.insert(failed_ns_ids, k) - table.insert(failed_messages, rv) + table.insert(failed, { k, rv }) end end - if failed_ns_ids[1] then - error( - string.format( - "Error executing 'on_key' with ns_ids '%s'\n Messages: %s", - table.concat(failed_ns_ids, ', '), - table.concat(failed_messages, '\n') - ) - ) + if #failed > 0 then + local errmsg = '' + for _, v in ipairs(failed) do + errmsg = errmsg .. string.format('\nWith ns_id %d: %s', v[1], v[2]) + end + error(errmsg) end return discard end -- cgit From db46b58569bc518967b73bf7d6ed92f8f5548c90 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 2 Nov 2024 16:09:23 +0100 Subject: vim-patch:9.1.0826: filetype: sway files are not recognized Problem: filetype: sway files are not recognized Solution: detect '*.sw' files as sway filetype, include a filetype plugin (Riley Bruins) References: https://github.com/FuelLabs/sway. Comments taken from their syntax documentation. File extension taken from the same documentation/GitHub's own recognition of these file types closes: vim/vim#15973 https://github.com/vim/vim/commit/84b5b1c660beb2f9e27de70687e41d39a023ae81 Co-authored-by: Riley Bruins --- runtime/lua/vim/filetype.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index f6928c6428..057a99123b 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1151,6 +1151,7 @@ local extension = { sface = 'surface', svelte = 'svelte', svg = 'svg', + sw = 'sway', swift = 'swift', swiftinterface = 'swift', swig = 'swig', -- cgit From 2e3f1069f4991d80bb9fa8f712720fe7500bbd9b Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 2 Nov 2024 10:40:44 +0100 Subject: fix(health): better layout of vim.treesitter health check Problem: Long lists of available parsers make it hard to see WASM status. Solution: Add separate headings for "treesitter features" (ABI, WASM) and "treesitter parsers". Also add minimum supported ABI version. --- runtime/lua/vim/treesitter/health.lua | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/health.lua b/runtime/lua/vim/treesitter/health.lua index 637f9ea543..53b64d1dec 100644 --- a/runtime/lua/vim/treesitter/health.lua +++ b/runtime/lua/vim/treesitter/health.lua @@ -4,10 +4,21 @@ local health = vim.health --- Performs a healthcheck for treesitter integration function M.check() - local parsers = vim.api.nvim_get_runtime_file('parser/*', true) + health.start('Treesitter features') + + health.info( + string.format( + 'Treesitter ABI support: min %d, max %d', + vim.treesitter.minimum_language_version, + ts.language_version + ) + ) - health.info(string.format('Nvim runtime ABI version: %d', ts.language_version)) + local can_wasm = vim._ts_add_language_from_wasm ~= nil + health.info(string.format('WASM parser support: %s', tostring(can_wasm))) + health.start('Treesitter parsers') + local parsers = vim.api.nvim_get_runtime_file('parser/*', true) for _, parser in pairs(parsers) do local parsername = vim.fn.fnamemodify(parser, ':t:r') local is_loadable, err_or_nil = pcall(ts.language.add, parsername) @@ -28,9 +39,6 @@ function M.check() ) end end - - local can_wasm = vim._ts_add_language_from_wasm ~= nil - health.info(string.format('Can load WASM parsers: %s', tostring(can_wasm))) end return M -- cgit From 3075c69ff02faf396e5efbdcb4a255b0b0309649 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 3 Nov 2024 10:06:41 +0800 Subject: vim-patch:9.1.0831: 'findexpr' can't be used as lambad or Funcref (#31058) Problem: 'findexpr' can't be used for lambads (Justin Keyes) Solution: Replace the findexpr option with the findfunc option (Yegappan Lakshmanan) related: vim/vim#15905 closes: vim/vim#15976 https://github.com/vim/vim/commit/a13f3a4f5de9c150f70298850e34747838904995 Co-authored-by: Yegappan Lakshmanan --- runtime/lua/vim/_meta/options.lua | 52 +++++++++++++++++++-------------------- runtime/lua/vim/_meta/vvars.lua | 8 +----- 2 files changed, 27 insertions(+), 33 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index 5e5b6b5ed1..f9886957a7 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -2294,31 +2294,31 @@ vim.wo.fcs = vim.wo.fillchars vim.go.fillchars = vim.o.fillchars vim.go.fcs = vim.go.fillchars ---- Expression that is evaluated to obtain the filename(s) for the `:find` +--- Function that is called to obtain the filename(s) for the `:find` --- command. When this option is empty, the internal `file-searching` --- mechanism is used. --- ---- While evaluating the expression, the `v:fname` variable is set to the ---- argument of the `:find` command. +--- The value can be the name of a function, a `lambda` or a `Funcref`. +--- See `option-value-function` for more information. --- ---- The expression is evaluated only once per `:find` command invocation. ---- The expression can process all the directories specified in 'path'. +--- The function is called with two arguments. The first argument is a +--- `String` and is the `:find` command argument. The second argument is +--- a `Boolean` and is set to `v:true` when the function is called to get +--- a List of command-line completion matches for the `:find` command. +--- The function should return a List of strings. --- ---- The expression may be evaluated for command-line completion as well, ---- in which case the `v:cmdcomplete` variable will be set to `v:true`, ---- otherwise it will be set to `v:false`. +--- The function is called only once per `:find` command invocation. +--- The function can process all the directories specified in 'path'. --- ---- If a match is found, the expression should return a `List` containing ---- one or more file names. If a match is not found, the expression +--- If a match is found, the function should return a `List` containing +--- one or more file names. If a match is not found, the function --- should return an empty List. --- ---- If any errors are encountered during the expression evaluation, an +--- If any errors are encountered during the function invocation, an --- empty List is used as the return value. --- ---- Using a function call without arguments is faster `expr-option-function` ---- --- It is not allowed to change text or jump to another window while ---- evaluating 'findexpr' `textlock`. +--- executing the 'findfunc' `textlock`. --- --- This option cannot be set from a `modeline` or in the `sandbox`, for --- security reasons. @@ -2327,28 +2327,28 @@ vim.go.fcs = vim.go.fillchars --- --- ```vim --- " Use glob() ---- func FindExprGlob() ---- let pat = v:cmdcomplete ? $'{v:fname}*' : v:fname +--- func FindFuncGlob(cmdarg, cmdcomplete) +--- let pat = a:cmdcomplete ? $'{a:cmdarg}*' : a:cmdarg --- return glob(pat, v:false, v:true) --- endfunc ---- set findexpr=FindExprGlob() +--- set findfunc=FindFuncGlob --- --- " Use the 'git ls-files' output ---- func FindGitFiles() +--- func FindGitFiles(cmdarg, cmdcomplete) --- let fnames = systemlist('git ls-files') ---- return fnames->filter('v:val =~? v:fname') +--- return fnames->filter('v:val =~? a:cmdarg') --- endfunc ---- set findexpr=FindGitFiles() +--- set findfunc=FindGitFiles --- ``` --- --- --- @type string -vim.o.findexpr = "" -vim.o.fexpr = vim.o.findexpr -vim.bo.findexpr = vim.o.findexpr -vim.bo.fexpr = vim.bo.findexpr -vim.go.findexpr = vim.o.findexpr -vim.go.fexpr = vim.go.findexpr +vim.o.findfunc = "" +vim.o.ffu = vim.o.findfunc +vim.bo.findfunc = vim.o.findfunc +vim.bo.ffu = vim.bo.findfunc +vim.go.findfunc = vim.o.findfunc +vim.go.ffu = vim.go.findfunc --- When writing a file and this option is on, at the end of file --- will be restored if missing. Turn this option off if you want to diff --git a/runtime/lua/vim/_meta/vvars.lua b/runtime/lua/vim/_meta/vvars.lua index b104356334..e00402ab3f 100644 --- a/runtime/lua/vim/_meta/vvars.lua +++ b/runtime/lua/vim/_meta/vvars.lua @@ -44,11 +44,6 @@ vim.v.cmdarg = ... --- @type integer vim.v.cmdbang = ... ---- When evaluating 'findexpr': if 'findexpr' is used for cmdline ---- completion the value is `v:true`, otherwise it is `v:false`. ---- @type boolean -vim.v.cmdcomplete = ... - --- The current locale setting for collation order of the runtime --- environment. This allows Vim scripts to be aware of the --- current locale encoding. Technical: it's the value of @@ -272,8 +267,7 @@ vim.v.fcs_choice = ... vim.v.fcs_reason = ... --- When evaluating 'includeexpr': the file name that was ---- detected. When evaluating 'findexpr': the argument passed to ---- the `:find` command. Empty otherwise. +--- detected. Empty otherwise. --- @type string vim.v.fname = ... -- cgit From 99acc9de559a2ea3b9de2b418dbcae774d1d9a75 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 3 Nov 2024 11:28:31 +0800 Subject: fix(treesitter): close InspectTree/EditQuery window on BufUnload (#31036) Problem: The window opened :InspectTree or :EditQuery isn't closed when the source buffer is unloaded, even though it is closed when the buffer is hidden. Solution: Also close the window on BufUnload. --- runtime/lua/vim/treesitter/dev.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/dev.lua b/runtime/lua/vim/treesitter/dev.lua index 7b522ca92b..d68a30cc11 100644 --- a/runtime/lua/vim/treesitter/dev.lua +++ b/runtime/lua/vim/treesitter/dev.lua @@ -527,7 +527,7 @@ function M.inspect_tree(opts) end, }) - api.nvim_create_autocmd('BufHidden', { + api.nvim_create_autocmd({ 'BufHidden', 'BufUnload' }, { group = group, buffer = buf, once = true, @@ -665,10 +665,10 @@ function M.edit_query(lang) api.nvim_buf_clear_namespace(query_buf, edit_ns, 0, -1) end, }) - api.nvim_create_autocmd('BufHidden', { + api.nvim_create_autocmd({ 'BufHidden', 'BufUnload' }, { group = group, buffer = buf, - desc = 'Close the editor window when the source buffer is hidden', + desc = 'Close the editor window when the source buffer is hidden or unloaded', once = true, callback = function() close_win(query_win) -- cgit From 6e68fed37441096bf9fd2aa27b9bf6e7d7eae550 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Fri, 1 Nov 2024 12:30:36 +0000 Subject: feat(lsp): multi-client support for signature_help Signatures can be cycled using `` when the user enters the floating window. --- runtime/lua/vim/lsp/buf.lua | 116 +++++++++++++++++++++++++++----------- runtime/lua/vim/lsp/util.lua | 131 +++++++++++++++++++++++++------------------ 2 files changed, 159 insertions(+), 88 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 152226a757..6d7597c5ff 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -258,6 +258,33 @@ function M.implementation(opts) get_locations(ms.textDocument_implementation, opts) end +--- @param results table +local function process_signature_help_results(results) + local signatures = {} --- @type [vim.lsp.Client,lsp.SignatureInformation][] + + -- Pre-process results + for client_id, r in pairs(results) do + local err = r.err + local client = assert(lsp.get_client_by_id(client_id)) + if err then + vim.notify( + client.name .. ': ' .. tostring(err.code) .. ': ' .. err.message, + vim.log.levels.ERROR + ) + api.nvim_command('redraw') + else + local result = r.result --- @type lsp.SignatureHelp + if result and result.signatures and result.signatures[1] then + for _, sig in ipairs(result.signatures) do + signatures[#signatures + 1] = { client, sig } + end + end + end + end + + return signatures +end + local sig_help_ns = api.nvim_create_namespace('vim_lsp_signature_help') --- @class vim.lsp.buf.signature_help.Opts : vim.lsp.util.open_floating_preview.Opts @@ -270,58 +297,79 @@ local sig_help_ns = api.nvim_create_namespace('vim_lsp_signature_help') function M.signature_help(config) local method = ms.textDocument_signatureHelp - config = config or {} + config = config and vim.deepcopy(config) or {} config.focus_id = method - lsp.buf_request(0, method, client_positional_params(), function(err, result, ctx) - local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) - - if err then - vim.notify( - client.name .. ': ' .. tostring(err.code) .. ': ' .. err.message, - vim.log.levels.ERROR - ) - api.nvim_command('redraw') - return - end - + lsp.buf_request_all(0, method, client_positional_params(), function(results, ctx) if api.nvim_get_current_buf() ~= ctx.bufnr then -- Ignore result since buffer changed. This happens for slow language servers. return end - -- When use `autocmd CompleteDone lua vim.lsp.buf.signature_help()` to call signatureHelp handler - -- If the completion item doesn't have signatures It will make noise. Change to use `print` that can use `` to ignore - if not result or not result.signatures or not result.signatures[1] then + local signatures = process_signature_help_results(results) + + if not next(signatures) then if config.silent ~= true then print('No signature help available') end return end - local triggers = - vim.tbl_get(client.server_capabilities, 'signatureHelpProvider', 'triggerCharacters') - local ft = vim.bo[ctx.bufnr].filetype - local lines, hl = util.convert_signature_help_to_markdown_lines(result, ft, triggers) - if not lines or vim.tbl_isempty(lines) then - if config.silent ~= true then - print('No signature help available') + local total = #signatures + local idx = 0 + + --- @param update_win? integer + local function show_signature(update_win) + idx = (idx % total) + 1 + local client, result = signatures[idx][1], signatures[idx][2] + --- @type string[]? + local triggers = + vim.tbl_get(client.server_capabilities, 'signatureHelpProvider', 'triggerCharacters') + local lines, hl = + util.convert_signature_help_to_markdown_lines({ signatures = { result } }, ft, triggers) + if not lines then + return end - return + + local sfx = total > 1 and string.format(' (%d/%d) ( to cycle)', idx, total) or '' + local title = string.format('Signature Help: %s%s', client.name, sfx) + if config.border then + config.title = title + else + table.insert(lines, 1, '# ' .. title) + if hl then + hl[1] = hl[1] + 1 + hl[3] = hl[3] + 1 + end + end + + config._update_win = update_win + + local buf, win = util.open_floating_preview(lines, 'markdown', config) + + if hl then + vim.api.nvim_buf_clear_namespace(buf, sig_help_ns, 0, -1) + vim.hl.range( + buf, + sig_help_ns, + 'LspSignatureActiveParameter', + { hl[1], hl[2] }, + { hl[3], hl[4] } + ) + end + return buf, win end - local fbuf = util.open_floating_preview(lines, 'markdown', config) + local fbuf, fwin = show_signature() - -- Highlight the active parameter. - if hl then - vim.hl.range( - fbuf, - sig_help_ns, - 'LspSignatureActiveParameter', - { hl[1], hl[2] }, - { hl[3], hl[4] } - ) + if total > 1 then + vim.keymap.set('n', '', function() + show_signature(fwin) + end, { + buffer = fbuf, + desc = 'Cycle next signature', + }) end end) end diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 763cd940c3..4ca4239127 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -737,7 +737,7 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers if active_signature >= #signature_help.signatures or active_signature < 0 then active_signature = 0 end - local signature = signature_help.signatures[active_signature + 1] + local signature = vim.deepcopy(signature_help.signatures[active_signature + 1]) local label = signature.label if ft then -- wrap inside a code block for proper rendering @@ -804,9 +804,11 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers active_offset[2] = active_offset[2] + #contents[1] end - active_hl = {} - list_extend(active_hl, get_pos_from_offset(active_offset[1], contents) or {}) - list_extend(active_hl, get_pos_from_offset(active_offset[2], contents) or {}) + local a_start = get_pos_from_offset(active_offset[1], contents) + local a_end = get_pos_from_offset(active_offset[2], contents) + if a_start and a_end then + active_hl = { a_start[1], a_start[2], a_end[1], a_end[2] } + end end return contents, active_hl @@ -818,7 +820,7 @@ end ---@param width integer window width (in character cells) ---@param height integer window height (in character cells) ---@param opts? vim.lsp.util.open_floating_preview.Opts ----@return table Options +---@return vim.api.keyset.win_config function M.make_floating_popup_options(width, height, opts) validate('opts', opts, 'table', true) opts = opts or {} @@ -1500,6 +1502,8 @@ end --- to display the full window height. --- (default: `'auto'`) --- @field anchor_bias? 'auto'|'above'|'below' +--- +--- @field _update_win? integer --- Shows contents in a floating window. --- @@ -1521,43 +1525,49 @@ function M.open_floating_preview(contents, syntax, opts) local bufnr = api.nvim_get_current_buf() - -- check if this popup is focusable and we need to focus - if opts.focus_id and opts.focusable ~= false and opts.focus then - -- Go back to previous window if we are in a focusable one - local current_winnr = api.nvim_get_current_win() - if vim.w[current_winnr][opts.focus_id] then - api.nvim_command('wincmd p') - return bufnr, current_winnr - end - do - local win = find_window_by_var(opts.focus_id, bufnr) - if win and api.nvim_win_is_valid(win) and vim.fn.pumvisible() == 0 then - -- focus and return the existing buf, win - api.nvim_set_current_win(win) - api.nvim_command('stopinsert') - return api.nvim_win_get_buf(win), win + local floating_winnr = opts._update_win + + -- Create/get the buffer + local floating_bufnr --- @type integer + if floating_winnr then + floating_bufnr = api.nvim_win_get_buf(floating_winnr) + else + -- check if this popup is focusable and we need to focus + if opts.focus_id and opts.focusable ~= false and opts.focus then + -- Go back to previous window if we are in a focusable one + local current_winnr = api.nvim_get_current_win() + if vim.w[current_winnr][opts.focus_id] then + api.nvim_command('wincmd p') + return bufnr, current_winnr + end + do + local win = find_window_by_var(opts.focus_id, bufnr) + if win and api.nvim_win_is_valid(win) and vim.fn.pumvisible() == 0 then + -- focus and return the existing buf, win + api.nvim_set_current_win(win) + api.nvim_command('stopinsert') + return api.nvim_win_get_buf(win), win + end end end - end - -- check if another floating preview already exists for this buffer - -- and close it if needed - local existing_float = vim.b[bufnr].lsp_floating_preview - if existing_float and api.nvim_win_is_valid(existing_float) then - api.nvim_win_close(existing_float, true) + -- check if another floating preview already exists for this buffer + -- and close it if needed + local existing_float = vim.b[bufnr].lsp_floating_preview + if existing_float and api.nvim_win_is_valid(existing_float) then + api.nvim_win_close(existing_float, true) + end + floating_bufnr = api.nvim_create_buf(false, true) end - -- Create the buffer - local floating_bufnr = api.nvim_create_buf(false, true) - -- Set up the contents, using treesitter for markdown local do_stylize = syntax == 'markdown' and vim.g.syntax_on ~= nil + if do_stylize then local width = M._make_floating_popup_size(contents, opts) contents = M._normalize_markdown(contents, { width = width }) vim.bo[floating_bufnr].filetype = 'markdown' vim.treesitter.start(floating_bufnr) - api.nvim_buf_set_lines(floating_bufnr, 0, -1, false, contents) else -- Clean up input: trim empty lines contents = vim.split(table.concat(contents, '\n'), '\n', { trimempty = true }) @@ -1565,19 +1575,47 @@ function M.open_floating_preview(contents, syntax, opts) if syntax then vim.bo[floating_bufnr].syntax = syntax end - api.nvim_buf_set_lines(floating_bufnr, 0, -1, true, contents) end - -- Compute size of float needed to show (wrapped) lines - if opts.wrap then - opts.wrap_at = opts.wrap_at or api.nvim_win_get_width(0) + vim.bo[floating_bufnr].modifiable = true + api.nvim_buf_set_lines(floating_bufnr, 0, -1, false, contents) + + if floating_winnr then + api.nvim_win_set_config(floating_winnr, { + border = opts.border, + title = opts.title, + }) else - opts.wrap_at = nil - end - local width, height = M._make_floating_popup_size(contents, opts) + -- Compute size of float needed to show (wrapped) lines + if opts.wrap then + opts.wrap_at = opts.wrap_at or api.nvim_win_get_width(0) + else + opts.wrap_at = nil + end + + -- TODO(lewis6991): These function assume the current window to determine options, + -- therefore it won't work for opts._update_win and the current window if the floating + -- window + local width, height = M._make_floating_popup_size(contents, opts) + local float_option = M.make_floating_popup_options(width, height, opts) - local float_option = M.make_floating_popup_options(width, height, opts) - local floating_winnr = api.nvim_open_win(floating_bufnr, false, float_option) + floating_winnr = api.nvim_open_win(floating_bufnr, false, float_option) + + api.nvim_buf_set_keymap( + floating_bufnr, + 'n', + 'q', + 'bdelete', + { silent = true, noremap = true, nowait = true } + ) + close_preview_autocmd(opts.close_events, floating_winnr, { floating_bufnr, bufnr }) + + -- save focus_id + if opts.focus_id then + api.nvim_win_set_var(floating_winnr, opts.focus_id, bufnr) + end + api.nvim_buf_set_var(bufnr, 'lsp_floating_preview', floating_winnr) + end if do_stylize then vim.wo[floating_winnr].conceallevel = 2 @@ -1590,21 +1628,6 @@ function M.open_floating_preview(contents, syntax, opts) vim.bo[floating_bufnr].modifiable = false vim.bo[floating_bufnr].bufhidden = 'wipe' - api.nvim_buf_set_keymap( - floating_bufnr, - 'n', - 'q', - 'bdelete', - { silent = true, noremap = true, nowait = true } - ) - close_preview_autocmd(opts.close_events, floating_winnr, { floating_bufnr, bufnr }) - - -- save focus_id - if opts.focus_id then - api.nvim_win_set_var(floating_winnr, opts.focus_id, bufnr) - end - api.nvim_buf_set_var(bufnr, 'lsp_floating_preview', floating_winnr) - return floating_bufnr, floating_winnr end -- cgit From a27419f3fc540f66567f4559a796cd6758f1bb1f Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Mon, 4 Nov 2024 19:00:12 +0600 Subject: feat(options)!: disallow setting hidden options #28400 Problem: There are three different ways of marking an option as hidden, `enable_if = false`, `hidden = true` and `immutable = true`. These also have different behaviors. Options hidden with `enable_if = false` can't have their value fetched using Vim script or the API, but options hidden with `hidden = true` or `immutable = true` can. On the other hand, options with `hidden = true` do not error when trying to set their value, but options with `immutable = true` do. Solution: Remove `enable_if = false`, remove the `hidden` property for options, and use `immutable = true` to mark an option as hidden instead. Also make hidden option variable pointers always point to the default value, which allows fetching the value of every hidden option using Vim script and the API. This does also mean that trying to set a hidden option will now give an error instead of just being ignored. --- runtime/lua/vim/_meta/options.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index f9886957a7..45ab14a774 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -1106,7 +1106,7 @@ vim.bo.cot = vim.bo.completeopt vim.go.completeopt = vim.o.completeopt vim.go.cot = vim.go.completeopt ---- only for MS-Windows +--- only modifiable in MS-Windows --- When this option is set it overrules 'shellslash' for completion: --- - When this option is set to "slash", a forward slash is used for path --- completion in insert mode. This is useful when editing HTML tag, or @@ -5547,7 +5547,7 @@ vim.o.srr = vim.o.shellredir vim.go.shellredir = vim.o.shellredir vim.go.srr = vim.go.shellredir ---- only for MS-Windows +--- only modifiable in MS-Windows --- When set, a forward slash is used when expanding file names. This is --- useful when a Unix-like shell is used instead of cmd.exe. Backward --- slashes can still be typed, but they are changed to forward slashes by @@ -5564,7 +5564,7 @@ vim.go.srr = vim.go.shellredir --- Also see 'completeslash'. --- --- @type boolean -vim.o.shellslash = false +vim.o.shellslash = true vim.o.ssl = vim.o.shellslash vim.go.shellslash = vim.o.shellslash vim.go.ssl = vim.go.shellslash -- cgit From 079e5f4f9b67a5aa2c1b481ce78711bf8c76caea Mon Sep 17 00:00:00 2001 From: Yochem van Rosmalen Date: Mon, 4 Nov 2024 15:06:18 +0100 Subject: feat(defaults): unimpaired empty line below/above cursor #30984 --- runtime/lua/vim/_defaults.lua | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua index d3b7bda871..f399360f1e 100644 --- a/runtime/lua/vim/_defaults.lua +++ b/runtime/lua/vim/_defaults.lua @@ -363,6 +363,19 @@ do cmd({ cmd = 'blast' }) end end, { desc = ':blast' }) + + -- Add empty lines + vim.keymap.set('n', '[', function() + local repeated = vim.fn['repeat']({ '' }, vim.v.count1) + local linenr = vim.api.nvim_win_get_cursor(0)[1] + vim.api.nvim_buf_set_lines(0, linenr - 1, linenr - 1, true, repeated) + end, { desc = 'Add empty line above cursor' }) + + vim.keymap.set('n', ']', function() + local repeated = vim.fn['repeat']({ '' }, vim.v.count1) + local linenr = vim.api.nvim_win_get_cursor(0)[1] + vim.api.nvim_buf_set_lines(0, linenr, linenr, true, repeated) + end, { desc = 'Add empty line below cursor' }) end end -- cgit From 65b9499e4a761e4e76e0e1e60cb507c4d6232bd9 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Mon, 4 Nov 2024 21:52:04 +0100 Subject: vim-patch:9.1.0840: filetype: idris2 files are not recognized Problem: filetype: idris2 files are not recognized Solution: detect '*.idr' files as idris2, '*.lidr' files as lidris2 and '*.ipkg' files as ipkg filetype (Serhii Khoma) closes: vim/vim#15987 https://github.com/vim/vim/commit/c04bc64ba61f2386fafb086b47f16f122a0c779a Co-authored-by: Serhii Khoma --- runtime/lua/vim/filetype.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 057a99123b..588a07b5d7 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -588,6 +588,7 @@ local extension = { ibi = 'ibasic', icn = 'icon', idl = detect.idl, + idr = 'idris2', inc = detect.inc, inf = 'inform', INF = 'inform', @@ -595,6 +596,7 @@ local extension = { inko = 'inko', inp = detect.inp, ms = detect_seq(detect.nroff, 'xmath'), + ipkg = 'ipkg', iss = 'iss', mst = 'ist', ist = 'ist', @@ -674,6 +676,7 @@ local extension = { ['l++'] = 'lex', l = 'lex', lhs = 'lhaskell', + lidr = 'lidris2', ll = 'lifelines', ly = 'lilypond', ily = 'lilypond', -- cgit From ba40a6dbe3a1c98a9f75e56c7cd08ecd522d27dd Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Mon, 4 Nov 2024 20:03:10 +0100 Subject: vim-patch:9.1.0839: filetype: leo files are not recognized Problem: filetype: leo files are not recognized Solution: detect '*.leo' files as leo filetype, include a filetype plugin (Riley Bruins) References: https://github.com/ProvableHQ/leo closes: vim/vim#15988 https://github.com/vim/vim/commit/93f65a4ab8168c766e4d3794607762b52762ef82 Co-authored-by: Riley Bruins --- runtime/lua/vim/filetype.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 588a07b5d7..6caf25c588 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -670,6 +670,7 @@ local extension = { journal = 'ledger', ldg = 'ledger', ledger = 'ledger', + leo = 'leo', less = 'less', lex = 'lex', lxx = 'lex', -- cgit From 7342e6b00d5e9f67fd5ad4d3fadaf7e501598486 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Fri, 8 Nov 2024 11:46:53 +0000 Subject: perf(lsp): do not apply semantic tokens to folded lines Fixes #31106 --- runtime/lua/vim/lsp/semantic_tokens.lua | 63 ++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 21 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index d680522592..839ec8bea3 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -380,6 +380,20 @@ function STHighlighter:process_response(response, client, version) api.nvim__redraw({ buf = self.bufnr, valid = true }) end +--- @param bufnr integer +--- @param ns integer +--- @param token STTokenRange +--- @param hl_group string +--- @param priority integer +local function set_mark(bufnr, ns, token, hl_group, priority) + vim.api.nvim_buf_set_extmark(bufnr, ns, token.line, token.start_col, { + hl_group = hl_group, + end_col = token.end_col, + priority = priority, + strict = false, + }) +end + --- on_win handler for the decoration provider (see |nvim_set_decoration_provider|) --- --- If there is a current result for the buffer and the version matches the @@ -433,13 +447,14 @@ function STHighlighter:on_win(topline, botline) -- finishes, clangd sends a refresh request which lets the client -- re-synchronize the tokens. - local set_mark = function(token, hl_group, delta) - vim.api.nvim_buf_set_extmark(self.bufnr, state.namespace, token.line, token.start_col, { - hl_group = hl_group, - end_col = token.end_col, - priority = vim.hl.priorities.semantic_tokens + delta, - strict = false, - }) + local function set_mark0(token, hl_group, delta) + set_mark( + self.bufnr, + state.namespace, + token, + hl_group, + vim.hl.priorities.semantic_tokens + delta + ) end local ft = vim.bo[self.bufnr].filetype @@ -447,13 +462,19 @@ function STHighlighter:on_win(topline, botline) local first = lower_bound(highlights, topline, 1, #highlights + 1) local last = upper_bound(highlights, botline, first, #highlights + 1) - 1 - for i = first, last do + local i = first + while i <= last do local token = highlights[i] - if not token.marked then - set_mark(token, string.format('@lsp.type.%s.%s', token.type, ft), 0) - for modifier, _ in pairs(token.modifiers) do - set_mark(token, string.format('@lsp.mod.%s.%s', modifier, ft), 1) - set_mark(token, string.format('@lsp.typemod.%s.%s.%s', token.type, modifier, ft), 2) + local folded = vim.fn.foldclosed(token.line + 1) + + -- Do not highlight folded lines + local can_mark = folded == -1 or folded == token.line + 1 + + if can_mark and not token.marked then + set_mark0(token, string.format('@lsp.type.%s.%s', token.type, ft), 0) + for modifier in pairs(token.modifiers) do + set_mark0(token, string.format('@lsp.mod.%s.%s', modifier, ft), 1) + set_mark0(token, string.format('@lsp.typemod.%s.%s.%s', token.type, modifier, ft), 2) end token.marked = true @@ -466,6 +487,12 @@ function STHighlighter:on_win(topline, botline) }, }) end + + if folded ~= -1 then + i = math.min(last, vim.fn.foldclosedend(token.line + 1) - 1) + else + i = i + 1 + end end end end @@ -745,15 +772,9 @@ function M.highlight_token(token, bufnr, client_id, hl_group, opts) return end - opts = opts or {} - local priority = opts.priority or vim.hl.priorities.semantic_tokens + 3 + local priority = opts and opts.priority or vim.hl.priorities.semantic_tokens + 3 - vim.api.nvim_buf_set_extmark(bufnr, state.namespace, token.line, token.start_col, { - hl_group = hl_group, - end_col = token.end_col, - priority = priority, - strict = false, - }) + set_mark(bufnr, state.namespace, token, hl_group, priority) end --- |lsp-handler| for the method `workspace/semanticTokens/refresh` -- cgit From ad3472e291694b6c589d8a664459b03962eaac95 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 7 Nov 2024 16:21:49 +0000 Subject: fix(vim.system): resolve executable paths on windows Fixes #31107 --- runtime/lua/vim/_system.lua | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_system.lua b/runtime/lua/vim/_system.lua index 9e27b4c152..ce5dbffeaa 100644 --- a/runtime/lua/vim/_system.lua +++ b/runtime/lua/vim/_system.lua @@ -230,6 +230,8 @@ local function default_handler(stream, text, bucket) end end +local is_win = vim.fn.has('win32') == 1 + local M = {} --- @param cmd string @@ -238,6 +240,13 @@ local M = {} --- @param on_error fun() --- @return uv.uv_process_t, integer local function spawn(cmd, opts, on_exit, on_error) + if is_win then + local cmd1 = vim.fn.exepath(cmd) + if cmd1 ~= '' then + cmd = cmd1 + end + end + local handle, pid_or_err = uv.spawn(cmd, opts, on_exit) if not handle then on_error() -- cgit From 5a27d02584656f35d757c01f10a1d3c88910c519 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sat, 9 Nov 2024 01:10:56 +0100 Subject: docs: misc (#30914) Co-authored-by: Ernie Rael Co-authored-by: Famiu Haque Co-authored-by: Jade Co-authored-by: glepnir Co-authored-by: zeertzjq --- runtime/lua/vim/_meta/api.lua | 4 ++-- runtime/lua/vim/_meta/vimfn.lua | 6 +++--- runtime/lua/vim/_meta/vvars.lua | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index 8236cc7cf0..c28af7bbff 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -1110,8 +1110,8 @@ function vim.api.nvim_del_var(name) end --- can be omitted for no highlight. --- @param history boolean if true, add to `message-history`. --- @param opts vim.api.keyset.echo_opts Optional parameters. ---- - verbose: Message was printed as a result of 'verbose' option ---- if Nvim was invoked with -V3log_file, the message will be +--- - verbose: Message is printed as a result of 'verbose' option. +--- If Nvim was invoked with -V3log_file, the message will be --- redirected to the log_file and suppressed from direct output. function vim.api.nvim_echo(chunks, history, opts) end diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index 7548d1beb2..5eb15e1eee 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -828,7 +828,7 @@ function vim.fn.charclass(string) end --- echo col('.') " returns 7 --- < --- ---- @param expr string|integer[] +--- @param expr string|any[] --- @param winid? integer --- @return integer function vim.fn.charcol(expr, winid) end @@ -956,7 +956,7 @@ function vim.fn.clearmatches(win) end --- imap echo col(".").."\n" --- < --- ---- @param expr string|integer[] +--- @param expr string|any[] --- @param winid? integer --- @return integer function vim.fn.col(expr, winid) end @@ -10546,7 +10546,7 @@ function vim.fn.values(dict) end --- echo max(map(range(1, line('$')), "virtcol([v:val, '$'])")) --- < --- ---- @param expr string|integer[] +--- @param expr string|any[] --- @param list? boolean --- @param winid? integer --- @return any diff --git a/runtime/lua/vim/_meta/vvars.lua b/runtime/lua/vim/_meta/vvars.lua index e00402ab3f..733aa965a2 100644 --- a/runtime/lua/vim/_meta/vvars.lua +++ b/runtime/lua/vim/_meta/vvars.lua @@ -166,7 +166,7 @@ vim.v.errors = ... --- inclusive Motion is `inclusive`, else exclusive. --- scope Event-specific scope name. --- operator Current `operator`. Also set for Ex ---- commands (unlike `v:operator`). For +--- commands (unlike `v:operator`). For --- example if `TextYankPost` is triggered --- by the `:yank` Ex command then --- `v:event.operator` is "y". -- cgit From 4b001f297ae29654b86eccd4dde72b2cc314ca01 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Sat, 9 Nov 2024 08:40:27 +0000 Subject: fix(lsp): fix infinite loop Fixes #31129 --- runtime/lua/vim/lsp/semantic_tokens.lua | 35 ++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 12 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index 839ec8bea3..f23c694c4b 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -394,6 +394,23 @@ local function set_mark(bufnr, ns, token, hl_group, priority) }) end +--- @param lnum integer +--- @param foldend integer? +--- @return boolean, integer? +local function check_fold(lnum, foldend) + if foldend and lnum <= foldend then + return true, foldend + end + + local folded = vim.fn.foldclosed(lnum) + + if folded == -1 then + return false, nil + end + + return folded ~= lnum, vim.fn.foldclosedend(lnum) +end + --- on_win handler for the decoration provider (see |nvim_set_decoration_provider|) --- --- If there is a current result for the buffer and the version matches the @@ -462,15 +479,15 @@ function STHighlighter:on_win(topline, botline) local first = lower_bound(highlights, topline, 1, #highlights + 1) local last = upper_bound(highlights, botline, first, #highlights + 1) - 1 - local i = first - while i <= last do + --- @type boolean?, integer? + local is_folded, foldend + + for i = first, last do local token = highlights[i] - local folded = vim.fn.foldclosed(token.line + 1) - -- Do not highlight folded lines - local can_mark = folded == -1 or folded == token.line + 1 + is_folded, foldend = check_fold(token.line + 1, foldend) - if can_mark and not token.marked then + if not is_folded and not token.marked then set_mark0(token, string.format('@lsp.type.%s.%s', token.type, ft), 0) for modifier in pairs(token.modifiers) do set_mark0(token, string.format('@lsp.mod.%s.%s', modifier, ft), 1) @@ -487,12 +504,6 @@ function STHighlighter:on_win(topline, botline) }, }) end - - if folded ~= -1 then - i = math.min(last, vim.fn.foldclosedend(token.line + 1) - 1) - else - i = i + 1 - end end end end -- cgit From c5f217db01eb9b07368604f86cbdba0cff8f21a3 Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Sat, 9 Nov 2024 13:00:37 -0800 Subject: refactor(lsp): simplify reference range logic --- runtime/lua/vim/lsp/util.lua | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 4ca4239127..6eab0f3da4 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1651,21 +1651,12 @@ do --[[ References ]] validate('bufnr', bufnr, 'number', true) validate('offset_encoding', offset_encoding, 'string', false) for _, reference in ipairs(references) do - local start_line = reference.range.start.line - local start_char = reference.range.start.character - local end_line = reference.range['end'].line - local end_char = reference.range['end'].character + local range = reference.range + local start_line = range.start.line + local end_line = range['end'].line - local start_idx = get_line_byte_from_position( - bufnr, - { line = start_line, character = start_char }, - offset_encoding - ) - local end_idx = get_line_byte_from_position( - bufnr, - { line = start_line, character = end_char }, - offset_encoding - ) + local start_idx = get_line_byte_from_position(bufnr, range.start, offset_encoding) + local end_idx = get_line_byte_from_position(bufnr, range['end'], offset_encoding) local document_highlight_kind = { [protocol.DocumentHighlightKind.Text] = 'LspReferenceText', -- cgit From ff575b38864022b0b27527f11636c49107acd1bc Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 7 Nov 2024 11:42:08 +0000 Subject: perf(filetype): optimize internal data structures This changes the type for the sorted pattern table from `vim.filetype.mapping[]` to `vim.filetype.mapping.sorted[]` E.g. instead of: ```lua { { ['/debian/changelog$'] = {'debchangelog', { parent = '/debian/' } }, { ['%.git/'] = { detect.git , { parent = 'git/', priority = -1 } }, } ``` It is now: ```lua { { '/debian/, '/debian/changelog$', 'debchangelog' }, { 'git/' , '%.git/' , detect.git , -1 }, } ``` Overall this should roughly cut the amount of tables used by 3, and replaces lots of hash indexes with array indexes. --- runtime/lua/vim/filetype.lua | 95 +++++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 45 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 6caf25c588..eda4c6a479 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -4,15 +4,22 @@ local fn = vim.fn local M = {} --- @alias vim.filetype.mapfn fun(path:string,bufnr:integer, ...):string?, fun(b:integer)? ---- @alias vim.filetype.mapopts { parent: string, priority: number } +--- @alias vim.filetype.mapopts { priority: number } --- @alias vim.filetype.maptbl [string|vim.filetype.mapfn, vim.filetype.mapopts] --- @alias vim.filetype.mapping.value string|vim.filetype.mapfn|vim.filetype.maptbl --- @alias vim.filetype.mapping table +--- @class vim.filetype.mapping.sorted +--- @nodoc +--- @field [1] string parent pattern +--- @field [2] string pattern +--- @field [3] string|vim.filetype.mapfn +--- @field [4] integer priority + --- @param ft string|vim.filetype.mapfn ---- @param opts? vim.filetype.mapopts +--- @param priority? integer --- @return vim.filetype.maptbl -local function starsetf(ft, opts) +local function starsetf(ft, priority) return { function(path, bufnr) -- Note: when `ft` is a function its return value may be nil. @@ -27,11 +34,8 @@ local function starsetf(ft, opts) end end, { - -- Allow setting "parent" to be reused in closures, but don't have default as it will be - -- assigned later from grouping - parent = opts and opts.parent, -- Starset matches should have lowest priority by default - priority = (opts and opts.priority) or -math.huge, + priority = priority or -math.huge, }, } end @@ -1874,11 +1878,10 @@ local filename = { } -- Re-use closures as much as possible -local detect_apache_diretc = starsetf('apache', { parent = '/etc/' }) -local detect_apache_dotconf = starsetf('apache', { parent = '%.conf' }) -local detect_muttrc = starsetf('muttrc', { parent = 'utt' }) -local detect_neomuttrc = starsetf('neomuttrc', { parent = 'utt' }) -local detect_xkb = starsetf('xkb', { parent = '/usr/' }) +local detect_apache = starsetf('apache') +local detect_muttrc = starsetf('muttrc') +local detect_neomuttrc = starsetf('neomuttrc') +local detect_xkb = starsetf('xkb') ---@type table local pattern = { @@ -1895,14 +1898,14 @@ local pattern = { ['/etc/asound%.conf$'] = 'alsaconf', ['/etc/apache2/sites%-.*/.*%.com$'] = 'apache', ['/etc/httpd/.*%.conf$'] = 'apache', - ['/etc/apache2/.*%.conf'] = detect_apache_diretc, - ['/etc/apache2/conf%..*/'] = detect_apache_diretc, - ['/etc/apache2/mods%-.*/'] = detect_apache_diretc, - ['/etc/apache2/sites%-.*/'] = detect_apache_diretc, - ['/etc/httpd/conf%..*/'] = detect_apache_diretc, - ['/etc/httpd/conf%.d/.*%.conf'] = detect_apache_diretc, - ['/etc/httpd/mods%-.*/'] = detect_apache_diretc, - ['/etc/httpd/sites%-.*/'] = detect_apache_diretc, + ['/etc/apache2/.*%.conf'] = detect_apache, + ['/etc/apache2/conf%..*/'] = detect_apache, + ['/etc/apache2/mods%-.*/'] = detect_apache, + ['/etc/apache2/sites%-.*/'] = detect_apache, + ['/etc/httpd/conf%..*/'] = detect_apache, + ['/etc/httpd/conf%.d/.*%.conf'] = detect_apache, + ['/etc/httpd/mods%-.*/'] = detect_apache, + ['/etc/httpd/sites%-.*/'] = detect_apache, ['/etc/proftpd/.*%.conf'] = starsetf('apachestyle'), ['/etc/proftpd/conf%..*/'] = starsetf('apachestyle'), ['/etc/cdrdao%.conf$'] = 'cdrdaoconf', @@ -2193,13 +2196,13 @@ local pattern = { }, ['%.conf'] = { ['^proftpd%.conf'] = starsetf('apachestyle'), - ['^access%.conf'] = detect_apache_dotconf, - ['^apache%.conf'] = detect_apache_dotconf, - ['^apache2%.conf'] = detect_apache_dotconf, - ['^httpd%.conf'] = detect_apache_dotconf, - ['^httpd%-.*%.conf'] = detect_apache_dotconf, - ['^proxy%-html%.conf'] = detect_apache_dotconf, - ['^srm%.conf'] = detect_apache_dotconf, + ['^access%.conf'] = detect_apache, + ['^apache%.conf'] = detect_apache, + ['^apache2%.conf'] = detect_apache, + ['^httpd%.conf'] = detect_apache, + ['^httpd%-.*%.conf'] = detect_apache, + ['^proxy%-html%.conf'] = detect_apache, + ['^srm%.conf'] = detect_apache, ['asterisk/.*%.conf'] = starsetf('asterisk'), ['asterisk.*/.*voicemail%.conf'] = starsetf('asteriskvm'), ['^dictd.*%.conf$'] = 'dictdconf', @@ -2372,7 +2375,7 @@ local pattern = { ['/app%-defaults/'] = starsetf('xdefaults'), ['^Xresources'] = starsetf('xdefaults'), -- Increase priority to run before the pattern below - ['^XF86Config%-4'] = starsetf(detect.xfree86_v4, { priority = -math.huge + 1 }), + ['^XF86Config%-4'] = starsetf(detect.xfree86_v4, -math.huge + 1), ['^XF86Config'] = starsetf(detect.xfree86_v3), ['Xmodmap$'] = 'xmodmap', ['xmodmap'] = starsetf('xmodmap'), @@ -2398,8 +2401,10 @@ local pattern = { --- @type table local pattern_lookup = {} +--- @param a vim.filetype.mapping.sorted +--- @param b vim.filetype.mapping.sorted local function compare_by_priority(a, b) - return a[next(a)][2].priority > b[next(b)][2].priority + return a[4] > b[4] end --- @param pat string @@ -2409,30 +2414,30 @@ local function parse_pattern(pat) end --- @param t table ---- @return vim.filetype.mapping[] ---- @return vim.filetype.mapping[] +--- @return vim.filetype.mapping.sorted[] +--- @return vim.filetype.mapping.sorted[] local function sort_by_priority(t) -- Separate patterns with non-negative and negative priority because they -- will be processed separately - local pos = {} --- @type vim.filetype.mapping[] - local neg = {} --- @type vim.filetype.mapping[] + local pos = {} --- @type vim.filetype.mapping.sorted[] + local neg = {} --- @type vim.filetype.mapping.sorted[] for parent, ft_map in pairs(t) do pattern_lookup[parent] = pattern_lookup[parent] or parse_pattern(parent) for pat, maptbl in pairs(ft_map) do - local ft = type(maptbl) == 'table' and maptbl[1] or maptbl + local ft_or_fun = type(maptbl) == 'table' and maptbl[1] or maptbl assert( - type(ft) == 'string' or type(ft) == 'function', + type(ft_or_fun) == 'string' or type(ft_or_fun) == 'function', 'Expected string or function for filetype' ) -- Parse pattern for common data and cache it once pattern_lookup[pat] = pattern_lookup[pat] or parse_pattern(pat) - local opts = (type(maptbl) == 'table' and type(maptbl[2]) == 'table') and maptbl[2] or {} - opts.parent = opts.parent or parent - opts.priority = opts.priority or 0 + --- @type vim.filetype.mapopts? + local opts = (type(maptbl) == 'table' and type(maptbl[2]) == 'table') and maptbl[2] or nil + local priority = opts and opts.priority or 0 - table.insert(opts.priority >= 0 and pos or neg, { [pat] = { ft, opts } }) + table.insert(priority >= 0 and pos or neg, { parent, pat, ft_or_fun, priority }) end end @@ -2643,7 +2648,8 @@ local function match_pattern(name, path, tail, pat, try_all_candidates) if some_env_missing then return nil end - pat, has_slash = expanded, expanded:find('/') ~= nil + pat = expanded + has_slash = has_slash or expanded:find('/') ~= nil end -- Try all possible candidates to make parent patterns not depend on slash presence @@ -2665,14 +2671,13 @@ end --- @param name string --- @param path string --- @param tail string ---- @param pattern_sorted vim.filetype.mapping[] +--- @param pattern_sorted vim.filetype.mapping.sorted[] --- @param parent_matches table --- @param bufnr integer? local function match_pattern_sorted(name, path, tail, pattern_sorted, parent_matches, bufnr) - for i = 1, #pattern_sorted do - local pat, ft_data = next(pattern_sorted[i]) + for _, p in ipairs(pattern_sorted) do + local parent, pat, ft_or_fn = p[1], p[2], p[3] - local parent = ft_data[2].parent local parent_is_matched = parent_matches[parent] if parent_is_matched == nil then parent_matches[parent] = match_pattern(name, path, tail, parent, true) ~= nil @@ -2682,7 +2687,7 @@ local function match_pattern_sorted(name, path, tail, pattern_sorted, parent_mat if parent_is_matched then local matches = match_pattern(name, path, tail, pat, false) if matches then - local ft, on_detect = dispatch(ft_data[1], path, bufnr, matches) + local ft, on_detect = dispatch(ft_or_fn, path, bufnr, matches) if ft then return ft, on_detect end -- cgit From 3e855d533f4477ffddfe94ccea48255979c8b7fb Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Mon, 11 Nov 2024 14:23:38 +0000 Subject: perf(lsp): use faster version of str_byteindex --- runtime/lua/vim/_editor.lua | 38 ++++++++++++++++++++++----------- runtime/lua/vim/lsp/semantic_tokens.lua | 14 ++++++------ runtime/lua/vim/shared.lua | 2 +- 3 files changed, 34 insertions(+), 20 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index cbab2a0165..b4a1e0fc15 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -764,8 +764,11 @@ function vim.str_byteindex(s, encoding, index, strict_indexing) return vim._str_byteindex(s, old_index, use_utf16) or error('index out of range') end - vim.validate('s', s, 'string') - vim.validate('index', index, 'number') + -- Avoid vim.validate for performance. + if type(s) ~= 'string' or type(index) ~= 'number' then + vim.validate('s', s, 'string') + vim.validate('index', index, 'number') + end local len = #s @@ -773,11 +776,15 @@ function vim.str_byteindex(s, encoding, index, strict_indexing) return 0 end - vim.validate('encoding', encoding, function(v) - return utfs[v], 'invalid encoding' - end) + if not utfs[encoding] then + vim.validate('encoding', encoding, function(v) + return utfs[v], 'invalid encoding' + end) + end - vim.validate('strict_indexing', strict_indexing, 'boolean', true) + if strict_indexing ~= nil and type(strict_indexing) ~= 'boolean' then + vim.validate('strict_indexing', strict_indexing, 'boolean', true) + end if strict_indexing == nil then strict_indexing = true end @@ -828,8 +835,11 @@ function vim.str_utfindex(s, encoding, index, strict_indexing) return col32, col16 end - vim.validate('s', s, 'string') - vim.validate('index', index, 'number', true) + if type(s) ~= 'string' or (index ~= nil and type(index) ~= 'number') then + vim.validate('s', s, 'string') + vim.validate('index', index, 'number', true) + end + if not index then index = math.huge strict_indexing = false @@ -839,11 +849,15 @@ function vim.str_utfindex(s, encoding, index, strict_indexing) return 0 end - vim.validate('encoding', encoding, function(v) - return utfs[v], 'invalid encoding' - end) + if not utfs[encoding] then + vim.validate('encoding', encoding, function(v) + return utfs[v], 'invalid encoding' + end) + end - vim.validate('strict_indexing', strict_indexing, 'boolean', true) + if strict_indexing ~= nil and type(strict_indexing) ~= 'boolean' then + vim.validate('strict_indexing', strict_indexing, 'boolean', true) + end if strict_indexing == nil then strict_indexing = true end diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index f23c694c4b..215e5f41aa 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -99,11 +99,12 @@ local function tokens_to_ranges(data, bufnr, client, request) local legend = client.server_capabilities.semanticTokensProvider.legend local token_types = legend.tokenTypes local token_modifiers = legend.tokenModifiers + local encoding = client.offset_encoding local lines = api.nvim_buf_get_lines(bufnr, 0, -1, false) local ranges = {} ---@type STTokenRange[] local start = uv.hrtime() - local ms_to_ns = 1000 * 1000 + local ms_to_ns = 1e6 local yield_interval_ns = 5 * ms_to_ns local co, is_main = coroutine.running() @@ -135,14 +136,13 @@ local function tokens_to_ranges(data, bufnr, client, request) -- data[i+3] +1 because Lua tables are 1-indexed local token_type = token_types[data[i + 3] + 1] - local modifiers = modifiers_from_number(data[i + 4], token_modifiers) - - local end_char = start_char + data[i + 2] - local buf_line = lines and lines[line + 1] or '' - local start_col = vim.str_byteindex(buf_line, client.offset_encoding, start_char, false) - local end_col = vim.str_byteindex(buf_line, client.offset_encoding, end_char, false) if token_type then + local modifiers = modifiers_from_number(data[i + 4], token_modifiers) + local end_char = start_char + data[i + 2] + local buf_line = lines and lines[line + 1] or '' + local start_col = vim.str_byteindex(buf_line, encoding, start_char, false) + local end_col = vim.str_byteindex(buf_line, encoding, end_char, false) ranges[#ranges + 1] = { line = line, start_col = start_col, diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 1a3b33c885..4f2373b182 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -1142,7 +1142,7 @@ end --- @param mod T --- @return T function vim._defer_require(root, mod) - return setmetatable({}, { + return setmetatable({ _submodules = mod }, { ---@param t table ---@param k string __index = function(t, k) -- cgit From 295f98c8e0d991bdf336d1300abafaa4cad841d1 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Mon, 11 Nov 2024 22:30:04 +0100 Subject: vim-patch:9.1.0853: filetype: kubernetes config file not recognized Problem: filetype: kubernetes config file not recognized Solution: detect '/.kube/config' file as yaml filetype (Jonathan Lopez) closes: vim/vim#11076 https://github.com/vim/vim/commit/6fbf63de865001dedafc227465e651926cf6f6dc Co-authored-by: Jonathan Lopez --- runtime/lua/vim/filetype.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index eda4c6a479..f22d814647 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -2148,6 +2148,7 @@ local pattern = { ['/sway/config$'] = 'swayconfig', ['/%.cargo/config$'] = 'toml', ['/%.bundle/config$'] = 'yaml', + ['/%.kube/config$'] = 'yaml', }, ['/%.'] = { ['/%.aws/credentials$'] = 'confini', -- cgit From 9aab98a275cb6140540eecbe643a7943d700b58d Mon Sep 17 00:00:00 2001 From: Nicolas Hillegeer Date: Tue, 12 Nov 2024 10:51:42 +0100 Subject: refactor(lsp): avoid redundant function wrapping Leftover from #21026. --- runtime/lua/vim/lsp/rpc.lua | 79 +++++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 42 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index ac43801c1c..6c8564845f 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -1,7 +1,7 @@ local uv = vim.uv local log = require('vim.lsp.log') local protocol = require('vim.lsp.protocol') -local validate, schedule, schedule_wrap = vim.validate, vim.schedule, vim.schedule_wrap +local validate, schedule_wrap = vim.validate, vim.schedule_wrap local is_win = vim.fn.has('win32') == 1 @@ -409,49 +409,44 @@ function Client:handle_body(body) local err --- @type lsp.ResponseError|nil -- Schedule here so that the users functions don't trigger an error and -- we can still use the result. - schedule(function() - coroutine.wrap(function() - local status, result - status, result, err = self:try_call( - M.client_errors.SERVER_REQUEST_HANDLER_ERROR, - self.dispatchers.server_request, - decoded.method, - decoded.params - ) - log.debug( - 'server_request: callback result', - { status = status, result = result, err = err } - ) - if status then - if result == nil and err == nil then - error( - string.format( - 'method %q: either a result or an error must be sent to the server in response', - decoded.method - ) - ) - end - if err then - ---@cast err lsp.ResponseError - assert( - type(err) == 'table', - 'err must be a table. Use rpc_response_error to help format errors.' - ) - ---@type string - local code_name = assert( - protocol.ErrorCodes[err.code], - 'Errors must use protocol.ErrorCodes. Use rpc_response_error to help format errors.' + vim.schedule(coroutine.wrap(function() + local status, result + status, result, err = self:try_call( + M.client_errors.SERVER_REQUEST_HANDLER_ERROR, + self.dispatchers.server_request, + decoded.method, + decoded.params + ) + log.debug('server_request: callback result', { status = status, result = result, err = err }) + if status then + if result == nil and err == nil then + error( + string.format( + 'method %q: either a result or an error must be sent to the server in response', + decoded.method ) - err.message = err.message or code_name - end - else - -- On an exception, result will contain the error message. - err = M.rpc_response_error(protocol.ErrorCodes.InternalError, result) - result = nil + ) end - self:send_response(decoded.id, err, result) - end)() - end) + if err then + ---@cast err lsp.ResponseError + assert( + type(err) == 'table', + 'err must be a table. Use rpc_response_error to help format errors.' + ) + ---@type string + local code_name = assert( + protocol.ErrorCodes[err.code], + 'Errors must use protocol.ErrorCodes. Use rpc_response_error to help format errors.' + ) + err.message = err.message or code_name + end + else + -- On an exception, result will contain the error message. + err = M.rpc_response_error(protocol.ErrorCodes.InternalError, result) + result = nil + end + self:send_response(decoded.id, err, result) + end)) -- This works because we are expecting vim.NIL here elseif decoded.id and (decoded.result ~= vim.NIL or decoded.error ~= vim.NIL) then -- We sent a number, so we expect a number. -- cgit From 86b737649bb8c51649a21033c0e365f53f2bc04a Mon Sep 17 00:00:00 2001 From: hykerr Date: Tue, 12 Nov 2024 12:26:07 +0100 Subject: fix(treesitter): add 'QuitPre' event to autocommands in inspect_tree Problem: Quitting source buffer for ```:InspectTree``` command raises ```E855``` when source buffer and tree views are the only open buffers. Solution: Add ```QuitPre``` event to autocmd handling closing/hiding the source buffer to close all open tree views. This allows nvim to quit when source and tree buffers are the only open windows. --- runtime/lua/vim/treesitter/dev.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/dev.lua b/runtime/lua/vim/treesitter/dev.lua index d68a30cc11..74d796f7fa 100644 --- a/runtime/lua/vim/treesitter/dev.lua +++ b/runtime/lua/vim/treesitter/dev.lua @@ -527,7 +527,7 @@ function M.inspect_tree(opts) end, }) - api.nvim_create_autocmd({ 'BufHidden', 'BufUnload' }, { + api.nvim_create_autocmd({ 'BufHidden', 'BufUnload', 'QuitPre' }, { group = group, buffer = buf, once = true, -- cgit From 33d10db5b7a7cd03b22e3ba401cb3bc74640f522 Mon Sep 17 00:00:00 2001 From: Kristijan Husak Date: Wed, 13 Nov 2024 16:18:29 +0100 Subject: fix(lsp): filter completion candidates based on completeopt (#30945) --- runtime/lua/vim/lsp/completion.lua | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/completion.lua b/runtime/lua/vim/lsp/completion.lua index 10086fa49e..92bc110a97 100644 --- a/runtime/lua/vim/lsp/completion.lua +++ b/runtime/lua/vim/lsp/completion.lua @@ -220,6 +220,20 @@ local function get_doc(item) return '' end +---@param value string +---@param prefix string +---@return boolean +local function match_item_by_value(value, prefix) + if vim.o.completeopt:find('fuzzy') ~= nil then + return next(vim.fn.matchfuzzy({ value }, prefix)) ~= nil + end + + if vim.o.ignorecase and (not vim.o.smartcase or not prefix:find('%u')) then + return vim.startswith(value:lower(), prefix:lower()) + end + return vim.startswith(value, prefix) +end + --- Turns the result of a `textDocument/completion` request into vim-compatible --- |complete-items|. --- @@ -244,8 +258,16 @@ function M._lsp_to_complete_items(result, prefix, client_id) else ---@param item lsp.CompletionItem matches = function(item) - local text = item.filterText or item.label - return next(vim.fn.matchfuzzy({ text }, prefix)) ~= nil + if item.filterText then + return match_item_by_value(item.filterText, prefix) + end + + if item.textEdit then + -- server took care of filtering + return true + end + + return match_item_by_value(item.label, prefix) end end -- cgit From de48fbbd5f8800bd7f1909a6fb41e53e871cf74c Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Thu, 20 Jun 2024 14:48:06 +0200 Subject: fix(messages)!: vim.ui_attach message callbacks are unsafe Problem: Lua callbacks for "msg_show" events with vim.ui_attach() are executed when it is not safe. Solution: Disallow non-fast API calls for "msg_show" event callbacks. Automatically detach callback after excessive errors. Make sure fast APIs do not modify Nvim state. --- runtime/lua/vim/_meta/builtin.lua | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/builtin.lua b/runtime/lua/vim/_meta/builtin.lua index 234c75d38f..dd6ef69eb8 100644 --- a/runtime/lua/vim/_meta/builtin.lua +++ b/runtime/lua/vim/_meta/builtin.lua @@ -233,6 +233,10 @@ function vim.wait(time, callback, interval, fast_only) end --- {callback} receives event name plus additional parameters. See |ui-popupmenu| --- and the sections below for event format for respective events. --- +--- Callbacks for `msg_show` events are executed in |api-fast| context. +--- +--- Excessive errors inside the callback will result in forced detachment. +--- --- WARNING: This api is considered experimental. Usability will vary for --- different screen elements. In particular `ext_messages` behavior is subject --- to further changes and usability improvements. This is expected to be -- cgit From d3e4ffafff425cf488c2bd076678a63440875cb7 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 14 Nov 2024 14:17:33 +0000 Subject: feat(lsp): support utf-8 and utf-32 position encodings Resolves #30034 --- runtime/lua/vim/lsp/protocol.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 98965d0e20..7db48b0c06 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -338,7 +338,9 @@ function protocol.make_client_capabilities() return { general = { positionEncodings = { + 'utf-8', 'utf-16', + 'utf-32', }, }, textDocument = { -- cgit From fa0dcde3d9f17f85baa9dd41aa751c123281ced3 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 15 Nov 2024 08:04:49 +0800 Subject: vim-patch:9.1.0864: message history is fixed to 200 (#31215) Problem: message history is fixed to 200 Solution: Add the 'msghistory' option, increase the default value to 500 (Shougo Matsushita) closes: vim/vim#16048 https://github.com/vim/vim/commit/4bd9b2b2467e696061104a029000e9824c6c609e Co-authored-by: Shougo Matsushita Co-authored-by: Milly --- runtime/lua/vim/_meta/options.lua | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index 45ab14a774..cb783720ac 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -3016,7 +3016,8 @@ vim.go.hid = vim.go.hidden --- A history of ":" commands, and a history of previous search patterns --- is remembered. This option decides how many entries may be stored in ---- each of these histories (see `cmdline-editing`). +--- each of these histories (see `cmdline-editing` and 'msghistory' for +--- the number of messages to remember). --- The maximum value is 10000. --- --- @type integer @@ -4378,6 +4379,15 @@ vim.o.mouset = vim.o.mousetime vim.go.mousetime = vim.o.mousetime vim.go.mouset = vim.go.mousetime +--- Determines how many entries are remembered in the `:messages` history. +--- The maximum value is 10000. +--- +--- @type integer +vim.o.msghistory = 500 +vim.o.mhi = vim.o.msghistory +vim.go.msghistory = vim.o.msghistory +vim.go.mhi = vim.go.msghistory + --- This defines what bases Vim will consider for numbers when using the --- CTRL-A and CTRL-X commands for adding to and subtracting from a number --- respectively; see `CTRL-A` for more info on these commands. -- cgit From 6ef80eb42c730e66e5a769f619e05b353c08c118 Mon Sep 17 00:00:00 2001 From: Hyker Date: Fri, 15 Nov 2024 04:00:18 +0100 Subject: fix(treesitter): keep treeview open if source window is still open #31198 Problem: When there is a tree view opened by :InspectTree and the source buffer is open in multiple windows, closing one of the source windows will lead to the tree view being closed as well. Regression by #31181. Solution: Check how many source windows are open when trying to quit one. If there are more than one, keep the tree view(s) open. If the only source window is closed, also close the tree view(s). fix #31196 --- runtime/lua/vim/treesitter/dev.lua | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/dev.lua b/runtime/lua/vim/treesitter/dev.lua index 74d796f7fa..26817cdba5 100644 --- a/runtime/lua/vim/treesitter/dev.lua +++ b/runtime/lua/vim/treesitter/dev.lua @@ -530,12 +530,19 @@ function M.inspect_tree(opts) api.nvim_create_autocmd({ 'BufHidden', 'BufUnload', 'QuitPre' }, { group = group, buffer = buf, - once = true, callback = function() + -- don't close inpector window if source buffer + -- has more than one open window + if #vim.fn.win_findbuf(buf) > 1 then + return + end + -- close all tree windows for _, window in pairs(vim.fn.win_findbuf(b)) do close_win(window) end + + return true end, }) end -- cgit From f1748b78e3165a0821a11f5ae1fb9398aa67c535 Mon Sep 17 00:00:00 2001 From: Yi Ming Date: Sat, 16 Nov 2024 00:13:41 +0800 Subject: feat(default): bind `vim.lsp.buf.signature_help` in select mode (#31223) --- runtime/lua/vim/_defaults.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua index f399360f1e..25afd83145 100644 --- a/runtime/lua/vim/_defaults.lua +++ b/runtime/lua/vim/_defaults.lua @@ -179,7 +179,7 @@ do vim.lsp.buf.document_symbol() end, { desc = 'vim.lsp.buf.document_symbol()' }) - vim.keymap.set('i', '', function() + vim.keymap.set({ 'i', 's' }, '', function() vim.lsp.buf.signature_help() end, { desc = 'vim.lsp.buf.signature_help()' }) end -- cgit From 6e4df18b457e9743c34068fd6e0a89fd04d3526c Mon Sep 17 00:00:00 2001 From: luukvbaal Date: Fri, 15 Nov 2024 23:34:42 +0100 Subject: fix(ui): no fast context for prompt message kinds #31224 Problem: No longer able to show prompt messages with vim.ui_attach(). Solution: Do not execute callback in fast context for prompt message kinds. These events must be safe to show the incoming message so the event itself serves to indicate that the message should be shown immediately. --- runtime/lua/vim/_meta/builtin.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/builtin.lua b/runtime/lua/vim/_meta/builtin.lua index dd6ef69eb8..b8779b66fe 100644 --- a/runtime/lua/vim/_meta/builtin.lua +++ b/runtime/lua/vim/_meta/builtin.lua @@ -233,7 +233,9 @@ function vim.wait(time, callback, interval, fast_only) end --- {callback} receives event name plus additional parameters. See |ui-popupmenu| --- and the sections below for event format for respective events. --- ---- Callbacks for `msg_show` events are executed in |api-fast| context. +--- Callbacks for `msg_show` events are executed in |api-fast| context unless +--- Nvim will wait for input, in which case messages should be shown +--- immediately. --- --- Excessive errors inside the callback will result in forced detachment. --- -- cgit From fbbb9d6f7bc8fd3ed309d195d723894572549cf8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 16 Nov 2024 08:42:12 +0800 Subject: vim-patch:9.1.0866: filetype: LLVM IR files are not recognized (#31228) Problem: filetype: LLVM IR files are not recognized Solution: detect '*.ll' files either as lifelines or llvm filetype (Wu, Zhenyu) closes: vim/vim#15824 https://github.com/vim/vim/commit/bc32bbddcfc2671158a4780838766ed2d1e14fa6 N/A patch: vim-patch:7e4b861: runtime(filetype): remove duplicated *.org file pattern Co-authored-by: Wu, Zhenyu --- runtime/lua/vim/filetype.lua | 2 +- runtime/lua/vim/filetype/detect.lua | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index f22d814647..3b3c4481f2 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -682,7 +682,6 @@ local extension = { l = 'lex', lhs = 'lhaskell', lidr = 'lidris2', - ll = 'lifelines', ly = 'lilypond', ily = 'lilypond', liquid = 'liquid', @@ -697,6 +696,7 @@ local extension = { lt = 'lite', lite = 'lite', livemd = 'livebook', + ll = detect.ll, log = detect.log, Log = detect.log, LOG = detect.log, diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 1b9e28efad..98b001bd51 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -868,6 +868,16 @@ function M.log(path, _) end end +--- @type vim.filetype.mapfn +function M.ll(_, bufnr) + local first_line = getline(bufnr, 1) + if matchregex(first_line, [[;\|\\|\]]) then + return 'llvm' + else + return 'lifelines' + end +end + --- @type vim.filetype.mapfn function M.lpc(_, bufnr) if vim.g.lpc_syntax_for_c then -- cgit From 2f7b0018b0354447497e8496fa3745773df2f218 Mon Sep 17 00:00:00 2001 From: errael Date: Fri, 15 Nov 2024 17:37:36 -0800 Subject: docs: standardize doc for ChanInfo/ChanOpen autocmds/v:event (#31099) --- runtime/lua/vim/_meta/vvars.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/vvars.lua b/runtime/lua/vim/_meta/vvars.lua index 733aa965a2..8784fdbac9 100644 --- a/runtime/lua/vim/_meta/vvars.lua +++ b/runtime/lua/vim/_meta/vvars.lua @@ -160,6 +160,7 @@ vim.v.errors = ... --- an aborting condition (e.g. `c_Esc` or --- `c_CTRL-C` for `CmdlineLeave`). --- chan `channel-id` +--- info Dict of arbitrary event data. --- cmdlevel Level of cmdline. --- cmdtype Type of cmdline, `cmdline-char`. --- cwd Current working directory. -- cgit From f85bc41c800d7f5c0256f29aa347a53600a7c8d5 Mon Sep 17 00:00:00 2001 From: luukvbaal Date: Sun, 17 Nov 2024 00:32:36 +0100 Subject: feat(ui): don't show unfocusable windows in :tabs, 'tabline' #27984 Problem: Floating windows with focusable set to false can reasonably be expected to be UI elements but are listed in some outputs that should contain only regular windows. Solution: Hide unfocusable floating windows from the default tabline and :tabs. --- runtime/lua/vim/_meta/api.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index c28af7bbff..30654efdea 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -1768,7 +1768,7 @@ function vim.api.nvim_open_term(buffer, opts) end --- - focusable: Enable focus by user actions (wincmds, mouse events). --- Defaults to true. Non-focusable windows can be entered by --- `nvim_set_current_win()`, or, when the `mouse` field is set to true, ---- by mouse events. +--- by mouse events. See `focusable`. --- - mouse: Specify how this window interacts with mouse events. --- Defaults to `focusable` value. --- - If false, mouse events pass through this window. -- cgit From 9c718bc2bce53b5be45061bff940f99e50c8bfcb Mon Sep 17 00:00:00 2001 From: luukvbaal Date: Sun, 17 Nov 2024 00:36:11 +0100 Subject: fix(api): validation, documentation of hl_group #31195 Problem: Documentation for "hl_group" in nvim_buf_set_extmark() is unclear. "hl_group" in nvim_echo() does not accept highlight group id. Solution: Move documentation for highlight group name/id to first mention of hl_group. Update nvim_echo() to accept highlight group id. --- runtime/lua/vim/_meta/api.lua | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index 30654efdea..3c9b9d4f44 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -592,8 +592,9 @@ function vim.api.nvim_buf_line_count(buffer) end --- - id : id of the extmark to edit. --- - end_row : ending line of the mark, 0-based inclusive. --- - end_col : ending col of the mark, 0-based exclusive. ---- - hl_group : name of the highlight group used to highlight ---- this mark. +--- - hl_group : highlight group used for the text range. This and below +--- highlight groups can be supplied either as a string or as an integer, +--- the latter of which can be obtained using `nvim_get_hl_id_by_name()`. --- - hl_eol : when true, for a multiline highlight covering the --- EOL of a line, continue the highlight for the rest --- of the screen line (just like for diff and @@ -603,9 +604,7 @@ function vim.api.nvim_buf_line_count(buffer) end --- text chunk with specified highlight. `highlight` element --- can either be a single highlight group, or an array of --- multiple highlight groups that will be stacked ---- (highest priority last). A highlight group can be supplied ---- either as a string or as an integer, the latter which ---- can be obtained using `nvim_get_hl_id_by_name()`. +--- (highest priority last). --- - virt_text_pos : position of virtual text. Possible values: --- - "eol": right after eol character (default). --- - "overlay": display over the specified column, without @@ -676,15 +675,12 @@ function vim.api.nvim_buf_line_count(buffer) end --- buffer or end of the line respectively. Defaults to true. --- - sign_text: string of length 1-2 used to display in the --- sign column. ---- - sign_hl_group: name of the highlight group used to ---- highlight the sign column text. ---- - number_hl_group: name of the highlight group used to ---- highlight the number column. ---- - line_hl_group: name of the highlight group used to ---- highlight the whole line. ---- - cursorline_hl_group: name of the highlight group used to ---- highlight the sign column text when the cursor is on ---- the same line as the mark and 'cursorline' is enabled. +--- - sign_hl_group: highlight group used for the sign column text. +--- - number_hl_group: highlight group used for the number column. +--- - line_hl_group: highlight group used for the whole line. +--- - cursorline_hl_group: highlight group used for the sign +--- column text when the cursor is on the same line as the +--- mark and 'cursorline' is enabled. --- - conceal: string which should be either empty or a single --- character. Enable concealing similar to `:syn-conceal`. --- When a character is supplied it is used as `:syn-cchar`. @@ -1106,8 +1102,8 @@ function vim.api.nvim_del_var(name) end --- Echo a message. --- --- @param chunks any[] A list of `[text, hl_group]` arrays, each representing a ---- text chunk with specified highlight. `hl_group` element ---- can be omitted for no highlight. +--- text chunk with specified highlight group name or ID. +--- `hl_group` element can be omitted for no highlight. --- @param history boolean if true, add to `message-history`. --- @param opts vim.api.keyset.echo_opts Optional parameters. --- - verbose: Message is printed as a result of 'verbose' option. -- cgit From cdc9baeaf89eb09f08427a09e3a0f86d56dcc812 Mon Sep 17 00:00:00 2001 From: luukvbaal Date: Sun, 17 Nov 2024 01:25:10 +0100 Subject: fix(treesitter): remove redundant on_bytes callback #31041 Problem: Treesitter highlighter implements an on_bytes callback that just re-marks a buffer range for redraw. The edit that prompted the callback will already have done that. Solution: Remove redundant on_bytes callback from the treesitter highlighter module. --- runtime/lua/vim/treesitter/highlighter.lua | 10 ---------- runtime/lua/vim/treesitter/languagetree.lua | 2 +- 2 files changed, 1 insertion(+), 11 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index 7bdcdc774a..4ad62f4816 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -93,9 +93,6 @@ function TSHighlighter.new(tree, opts) opts = opts or {} ---@type { queries: table } self.tree = tree tree:register_cbs({ - on_bytes = function(...) - self:on_bytes(...) - end, on_detach = function() self:on_detach() end, @@ -214,13 +211,6 @@ function TSHighlighter:for_each_highlight_state(fn) end end ----@package ----@param start_row integer ----@param new_end integer -function TSHighlighter:on_bytes(_, _, start_row, _, _, _, _, _, new_end) - api.nvim__redraw({ buf = self.bufnr, range = { start_row, start_row + new_end + 1 } }) -end - ---@package function TSHighlighter:on_detach() self:destroy() diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index fd68c2b910..4b42164dc8 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -1037,7 +1037,7 @@ end --- Registers callbacks for the [LanguageTree]. ---@param cbs table An [nvim_buf_attach()]-like table argument with the following handlers: ---- - `on_bytes` : see [nvim_buf_attach()], but this will be called _after_ the parsers callback. +--- - `on_bytes` : see [nvim_buf_attach()]. --- - `on_changedtree` : a callback that will be called every time the tree has syntactical changes. --- It will be passed two arguments: a table of the ranges (as node ranges) that --- changed and the changed tree. -- cgit From 38838fb00ab3d2ebaefc820cebcc5990ea98ea03 Mon Sep 17 00:00:00 2001 From: Maria José Solano Date: Sat, 16 Nov 2024 18:32:09 -0800 Subject: fix(lsp): type-errors, other nits in vim.lsp.log #31235 --- runtime/lua/vim/lsp/log.lua | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua index 4f177b47fd..ec78dd3dc5 100644 --- a/runtime/lua/vim/lsp/log.lua +++ b/runtime/lua/vim/lsp/log.lua @@ -32,12 +32,12 @@ local function notify(msg, level) end end -local logfilename = vim.fs.joinpath(vim.fn.stdpath('log'), 'lsp.log') +local logfilename = vim.fs.joinpath(vim.fn.stdpath('log') --[[@as string]], 'lsp.log') -- TODO: Ideally the directory should be created in open_logfile(), right -- before opening the log file, but open_logfile() can be called from libuv -- callbacks, where using fn.mkdir() is not allowed. -vim.fn.mkdir(vim.fn.stdpath('log'), 'p') +vim.fn.mkdir(vim.fn.stdpath('log') --[[@as string]], 'p') --- Returns the log filename. ---@return string log filename @@ -82,6 +82,7 @@ end for level, levelnr in pairs(log_levels) do -- Also export the log level on the root object. + ---@diagnostic disable-next-line: no-unknown log[level] = levelnr -- Add a reverse lookup. @@ -93,7 +94,7 @@ end --- @return fun(...:any): boolean? local function create_logger(level, levelnr) return function(...) - if levelnr < current_log_level then + if not log.should_log(levelnr) then return false end local argc = select('#', ...) @@ -169,7 +170,7 @@ end --- Checks whether the level is sufficient for logging. ---@param level integer log level ----@return bool : true if would log, false if not +---@return boolean : true if would log, false if not function log.should_log(level) return level >= current_log_level end -- cgit From e025f5a5b30a1ef92e88fed0f0c548d2240d30c0 Mon Sep 17 00:00:00 2001 From: luukvbaal Date: Sun, 17 Nov 2024 19:21:50 +0100 Subject: fix(messages): proper multiline Lua print() messages #31205 Problem: Separate message emitted for each newline present in Lua print() arguments. Solution: Make msg_multiline() handle NUL bytes. Refactor print() to use msg_multiline(). Refactor vim.print() to use print(). --- runtime/lua/vim/_editor.lua | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index b4a1e0fc15..44f17b3f85 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -1151,21 +1151,16 @@ end --- @param ... any --- @return any # given arguments. function vim.print(...) - if vim.in_fast_event() then - print(...) - return ... - end - + local msg = {} for i = 1, select('#', ...) do local o = select(i, ...) if type(o) == 'string' then - vim.api.nvim_out_write(o) + table.insert(msg, o) else - vim.api.nvim_out_write(vim.inspect(o, { newline = '\n', indent = ' ' })) + table.insert(msg, vim.inspect(o, { newline = '\n', indent = ' ' })) end - vim.api.nvim_out_write('\n') end - + print(table.concat(msg, '\n')) return ... end -- cgit From 235cb5bc5f2553f5a46807267b8705e53f7a14af Mon Sep 17 00:00:00 2001 From: luukvbaal Date: Sun, 17 Nov 2024 20:43:36 +0100 Subject: fix(api): update "range" windows in nvim__redraw #31042 Problem: nvim__redraw's "range" marks a buffer range for redraw, and subsequently flushes the UI without updating the windows containing that buffer. Solution: Implicitly update the screen, unless specified otherwise. Only update the screen with the last call of the treesitter on_changedtree() callback. --- runtime/lua/vim/treesitter/highlighter.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index 4ad62f4816..05124c29f0 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -219,8 +219,8 @@ end ---@package ---@param changes Range6[] function TSHighlighter:on_changedtree(changes) - for _, ch in ipairs(changes) do - api.nvim__redraw({ buf = self.bufnr, range = { ch[1], ch[4] + 1 } }) + for i, ch in ipairs(changes) do + api.nvim__redraw({ buf = self.bufnr, range = { ch[1], ch[4] + 1 }, flush = i == #changes }) end end -- cgit From 44229bb85b6cff00193164967126d85a7a785a7b Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Sun, 17 Nov 2024 12:31:32 -0800 Subject: feat(lsp): highlight hover target/range #31110 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Problem:** Despite the LSP providing the option for language servers to specify a range with a hover response (for highlighting), Neovim does not give the option to highlight this range. **Solution:** Add an option to `buf.hover()` which causes this range to be highlighted. Co-authored-by: Mathias Fußenegger --- runtime/lua/vim/lsp/buf.lua | 45 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 6d7597c5ff..a75e322e90 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -20,6 +20,8 @@ local function client_positional_params(params) end end +local hover_ns = api.nvim_create_namespace('vim_lsp_hover_range') + --- @class vim.lsp.buf.hover.Opts : vim.lsp.util.open_floating_preview.Opts --- @field silent? boolean @@ -30,13 +32,24 @@ end --- In the floating window, all commands and mappings are available as usual, --- except that "q" dismisses the window. --- You can scroll the contents the same as you would any other buffer. +--- +--- Note: to disable hover highlights, add the following to your config: +--- +--- ```lua +--- vim.api.nvim_create_autocmd('ColorScheme', { +--- callback = function() +--- vim.api.nvim_set_hl(0, 'LspReferenceTarget', {}) +--- end, +--- }) +--- ``` --- @param config? vim.lsp.buf.hover.Opts function M.hover(config) config = config or {} config.focus_id = ms.textDocument_hover lsp.buf_request_all(0, ms.textDocument_hover, client_positional_params(), function(results, ctx) - if api.nvim_get_current_buf() ~= ctx.bufnr then + local bufnr = assert(ctx.bufnr) + if api.nvim_get_current_buf() ~= bufnr then -- Ignore result since buffer changed. This happens for slow language servers. return end @@ -67,9 +80,10 @@ function M.hover(config) local format = 'markdown' for client_id, result in pairs(results1) do + local client = assert(lsp.get_client_by_id(client_id)) if nresults > 1 then -- Show client name if there are multiple clients - contents[#contents + 1] = string.format('# %s', lsp.get_client_by_id(client_id).name) + contents[#contents + 1] = string.format('# %s', client.name) end if type(result.contents) == 'table' and result.contents.kind == 'plaintext' then if #results1 == 1 then @@ -87,6 +101,22 @@ function M.hover(config) else vim.list_extend(contents, util.convert_input_to_markdown_lines(result.contents)) end + local range = result.range + if range then + local start = range.start + local end_ = range['end'] + local start_idx = util._get_line_byte_from_position(bufnr, start, client.offset_encoding) + local end_idx = util._get_line_byte_from_position(bufnr, end_, client.offset_encoding) + + vim.hl.range( + bufnr, + hover_ns, + 'LspReferenceTarget', + { start.line, start_idx }, + { end_.line, end_idx }, + { priority = vim.hl.priorities.user } + ) + end contents[#contents + 1] = '---' end @@ -100,7 +130,16 @@ function M.hover(config) return end - lsp.util.open_floating_preview(contents, format, config) + local _, winid = lsp.util.open_floating_preview(contents, format, config) + + api.nvim_create_autocmd('WinClosed', { + pattern = tostring(winid), + once = true, + callback = function() + api.nvim_buf_clear_namespace(bufnr, hover_ns, 0, -1) + return true + end, + }) end) end -- cgit From cc6992f1ca443f4da01cb4d57159d4cef37b36b7 Mon Sep 17 00:00:00 2001 From: Peter Aronoff Date: Sun, 17 Nov 2024 16:31:24 -0500 Subject: feat(defaults): dot-repeat [ #31186 Problem: `[` and `]` do not support repetition. Solution: use `operatorfunc` and `g@l` to make these mappings dot repeatable. --- runtime/lua/vim/_buf.lua | 23 +++++++++++++++++++++++ runtime/lua/vim/_defaults.lua | 16 ++++++++-------- 2 files changed, 31 insertions(+), 8 deletions(-) create mode 100644 runtime/lua/vim/_buf.lua (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_buf.lua b/runtime/lua/vim/_buf.lua new file mode 100644 index 0000000000..0631c96f77 --- /dev/null +++ b/runtime/lua/vim/_buf.lua @@ -0,0 +1,23 @@ +local M = {} + +--- Adds one or more blank lines above or below the cursor. +-- TODO: move to _defaults.lua once it is possible to assign a Lua function to options #25672 +--- @param above? boolean Place blank line(s) above the cursor +local function add_blank(above) + local offset = above and 1 or 0 + local repeated = vim.fn['repeat']({ '' }, vim.v.count1) + local linenr = vim.api.nvim_win_get_cursor(0)[1] + vim.api.nvim_buf_set_lines(0, linenr - offset, linenr - offset, true, repeated) +end + +-- TODO: move to _defaults.lua once it is possible to assign a Lua function to options #25672 +function M.space_above() + add_blank(true) +end + +-- TODO: move to _defaults.lua once it is possible to assign a Lua function to options #25672 +function M.space_below() + add_blank() +end + +return M diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua index 25afd83145..06f6ed6829 100644 --- a/runtime/lua/vim/_defaults.lua +++ b/runtime/lua/vim/_defaults.lua @@ -366,16 +366,16 @@ do -- Add empty lines vim.keymap.set('n', '[', function() - local repeated = vim.fn['repeat']({ '' }, vim.v.count1) - local linenr = vim.api.nvim_win_get_cursor(0)[1] - vim.api.nvim_buf_set_lines(0, linenr - 1, linenr - 1, true, repeated) - end, { desc = 'Add empty line above cursor' }) + -- TODO: update once it is possible to assign a Lua function to options #25672 + vim.go.operatorfunc = "v:lua.require'vim._buf'.space_above" + return 'g@l' + end, { expr = true, desc = 'Add empty line above cursor' }) vim.keymap.set('n', ']', function() - local repeated = vim.fn['repeat']({ '' }, vim.v.count1) - local linenr = vim.api.nvim_win_get_cursor(0)[1] - vim.api.nvim_buf_set_lines(0, linenr, linenr, true, repeated) - end, { desc = 'Add empty line below cursor' }) + -- TODO: update once it is possible to assign a Lua function to options #25672 + vim.go.operatorfunc = "v:lua.require'vim._buf'.space_below" + return 'g@l' + end, { expr = true, desc = 'Add empty line below cursor' }) end end -- cgit From 40347f6e27f1594797ecb0b268076a94630e4c1b Mon Sep 17 00:00:00 2001 From: luukvbaal Date: Mon, 18 Nov 2024 15:35:21 +0100 Subject: fix(api): only flush nvim__redraw when necessary #31250 Problem: Not possible to only set a "redraw later" type with nvim__redraw, which seems to be desired for the treesitter highlighter. Solution: Do not update the screen when "flush" is explicitly set to false and only redraw later types are present. In that case, do not call ui_flush() either. --- runtime/lua/vim/lsp/inlay_hint.lua | 6 +++--- runtime/lua/vim/treesitter/highlighter.lua | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua index ca7bc3b022..f1ae9a8e9e 100644 --- a/runtime/lua/vim/lsp/inlay_hint.lua +++ b/runtime/lua/vim/lsp/inlay_hint.lua @@ -65,7 +65,7 @@ function M.on_inlayhint(err, result, ctx) if num_unprocessed == 0 then client_hints[client_id] = {} bufstate.version = ctx.version - api.nvim__redraw({ buf = bufnr, valid = true }) + api.nvim__redraw({ buf = bufnr, valid = true, flush = false }) return end @@ -81,7 +81,7 @@ function M.on_inlayhint(err, result, ctx) client_hints[client_id] = new_lnum_hints bufstate.version = ctx.version - api.nvim__redraw({ buf = bufnr, valid = true }) + api.nvim__redraw({ buf = bufnr, valid = true, flush = false }) end --- |lsp-handler| for the method `workspace/inlayHint/refresh` @@ -215,7 +215,7 @@ local function clear(bufnr) end end api.nvim_buf_clear_namespace(bufnr, namespace, 0, -1) - api.nvim__redraw({ buf = bufnr, valid = true }) + api.nvim__redraw({ buf = bufnr, valid = true, flush = false }) end --- Disable inlay hints for a buffer diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index 05124c29f0..8ce8652f7d 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -219,8 +219,8 @@ end ---@package ---@param changes Range6[] function TSHighlighter:on_changedtree(changes) - for i, ch in ipairs(changes) do - api.nvim__redraw({ buf = self.bufnr, range = { ch[1], ch[4] + 1 }, flush = i == #changes }) + for _, ch in ipairs(changes) do + api.nvim__redraw({ buf = self.bufnr, range = { ch[1], ch[4] + 1 }, flush = false }) end end -- cgit From 989a37a594649528f28432388c0e7e28e8be2753 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 5 Nov 2024 17:34:21 +0000 Subject: refactor(lsp): fold in dynamic_registration code into the client Problem: Capability register logic is spread across 3 files. Solution: - Consolidate (and simplify) logic into the client. - Teach client.supports_method about resolve methods --- runtime/lua/vim/lsp.lua | 8 +- runtime/lua/vim/lsp/_dynamic.lua | 110 --------------------- runtime/lua/vim/lsp/_watchfiles.lua | 10 +- runtime/lua/vim/lsp/buf.lua | 7 +- runtime/lua/vim/lsp/client.lua | 185 +++++++++++++++++++++++++++++++----- runtime/lua/vim/lsp/handlers.lua | 50 ++-------- 6 files changed, 184 insertions(+), 186 deletions(-) delete mode 100644 runtime/lua/vim/lsp/_dynamic.lua (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 42a0ccc3d4..0de3b4ee4d 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -3,7 +3,6 @@ local validate = vim.validate local lsp = vim._defer_require('vim.lsp', { _changetracking = ..., --- @module 'vim.lsp._changetracking' - _dynamic = ..., --- @module 'vim.lsp._dynamic' _snippet_grammar = ..., --- @module 'vim.lsp._snippet_grammar' _tagfunc = ..., --- @module 'vim.lsp._tagfunc' _watchfiles = ..., --- @module 'vim.lsp._watchfiles' @@ -31,6 +30,13 @@ local changetracking = lsp._changetracking ---@nodoc lsp.rpc_response_error = lsp.rpc.rpc_response_error +lsp._resolve_to_request = { + [ms.codeAction_resolve] = ms.textDocument_codeAction, + [ms.codeLens_resolve] = ms.textDocument_codeLens, + [ms.documentLink_resolve] = ms.textDocument_documentLink, + [ms.inlayHint_resolve] = ms.textDocument_inlayHint, +} + -- maps request name to the required server_capability in the client. lsp._request_name_to_capability = { [ms.callHierarchy_incomingCalls] = { 'callHierarchyProvider' }, diff --git a/runtime/lua/vim/lsp/_dynamic.lua b/runtime/lua/vim/lsp/_dynamic.lua deleted file mode 100644 index 27113c0e74..0000000000 --- a/runtime/lua/vim/lsp/_dynamic.lua +++ /dev/null @@ -1,110 +0,0 @@ -local glob = vim.glob - ---- @class lsp.DynamicCapabilities ---- @field capabilities table ---- @field client_id number -local M = {} - ---- @param client_id number ---- @return lsp.DynamicCapabilities -function M.new(client_id) - return setmetatable({ - capabilities = {}, - client_id = client_id, - }, { __index = M }) -end - -function M:supports_registration(method) - local client = vim.lsp.get_client_by_id(self.client_id) - if not client then - return false - end - local capability = vim.tbl_get(client.capabilities, unpack(vim.split(method, '/'))) - return type(capability) == 'table' and capability.dynamicRegistration -end - ---- @param registrations lsp.Registration[] -function M:register(registrations) - -- remove duplicates - self:unregister(registrations) - for _, reg in ipairs(registrations) do - local method = reg.method - if not self.capabilities[method] then - self.capabilities[method] = {} - end - table.insert(self.capabilities[method], reg) - end -end - ---- @param unregisterations lsp.Unregistration[] -function M:unregister(unregisterations) - for _, unreg in ipairs(unregisterations) do - local method = unreg.method - if not self.capabilities[method] then - return - end - local id = unreg.id - for i, reg in ipairs(self.capabilities[method]) do - if reg.id == id then - table.remove(self.capabilities[method], i) - break - end - end - end -end - ---- @param method string ---- @param opts? {bufnr: integer?} ---- @return lsp.Registration? (table|nil) the registration if found -function M:get(method, opts) - opts = opts or {} - opts.bufnr = opts.bufnr or vim.api.nvim_get_current_buf() - for _, reg in ipairs(self.capabilities[method] or {}) do - if not reg.registerOptions then - return reg - end - local documentSelector = reg.registerOptions.documentSelector - if not documentSelector then - return reg - end - if self:match(opts.bufnr, documentSelector) then - return reg - end - end -end - ---- @param method string ---- @param opts? {bufnr: integer?} -function M:supports(method, opts) - return self:get(method, opts) ~= nil -end - ---- @param bufnr number ---- @param documentSelector lsp.DocumentSelector ---- @private -function M:match(bufnr, documentSelector) - local client = vim.lsp.get_client_by_id(self.client_id) - if not client then - return false - end - local language = client.get_language_id(bufnr, vim.bo[bufnr].filetype) - local uri = vim.uri_from_bufnr(bufnr) - local fname = vim.uri_to_fname(uri) - for _, filter in ipairs(documentSelector) do - local matches = true - if filter.language and language ~= filter.language then - matches = false - end - if matches and filter.scheme and not vim.startswith(uri, filter.scheme .. ':') then - matches = false - end - if matches and filter.pattern and not glob.to_lpeg(filter.pattern):match(fname) then - matches = false - end - if matches then - return true - end - end -end - -return M diff --git a/runtime/lua/vim/lsp/_watchfiles.lua b/runtime/lua/vim/lsp/_watchfiles.lua index 98e9818bcd..c4cdb5aea8 100644 --- a/runtime/lua/vim/lsp/_watchfiles.lua +++ b/runtime/lua/vim/lsp/_watchfiles.lua @@ -44,9 +44,8 @@ M._poll_exclude_pattern = glob.to_lpeg('**/.git/{objects,subtree-cache}/**') --- Registers the workspace/didChangeWatchedFiles capability dynamically. --- ---@param reg lsp.Registration LSP Registration object. ----@param ctx lsp.HandlerContext Context from the |lsp-handler|. -function M.register(reg, ctx) - local client_id = ctx.client_id +---@param client_id integer Client ID. +function M.register(reg, client_id) local client = assert(vim.lsp.get_client_by_id(client_id), 'Client must be running') -- Ill-behaved servers may not honor the client capability and try to register -- anyway, so ignore requests when the user has opted out of the feature. @@ -155,9 +154,8 @@ end --- Unregisters the workspace/didChangeWatchedFiles capability dynamically. --- ---@param unreg lsp.Unregistration LSP Unregistration object. ----@param ctx lsp.HandlerContext Context from the |lsp-handler|. -function M.unregister(unreg, ctx) - local client_id = ctx.client_id +---@param client_id integer Client ID. +function M.unregister(unreg, client_id) local client_cancels = cancels[client_id] local reg_cancels = client_cancels[unreg.id] while #reg_cancels > 0 do diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index a75e322e90..6383855a30 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -1131,12 +1131,7 @@ local function on_code_action_results(results, opts) local action = choice.action local bufnr = assert(choice.ctx.bufnr, 'Must have buffer number') - local reg = client.dynamic_capabilities:get(ms.textDocument_codeAction, { bufnr = bufnr }) - - local supports_resolve = vim.tbl_get(reg or {}, 'registerOptions', 'resolveProvider') - or client.supports_method(ms.codeAction_resolve) - - if not action.edit and client and supports_resolve then + if not action.edit and client.supports_method(ms.codeAction_resolve) then client.request(ms.codeAction_resolve, action, function(err, resolved_action) if err then if action.command then diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index 2718f40c96..ba12447c40 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -91,8 +91,8 @@ local validate = vim.validate --- (default: client-id) --- @field name? string --- ---- Language ID as string. Defaults to the filetype. ---- @field get_language_id? fun(bufnr: integer, filetype: string): string +--- Language ID as string. Defaults to the buffer filetype. +--- @field get_language_id? fun(bufnr: integer, filetype?: string): string --- --- The encoding that the LSP server expects. Client does not verify this is correct. --- @field offset_encoding? 'utf-8'|'utf-16'|'utf-32' @@ -212,10 +212,11 @@ local validate = vim.validate --- A table with flags for the client. The current (experimental) flags are: --- @field flags vim.lsp.Client.Flags --- ---- @field get_language_id fun(bufnr: integer, filetype: string): string +--- @field get_language_id fun(bufnr: integer, filetype?: string): string --- --- The capabilities provided by the client (editor or tool) --- @field capabilities lsp.ClientCapabilities +--- @field private registrations table --- @field dynamic_capabilities lsp.DynamicCapabilities --- --- Sends a request to the server. @@ -339,10 +340,10 @@ end --- By default, get_language_id just returns the exact filetype it is passed. --- It is possible to pass in something that will calculate a different filetype, --- to be sent by the client. ---- @param _bufnr integer ---- @param filetype string -local function default_get_language_id(_bufnr, filetype) - return filetype +--- @param bufnr integer +--- @param filetype? string +local function default_get_language_id(bufnr, filetype) + return filetype or vim.bo[bufnr].filetype end --- Validates a client configuration as given to |vim.lsp.start_client()|. @@ -403,18 +404,16 @@ local function get_name(id, config) return tostring(id) end ---- @param workspace_folders lsp.WorkspaceFolder[]? ---- @param root_dir string? +--- @param workspace_folders string|lsp.WorkspaceFolder[]? --- @return lsp.WorkspaceFolder[]? -local function get_workspace_folders(workspace_folders, root_dir) - if workspace_folders then +local function get_workspace_folders(workspace_folders) + if type(workspace_folders) == 'table' then return workspace_folders - end - if root_dir then + elseif type(workspace_folders) == 'string' then return { { - uri = vim.uri_from_fname(root_dir), - name = root_dir, + uri = vim.uri_from_fname(workspace_folders), + name = workspace_folders, }, } end @@ -451,13 +450,13 @@ function Client.create(config) requests = {}, attached_buffers = {}, server_capabilities = {}, - dynamic_capabilities = lsp._dynamic.new(id), + registrations = {}, commands = config.commands or {}, settings = config.settings or {}, flags = config.flags or {}, get_language_id = config.get_language_id or default_get_language_id, capabilities = config.capabilities or lsp.protocol.make_client_capabilities(), - workspace_folders = get_workspace_folders(config.workspace_folders, config.root_dir), + workspace_folders = get_workspace_folders(config.workspace_folders or config.root_dir), root_dir = config.root_dir, _before_init_cb = config.before_init, _on_init_cbs = ensure_list(config.on_init), @@ -478,6 +477,28 @@ function Client.create(config) messages = { name = name, messages = {}, progress = {}, status = {} }, } + --- @class lsp.DynamicCapabilities + --- @nodoc + self.dynamic_capabilities = { + capabilities = self.registrations, + client_id = id, + register = function(_, registrations) + return self:_register_dynamic(registrations) + end, + unregister = function(_, unregistrations) + return self:_unregister_dynamic(unregistrations) + end, + get = function(_, method, opts) + return self:_get_registration(method, opts and opts.bufnr) + end, + supports_registration = function(_, method) + return self:_supports_registration(method) + end, + supports = function(_, method, opts) + return self:_get_registration(method, opts and opts.bufnr) ~= nil + end, + } + self.request = method_wrapper(self, Client._request) self.request_sync = method_wrapper(self, Client._request_sync) self.notify = method_wrapper(self, Client._notify) @@ -846,6 +867,100 @@ function Client:_stop(force) end) end +--- Get options for a method that is registered dynamically. +--- @param method string +function Client:_supports_registration(method) + local capability = vim.tbl_get(self.capabilities, unpack(vim.split(method, '/'))) + return type(capability) == 'table' and capability.dynamicRegistration +end + +--- @private +--- @param registrations lsp.Registration[] +function Client:_register_dynamic(registrations) + -- remove duplicates + self:_unregister_dynamic(registrations) + for _, reg in ipairs(registrations) do + local method = reg.method + if not self.registrations[method] then + self.registrations[method] = {} + end + table.insert(self.registrations[method], reg) + end +end + +--- @param registrations lsp.Registration[] +function Client:_register(registrations) + self:_register_dynamic(registrations) + + local unsupported = {} --- @type string[] + + for _, reg in ipairs(registrations) do + local method = reg.method + if method == ms.workspace_didChangeWatchedFiles then + vim.lsp._watchfiles.register(reg, self.id) + elseif not self:_supports_registration(method) then + unsupported[#unsupported + 1] = method + end + end + + if #unsupported > 0 then + local warning_tpl = 'The language server %s triggers a registerCapability ' + .. 'handler for %s despite dynamicRegistration set to false. ' + .. 'Report upstream, this warning is harmless' + log.warn(string.format(warning_tpl, self.name, table.concat(unsupported, ', '))) + end +end + +--- @private +--- @param unregistrations lsp.Unregistration[] +function Client:_unregister_dynamic(unregistrations) + for _, unreg in ipairs(unregistrations) do + local sreg = self.registrations[unreg.method] + -- Unegister dynamic capability + for i, reg in ipairs(sreg or {}) do + if reg.id == unreg.id then + table.remove(sreg, i) + break + end + end + end +end + +--- @param unregistrations lsp.Unregistration[] +function Client:_unregister(unregistrations) + self:_unregister_dynamic(unregistrations) + for _, unreg in ipairs(unregistrations) do + if unreg.method == ms.workspace_didChangeWatchedFiles then + vim.lsp._watchfiles.unregister(unreg, self.id) + end + end +end + +--- @param method string +--- @param bufnr? integer +--- @return lsp.Registration? +function Client:_get_registration(method, bufnr) + bufnr = bufnr or vim.api.nvim_get_current_buf() + for _, reg in ipairs(self.registrations[method] or {}) do + if not reg.registerOptions or not reg.registerOptions.documentSelector then + return reg + end + local documentSelector = reg.registerOptions.documentSelector + local language = self.get_language_id(bufnr) + local uri = vim.uri_from_bufnr(bufnr) + local fname = vim.uri_to_fname(uri) + for _, filter in ipairs(documentSelector) do + if + not (filter.language and language ~= filter.language) + and not (filter.scheme and not vim.startswith(uri, filter.scheme .. ':')) + and not (filter.pattern and not vim.glob.to_lpeg(filter.pattern):match(fname)) + then + return reg + end + end + end +end + --- @private --- Checks whether a client is stopped. --- @@ -908,12 +1023,11 @@ function Client:_text_document_did_open_handler(bufnr) return end - local filetype = vim.bo[bufnr].filetype self.notify(ms.textDocument_didOpen, { textDocument = { version = lsp.util.buf_versions[bufnr], uri = vim.uri_from_bufnr(bufnr), - languageId = self.get_language_id(bufnr, filetype), + languageId = self.get_language_id(bufnr), text = lsp._buf_get_full_text(bufnr), }, }) @@ -978,12 +1092,37 @@ function Client:_supports_method(method, opts) if vim.tbl_get(self.server_capabilities, unpack(required_capability)) then return true end - if self.dynamic_capabilities:supports_registration(method) then - return self.dynamic_capabilities:supports(method, opts) + + local rmethod = lsp._resolve_to_request[method] + if rmethod then + if self:_supports_registration(rmethod) then + local reg = self:_get_registration(rmethod, opts and opts.bufnr) + return vim.tbl_get(reg or {}, 'registerOptions', 'resolveProvider') or false + end + else + if self:_supports_registration(method) then + return self:_get_registration(method, opts and opts.bufnr) ~= nil + end end return false end +--- Get options for a method that is registered dynamically. +--- @param method string +--- @param bufnr? integer +--- @return lsp.LSPAny? +function Client:_get_registration_options(method, bufnr) + if not self:_supports_registration(method) then + return + end + + local reg = self:_get_registration(method, bufnr) + + if reg then + return reg.registerOptions + end +end + --- @private --- Handles a notification sent by an LSP server by invoking the --- corresponding handler. @@ -1061,7 +1200,7 @@ function Client:_add_workspace_folder(dir) end end - local wf = assert(get_workspace_folders(nil, dir)) + local wf = assert(get_workspace_folders(dir)) self:_notify(ms.workspace_didChangeWorkspaceFolders, { event = { added = wf, removed = {} }, @@ -1076,7 +1215,7 @@ end --- Remove a directory to the workspace folders. --- @param dir string? function Client:_remove_workspace_folder(dir) - local wf = assert(get_workspace_folders(nil, dir)) + local wf = assert(get_workspace_folders(dir)) self:_notify(ms.workspace_didChangeWorkspaceFolders, { event = { added = {}, removed = wf }, diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 2b7aefe0e1..5c28d88b38 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -122,46 +122,19 @@ end --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_registerCapability --- @param params lsp.RegistrationParams RSC[ms.client_registerCapability] = function(_, params, ctx) - local client_id = ctx.client_id - local client = assert(vim.lsp.get_client_by_id(client_id)) - - client.dynamic_capabilities:register(params.registrations) - for bufnr, _ in pairs(client.attached_buffers) do + local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) + client:_register(params.registrations) + for bufnr in pairs(client.attached_buffers) do vim.lsp._set_defaults(client, bufnr) end - - ---@type string[] - local unsupported = {} - for _, reg in ipairs(params.registrations) do - if reg.method == ms.workspace_didChangeWatchedFiles then - vim.lsp._watchfiles.register(reg, ctx) - elseif not client.dynamic_capabilities:supports_registration(reg.method) then - unsupported[#unsupported + 1] = reg.method - end - end - if #unsupported > 0 then - local warning_tpl = 'The language server %s triggers a registerCapability ' - .. 'handler for %s despite dynamicRegistration set to false. ' - .. 'Report upstream, this warning is harmless' - local client_name = client and client.name or string.format('id=%d', client_id) - local warning = string.format(warning_tpl, client_name, table.concat(unsupported, ', ')) - log.warn(warning) - end return vim.NIL end --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_unregisterCapability --- @param params lsp.UnregistrationParams RSC[ms.client_unregisterCapability] = function(_, params, ctx) - local client_id = ctx.client_id - local client = assert(vim.lsp.get_client_by_id(client_id)) - client.dynamic_capabilities:unregister(params.unregisterations) - - for _, unreg in ipairs(params.unregisterations) do - if unreg.method == ms.workspace_didChangeWatchedFiles then - vim.lsp._watchfiles.unregister(unreg, ctx) - end - end + local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) + client:_unregister(params.unregisterations) return vim.NIL end @@ -173,8 +146,7 @@ RSC[ms.workspace_applyEdit] = function(_, params, ctx) 'workspace/applyEdit must be called with `ApplyWorkspaceEditParams`. Server is violating the specification' ) -- TODO(ashkan) Do something more with label? - local client_id = ctx.client_id - local client = assert(vim.lsp.get_client_by_id(client_id)) + local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) if params.label then print('Workspace edit', params.label) end @@ -196,12 +168,11 @@ end --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_configuration --- @param params lsp.ConfigurationParams RSC[ms.workspace_configuration] = function(_, params, ctx) - local client_id = ctx.client_id - local client = vim.lsp.get_client_by_id(client_id) + local client = vim.lsp.get_client_by_id(ctx.client_id) if not client then err_message( 'LSP[', - client_id, + ctx.client_id, '] client has shut down after sending a workspace/configuration request' ) return @@ -229,10 +200,9 @@ end --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_workspaceFolders RSC[ms.workspace_workspaceFolders] = function(_, _, ctx) - local client_id = ctx.client_id - local client = vim.lsp.get_client_by_id(client_id) + local client = vim.lsp.get_client_by_id(ctx.client_id) if not client then - err_message('LSP[id=', client_id, '] client has shut down after sending the message') + err_message('LSP[id=', ctx.client_id, '] client has shut down after sending the message') return end return client.workspace_folders or vim.NIL -- cgit From c12be1249f5e44289acdfeaf0f059c7942593275 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Tue, 19 Nov 2024 08:41:02 +0100 Subject: vim-patch:9.1.0873: filetype: Vivado files are not recognized Problem: filetype: Vivado files are not recognized Solution: detect '*.mss' files as 'mss' filetype (Wu, Zhenyu) references: https://docs.amd.com/r/2020.2-English/ug1400-vitis-embedded/Microprocessor-Software-Specification-MSS closes: vim/vim#15907 https://github.com/vim/vim/commit/a87462a498a883e12ad7699b26bd28f4600b68c6 Co-authored-by: Wu, Zhenyu --- runtime/lua/vim/filetype.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 3b3c4481f2..e1e73d63fe 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -792,6 +792,7 @@ local extension = { mof = 'msidl', odl = 'msidl', msql = 'msql', + mss = 'mss', mu = 'mupad', mush = 'mush', mustache = 'mustache', -- cgit From 081beb3659bd6d8efc3e977a160b1e72becbd8a2 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 19 Nov 2024 10:11:40 +0000 Subject: fix(lsp): restore get_language_id behaviour Ensure filetype is always passed. Fixes #31262 --- runtime/lua/vim/lsp/client.lua | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index ba12447c40..11ecb87507 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -92,7 +92,7 @@ local validate = vim.validate --- @field name? string --- --- Language ID as string. Defaults to the buffer filetype. ---- @field get_language_id? fun(bufnr: integer, filetype?: string): string +--- @field get_language_id? fun(bufnr: integer, filetype: string): string --- --- The encoding that the LSP server expects. Client does not verify this is correct. --- @field offset_encoding? 'utf-8'|'utf-16'|'utf-32' @@ -212,7 +212,7 @@ local validate = vim.validate --- A table with flags for the client. The current (experimental) flags are: --- @field flags vim.lsp.Client.Flags --- ---- @field get_language_id fun(bufnr: integer, filetype?: string): string +--- @field get_language_id fun(bufnr: integer, filetype: string): string --- --- The capabilities provided by the client (editor or tool) --- @field capabilities lsp.ClientCapabilities @@ -340,10 +340,10 @@ end --- By default, get_language_id just returns the exact filetype it is passed. --- It is possible to pass in something that will calculate a different filetype, --- to be sent by the client. ---- @param bufnr integer ---- @param filetype? string -local function default_get_language_id(bufnr, filetype) - return filetype or vim.bo[bufnr].filetype +--- @param _bufnr integer +--- @param filetype string +local function default_get_language_id(_bufnr, filetype) + return filetype end --- Validates a client configuration as given to |vim.lsp.start_client()|. @@ -936,6 +936,11 @@ function Client:_unregister(unregistrations) end end +--- @private +function Client:_get_language_id(bufnr) + return self.get_language_id(bufnr, vim.bo[bufnr].filetype) +end + --- @param method string --- @param bufnr? integer --- @return lsp.Registration? @@ -946,7 +951,7 @@ function Client:_get_registration(method, bufnr) return reg end local documentSelector = reg.registerOptions.documentSelector - local language = self.get_language_id(bufnr) + local language = self:_get_language_id(bufnr) local uri = vim.uri_from_bufnr(bufnr) local fname = vim.uri_to_fname(uri) for _, filter in ipairs(documentSelector) do @@ -1027,7 +1032,7 @@ function Client:_text_document_did_open_handler(bufnr) textDocument = { version = lsp.util.buf_versions[bufnr], uri = vim.uri_from_bufnr(bufnr), - languageId = self.get_language_id(bufnr), + languageId = self:_get_language_id(bufnr), text = lsp._buf_get_full_text(bufnr), }, }) -- cgit