aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAmit Singh <29333147+amitds1997@users.noreply.github.com>2024-07-17 20:14:53 +0530
committerGitHub <noreply@github.com>2024-07-17 16:44:53 +0200
commite29f245a10821fcce454f7ede684aa0dd64efc33 (patch)
treeee0db4bb261bf3cea6cbf817daa6eadf62fa52b0
parent0500804df52c64bd250a75ed94f4c414a85ca52b (diff)
downloadrneovim-e29f245a10821fcce454f7ede684aa0dd64efc33.tar.gz
rneovim-e29f245a10821fcce454f7ede684aa0dd64efc33.tar.bz2
rneovim-e29f245a10821fcce454f7ede684aa0dd64efc33.zip
fix(lsp): inlay hints are rendered in the correct order (#29707)
Problem: When there are multiple inlay hints present at the same position, they should be rendered in the order they are received in the response from LSP as per the LSP spec. Currently, this is not respected. Solution: Gather all hints for a given position, and then set it in a single extmark call instead of multiple set_extmark calls. This leads to fewer extmark calls and correct inlay hints being rendered.
-rw-r--r--runtime/lua/vim/lsp/inlay_hint.lua19
-rw-r--r--test/functional/plugin/lsp/inlay_hint_spec.lua117
2 files changed, 107 insertions, 29 deletions
diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua
index aa84294cc4..1e224d1bef 100644
--- a/runtime/lua/vim/lsp/inlay_hint.lua
+++ b/runtime/lua/vim/lsp/inlay_hint.lua
@@ -336,6 +336,8 @@ api.nvim_set_decoration_provider(namespace, {
for lnum = topline, botline do
if bufstate.applied[lnum] ~= bufstate.version then
api.nvim_buf_clear_namespace(bufnr, namespace, lnum, lnum + 1)
+
+ local hint_virtual_texts = {} --- @type table<integer, [string, string?][]>
for _, lnum_hints in pairs(client_hints) do
local hints = lnum_hints[lnum] or {}
for _, hint in pairs(hints) do
@@ -348,7 +350,7 @@ api.nvim_set_decoration_provider(namespace, {
text = text .. part.value
end
end
- local vt = {} --- @type [string, string?][]
+ local vt = hint_virtual_texts[hint.position.character] or {}
if hint.paddingLeft then
vt[#vt + 1] = { ' ' }
end
@@ -356,13 +358,18 @@ api.nvim_set_decoration_provider(namespace, {
if hint.paddingRight then
vt[#vt + 1] = { ' ' }
end
- api.nvim_buf_set_extmark(bufnr, namespace, lnum, hint.position.character, {
- virt_text_pos = 'inline',
- ephemeral = false,
- virt_text = vt,
- })
+ hint_virtual_texts[hint.position.character] = vt
end
end
+
+ for pos, vt in pairs(hint_virtual_texts) do
+ api.nvim_buf_set_extmark(bufnr, namespace, lnum, pos, {
+ virt_text_pos = 'inline',
+ ephemeral = false,
+ virt_text = vt,
+ })
+ end
+
bufstate.applied[lnum] = bufstate.version
end
end
diff --git a/test/functional/plugin/lsp/inlay_hint_spec.lua b/test/functional/plugin/lsp/inlay_hint_spec.lua
index d3b5ae0e4e..00f79b9963 100644
--- a/test/functional/plugin/lsp/inlay_hint_spec.lua
+++ b/test/functional/plugin/lsp/inlay_hint_spec.lua
@@ -12,7 +12,8 @@ local api = n.api
local clear_notrace = t_lsp.clear_notrace
local create_server_definition = t_lsp.create_server_definition
-local text = dedent([[
+describe('vim.lsp.inlay_hint', function()
+ local text = dedent([[
auto add(int a, int b) { return a + b; }
int main() {
@@ -22,7 +23,7 @@ int main() {
}
}]])
-local response = [==[
+ local response = [==[
[
{"kind":1,"paddingLeft":false,"label":"-> int","position":{"character":22,"line":0},"paddingRight":false},
{"kind":2,"paddingLeft":false,"label":"a:","position":{"character":15,"line":5},"paddingRight":true},
@@ -30,7 +31,7 @@ local response = [==[
]
]==]
-local grid_without_inlay_hints = [[
+ local grid_without_inlay_hints = [[
auto add(int a, int b) { return a + b; } |
|
int main() { |
@@ -42,7 +43,7 @@ local grid_without_inlay_hints = [[
|
]]
-local grid_with_inlay_hints = [[
+ local grid_with_inlay_hints = [[
auto add(int a, int b){1:-> int} { return a + b; } |
|
int main() { |
@@ -54,16 +55,16 @@ local grid_with_inlay_hints = [[
|
]]
---- @type test.functional.ui.screen
-local screen
-before_each(function()
- clear_notrace()
- screen = Screen.new(50, 9)
- screen:attach()
+ --- @type test.functional.ui.screen
+ local screen
+ before_each(function()
+ clear_notrace()
+ screen = Screen.new(50, 9)
+ screen:attach()
- exec_lua(create_server_definition)
- exec_lua(
- [[
+ exec_lua(create_server_definition)
+ exec_lua(
+ [[
local response = ...
server = _create_server({
capabilities = {
@@ -81,19 +82,18 @@ before_each(function()
client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
]],
- response
- )
+ response
+ )
- insert(text)
- exec_lua([[vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })]])
- screen:expect({ grid = grid_with_inlay_hints })
-end)
+ insert(text)
+ exec_lua([[vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })]])
+ screen:expect({ grid = grid_with_inlay_hints })
+ end)
-after_each(function()
- api.nvim_exec_autocmds('VimLeavePre', { modeline = false })
-end)
+ after_each(function()
+ api.nvim_exec_autocmds('VimLeavePre', { modeline = false })
+ end)
-describe('vim.lsp.inlay_hint', function()
it('clears inlay hints when sole client detaches', function()
exec_lua([[vim.lsp.stop_client(client_id)]])
screen:expect({ grid = grid_without_inlay_hints, unchanged = true })
@@ -258,3 +258,74 @@ describe('vim.lsp.inlay_hint', function()
end)
end)
end)
+
+describe('Inlay hints handler', function()
+ local text = dedent([[
+test text
+ ]])
+
+ local response = [==[
+ [
+ { "position": { "line": 0, "character": 0 }, "label": "0" },
+ { "position": { "line": 0, "character": 0 }, "label": "1" },
+ { "position": { "line": 0, "character": 0 }, "label": "2" },
+ { "position": { "line": 0, "character": 0 }, "label": "3" },
+ { "position": { "line": 0, "character": 0 }, "label": "4" }
+ ]
+ ]==]
+
+ local grid_without_inlay_hints = [[
+ test text |
+ ^ |
+ |
+]]
+
+ local grid_with_inlay_hints = [[
+ {1:01234}test text |
+ ^ |
+ |
+]]
+
+ --- @type test.functional.ui.screen
+ local screen
+ before_each(function()
+ clear_notrace()
+ screen = Screen.new(50, 3)
+ screen:attach()
+
+ exec_lua(create_server_definition)
+ exec_lua(
+ [[
+ local response = ...
+ server = _create_server({
+ capabilities = {
+ inlayHintProvider = true,
+ },
+ handlers = {
+ ['textDocument/inlayHint'] = function(_, _, callback)
+ callback(nil, vim.json.decode(response))
+ end,
+ }
+ })
+
+ bufnr = vim.api.nvim_get_current_buf()
+ vim.api.nvim_win_set_buf(0, bufnr)
+
+ client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
+ ]],
+ response
+ )
+ insert(text)
+ end)
+
+ it('renders hints with same position in received order', function()
+ exec_lua([[vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })]])
+ screen:expect({ grid = grid_with_inlay_hints })
+ exec_lua([[vim.lsp.stop_client(client_id)]])
+ screen:expect({ grid = grid_without_inlay_hints, unchanged = true })
+ end)
+
+ after_each(function()
+ api.nvim_exec_autocmds('VimLeavePre', { modeline = false })
+ end)
+end)