Skip to content

Commit

Permalink
feat(lsp): highlight hover target/range neovim#31110
Browse files Browse the repository at this point in the history
**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 <[email protected]>
  • Loading branch information
ribru17 and mfussenegger authored Nov 17, 2024
1 parent 235cb5b commit 44229bb
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 7 deletions.
19 changes: 15 additions & 4 deletions runtime/doc/lsp.txt
Original file line number Diff line number Diff line change
Expand Up @@ -338,13 +338,16 @@ Highlight groups that are meant to be used by |vim.lsp.buf.document_highlight()|
You can see more about the differences in types here:
https://microsoft.github.io/language-server-protocol/specification#textDocument_documentHighlight

*hl-LspReferenceText*
*hl-LspReferenceText*
LspReferenceText used for highlighting "text" references
*hl-LspReferenceRead*
*hl-LspReferenceRead*
LspReferenceRead used for highlighting "read" references
*hl-LspReferenceWrite*
*hl-LspReferenceWrite*
LspReferenceWrite used for highlighting "write" references
*hl-LspInlayHint*
*hl-LspReferenceTarget*
LspReferenceTarget used for highlighting reference targets (e.g. in a
hover range)
*hl-LspInlayHint*
LspInlayHint used for highlighting inlay hints


Expand Down Expand Up @@ -1335,6 +1338,14 @@ hover({config}) *vim.lsp.buf.hover()*
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,
})
<

Parameters: ~
{config} (`vim.lsp.buf.hover.Opts?`) See |vim.lsp.buf.hover.Opts|.

Expand Down
2 changes: 2 additions & 0 deletions runtime/doc/news.txt
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ LSP
|vim.lsp.buf.signature_help()| can now cycle through different signatures
using `<C-s>` and also support multiple clients.
• 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

Expand Down
45 changes: 42 additions & 3 deletions runtime/lua/vim/lsp/buf.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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

Expand All @@ -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

Expand Down
1 change: 1 addition & 0 deletions src/nvim/highlight_group.c
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ static const char *highlight_init_both[] = {
"default link LspReferenceRead LspReferenceText",
"default link LspReferenceText Visual",
"default link LspReferenceWrite LspReferenceText",
"default link LspReferenceTarget LspReferenceText",
"default link LspSignatureActiveParameter Visual",
"default link SnippetTabstop Visual",

Expand Down

0 comments on commit 44229bb

Please sign in to comment.