aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim/lsp/inlay_hint.lua
diff options
context:
space:
mode:
authorLW <git@llllvvuu.dev>2023-11-12 04:54:27 -0800
committerGitHub <noreply@github.com>2023-11-12 04:54:27 -0800
commit448907f65d6709fa234d8366053e33311a01bdb9 (patch)
tree17dcb47f3f7481f7ded11e0675309461c7a15973 /runtime/lua/vim/lsp/inlay_hint.lua
parentad3568a70167ceb870931650afb7dcaed88640ec (diff)
downloadrneovim-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.lua163
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