feat(lsp): highlight hover target/range #31110

**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 <mfussenegger@users.noreply.github.com>
This commit is contained in:
Riley Bruins 2024-11-17 12:31:32 -08:00 committed by GitHub
parent 235cb5bc5f
commit 44229bb85b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 60 additions and 7 deletions

View File

@ -344,6 +344,9 @@ LspReferenceText used for highlighting "text" references
LspReferenceRead used for highlighting "read" references LspReferenceRead used for highlighting "read" references
*hl-LspReferenceWrite* *hl-LspReferenceWrite*
LspReferenceWrite used for highlighting "write" references LspReferenceWrite used for highlighting "write" references
*hl-LspReferenceTarget*
LspReferenceTarget used for highlighting reference targets (e.g. in a
hover range)
*hl-LspInlayHint* *hl-LspInlayHint*
LspInlayHint used for highlighting inlay hints LspInlayHint used for highlighting inlay hints
@ -1335,6 +1338,14 @@ hover({config}) *vim.lsp.buf.hover()*
mappings are available as usual, except that "q" dismisses the window. You mappings are available as usual, except that "q" dismisses the window. You
can scroll the contents the same as you would any other buffer. 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,
})
<
Parameters: ~ Parameters: ~
• {config} (`vim.lsp.buf.hover.Opts?`) See |vim.lsp.buf.hover.Opts|. • {config} (`vim.lsp.buf.hover.Opts?`) See |vim.lsp.buf.hover.Opts|.

View File

@ -216,6 +216,8 @@ LSP
• |vim.lsp.buf.signature_help()| can now cycle through different signatures • |vim.lsp.buf.signature_help()| can now cycle through different signatures
using `<C-s>` and also support multiple clients. using `<C-s>` and also support multiple clients.
• The client now supports `'utf-8'` and `'utf-32'` position encodings. • The client now supports `'utf-8'` and `'utf-32'` position encodings.
• |vim.lsp.buf.hover()| now highlights hover ranges using the
|hl-LspReferenceTarget| highlight group.
LUA LUA

View File

@ -20,6 +20,8 @@ local function client_positional_params(params)
end end
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 --- @class vim.lsp.buf.hover.Opts : vim.lsp.util.open_floating_preview.Opts
--- @field silent? boolean --- @field silent? boolean
@ -30,13 +32,24 @@ end
--- In the floating window, all commands and mappings are available as usual, --- In the floating window, all commands and mappings are available as usual,
--- except that "q" dismisses the window. --- except that "q" dismisses the window.
--- You can scroll the contents the same as you would any other buffer. --- 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 --- @param config? vim.lsp.buf.hover.Opts
function M.hover(config) function M.hover(config)
config = config or {} config = config or {}
config.focus_id = ms.textDocument_hover config.focus_id = ms.textDocument_hover
lsp.buf_request_all(0, ms.textDocument_hover, client_positional_params(), function(results, ctx) 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. -- Ignore result since buffer changed. This happens for slow language servers.
return return
end end
@ -67,9 +80,10 @@ function M.hover(config)
local format = 'markdown' local format = 'markdown'
for client_id, result in pairs(results1) do for client_id, result in pairs(results1) do
local client = assert(lsp.get_client_by_id(client_id))
if nresults > 1 then if nresults > 1 then
-- Show client name if there are multiple clients -- 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 end
if type(result.contents) == 'table' and result.contents.kind == 'plaintext' then if type(result.contents) == 'table' and result.contents.kind == 'plaintext' then
if #results1 == 1 then if #results1 == 1 then
@ -87,6 +101,22 @@ function M.hover(config)
else else
vim.list_extend(contents, util.convert_input_to_markdown_lines(result.contents)) vim.list_extend(contents, util.convert_input_to_markdown_lines(result.contents))
end 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] = '---' contents[#contents + 1] = '---'
end end
@ -100,7 +130,16 @@ function M.hover(config)
return return
end 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)
end end

View File

@ -215,6 +215,7 @@ static const char *highlight_init_both[] = {
"default link LspReferenceRead LspReferenceText", "default link LspReferenceRead LspReferenceText",
"default link LspReferenceText Visual", "default link LspReferenceText Visual",
"default link LspReferenceWrite LspReferenceText", "default link LspReferenceWrite LspReferenceText",
"default link LspReferenceTarget LspReferenceText",
"default link LspSignatureActiveParameter Visual", "default link LspSignatureActiveParameter Visual",
"default link SnippetTabstop Visual", "default link SnippetTabstop Visual",