diff options
author | LW <git@llllvvuu.dev> | 2023-11-12 04:54:27 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-11-12 04:54:27 -0800 |
commit | 448907f65d6709fa234d8366053e33311a01bdb9 (patch) | |
tree | 17dcb47f3f7481f7ded11e0675309461c7a15973 /runtime/lua/vim/lsp/inlay_hint.lua | |
parent | ad3568a70167ceb870931650afb7dcaed88640ec (diff) | |
download | rneovim-448907f65d6709fa234d8366053e33311a01bdb9.tar.gz rneovim-448907f65d6709fa234d8366053e33311a01bdb9.tar.bz2 rneovim-448907f65d6709fa234d8366053e33311a01bdb9.zip |
feat(lsp)!: vim.lsp.inlay_hint.get(), enable(), is_enabled() #25512
refactor!: `vim.lsp.inlay_hint()` -> `vim.lsp.inlay_hint.enable()`
Problem:
The LSP specification allows inlay hints to include tooltips, clickable
label parts, and code actions; but Neovim provides no API to query for
these.
Solution:
Add minimal viable extension point from which plugins can query for
inlay hints in a range, in order to build functionality on top of.
Possible Next Steps
---
- Add `virt_text_idx` field to `vim.fn.getmousepos()` return value, for
usage in mappings of `<LeftMouse>`, `<C-LeftMouse>`, etc
Diffstat (limited to 'runtime/lua/vim/lsp/inlay_hint.lua')
-rw-r--r-- | runtime/lua/vim/lsp/inlay_hint.lua | 163 |
1 files changed, 131 insertions, 32 deletions
diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua index 7b58188c53..cdda5dcc17 100644 --- a/runtime/lua/vim/lsp/inlay_hint.lua +++ b/runtime/lua/vim/lsp/inlay_hint.lua @@ -98,6 +98,107 @@ function M.on_refresh(err, _, ctx, _) return vim.NIL end +--- @class vim.lsp.inlay_hint.get.filter +--- @field bufnr integer? +--- @field range lsp.Range? +--- +--- @class vim.lsp.inlay_hint.get.ret +--- @field bufnr integer +--- @field client_id integer +--- @field inlay_hint lsp.InlayHint + +--- Get the list of inlay hints, (optionally) restricted by buffer, client, or range. +--- +--- Example usage: +--- +--- ```lua +--- local hint = vim.lsp.inlay_hint.get({ bufnr = 0 })[1] -- 0 for current buffer +--- +--- local client = vim.lsp.get_client_by_id(hint.client_id) +--- resolved_hint = client.request_sync('inlayHint/resolve', hint.inlay_hint, 100, 0).result +--- vim.lsp.util.apply_text_edits(resolved_hint.textEdits, 0, client.encoding) +--- +--- location = resolved_hint.label[1].location +--- client.request('textDocument/hover', { +--- textDocument = { uri = location.uri }, +--- position = location.range.start, +--- }) +--- ``` +--- +--- @param filter vim.lsp.inlay_hint.get.filter? +--- Optional filters |kwargs|: +--- - bufnr (integer?): 0 for current buffer +--- - range (lsp.Range?) +--- +--- @return vim.lsp.inlay_hint.get.ret[] +--- Each list item is a table with the following fields: +--- - bufnr (integer) +--- - client_id (integer) +--- - inlay_hint (lsp.InlayHint) +function M.get(filter) + vim.validate({ filter = { filter, 'table', true } }) + filter = filter or {} + + local bufnr = filter.bufnr + if not bufnr then + --- @type vim.lsp.inlay_hint.get.ret[] + local hints = {} + --- @param buf integer + vim.tbl_map(function(buf) + vim.list_extend(hints, M.get(vim.tbl_extend('keep', { bufnr = buf }, filter))) + end, vim.api.nvim_list_bufs()) + return hints + elseif bufnr == 0 then + bufnr = api.nvim_get_current_buf() + end + + local bufstate = bufstates[bufnr] + if not (bufstate and bufstate.client_hint) then + return {} + end + + local clients = vim.lsp.get_clients({ + bufnr = bufnr, + method = ms.textDocument_inlayHint, + }) + if #clients == 0 then + return {} + end + + local range = filter.range + if not range then + range = { + start = { line = 0, character = 0 }, + ['end'] = { line = api.nvim_buf_line_count(bufnr), character = 0 }, + } + end + + --- @type vim.lsp.inlay_hint.get.ret[] + local hints = {} + for _, client in pairs(clients) do + local hints_by_lnum = bufstate.client_hint[client.id] + if hints_by_lnum then + for lnum = range.start.line, range['end'].line do + local line_hints = hints_by_lnum[lnum] or {} + for _, hint in pairs(line_hints) do + local line, char = hint.position.line, hint.position.character + if + (line > range.start.line or char >= range.start.character) + and (line < range['end'].line or char <= range['end'].character) + then + table.insert(hints, { + bufnr = bufnr, + client_id = client.id, + inlay_hint = hint, + }) + end + end + end + end + end + return hints +end + --- Clear inlay hints ---@param bufnr (integer) Buffer handle, or 0 for current local function clear(bufnr) @@ -120,8 +221,8 @@ local function clear(bufnr) end --- Disable inlay hints for a buffer ----@param bufnr (integer) Buffer handle, or 0 for current -local function disable(bufnr) +---@param bufnr (integer|nil) Buffer handle, or 0 or nil for current +local function _disable(bufnr) if bufnr == nil or bufnr == 0 then bufnr = api.nvim_get_current_buf() end @@ -142,8 +243,8 @@ local function _refresh(bufnr, opts) end --- Enable inlay hints for a buffer ----@param bufnr (integer) Buffer handle, or 0 for current -local function enable(bufnr) +---@param bufnr (integer|nil) Buffer handle, or 0 or nil for current +local function _enable(bufnr) if bufnr == nil or bufnr == 0 then bufnr = api.nvim_get_current_buf() end @@ -175,7 +276,7 @@ local function enable(bufnr) end end, on_detach = function(_, cb_bufnr) - disable(cb_bufnr) + _disable(cb_bufnr) end, }) api.nvim_create_autocmd('LspDetach', { @@ -188,7 +289,7 @@ local function enable(bufnr) return c.id ~= args.data.client_id end) then - disable(bufnr) + _disable(bufnr) end end, group = augroup, @@ -199,20 +300,6 @@ local function enable(bufnr) end end ---- Toggle inlay hints for a buffer ----@param bufnr (integer) Buffer handle, or 0 for current -local function toggle(bufnr) - if bufnr == nil or bufnr == 0 then - bufnr = api.nvim_get_current_buf() - end - local bufstate = bufstates[bufnr] - if bufstate and bufstate.enabled then - disable(bufnr) - else - enable(bufnr) - end -end - api.nvim_set_decoration_provider(namespace, { on_win = function(_, _, bufnr, topline, botline) local bufstate = bufstates[bufnr] @@ -260,15 +347,27 @@ api.nvim_set_decoration_provider(namespace, { end, }) -return setmetatable(M, { - __call = function(_, bufnr, enable_) - vim.validate({ enable = { enable_, { 'boolean', 'nil' } }, bufnr = { bufnr, 'number' } }) - if enable_ then - enable(bufnr) - elseif enable_ == false then - disable(bufnr) - else - toggle(bufnr) - end - end, -}) +--- @param bufnr (integer|nil) Buffer handle, or 0 or nil for current +--- @return boolean +function M.is_enabled(bufnr) + vim.validate({ bufnr = { bufnr, 'number', true } }) + if bufnr == nil or bufnr == 0 then + bufnr = api.nvim_get_current_buf() + end + return bufstates[bufnr] and bufstates[bufnr].enabled or false +end + +--- Enable/disable/toggle inlay hints for a buffer +--- +--- @param bufnr (integer|nil) Buffer handle, or 0 or nil for current +--- @param enable (boolean|nil) true/nil to enable, false to disable +function M.enable(bufnr, enable) + vim.validate({ enable = { enable, 'boolean', true }, bufnr = { bufnr, 'number', true } }) + if enable == false then + _disable(bufnr) + else + _enable(bufnr) + end +end + +return M |