mirror of
https://github.com/neovim/neovim.git
synced 2024-12-29 14:41:06 -07:00
e29f245a10
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.
332 lines
10 KiB
Lua
332 lines
10 KiB
Lua
local t = require('test.testutil')
|
|
local n = require('test.functional.testnvim')()
|
|
local Screen = require('test.functional.ui.screen')
|
|
local t_lsp = require('test.functional.plugin.lsp.testutil')
|
|
|
|
local eq = t.eq
|
|
local dedent = t.dedent
|
|
local exec_lua = n.exec_lua
|
|
local insert = n.insert
|
|
local api = n.api
|
|
|
|
local clear_notrace = t_lsp.clear_notrace
|
|
local create_server_definition = t_lsp.create_server_definition
|
|
|
|
describe('vim.lsp.inlay_hint', function()
|
|
local text = dedent([[
|
|
auto add(int a, int b) { return a + b; }
|
|
|
|
int main() {
|
|
int x = 1;
|
|
int y = 2;
|
|
return add(x,y);
|
|
}
|
|
}]])
|
|
|
|
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},
|
|
{"kind":2,"paddingLeft":false,"label":"b:","position":{"character":17,"line":5},"paddingRight":true}
|
|
]
|
|
]==]
|
|
|
|
local grid_without_inlay_hints = [[
|
|
auto add(int a, int b) { return a + b; } |
|
|
|
|
|
int main() { |
|
|
int x = 1; |
|
|
int y = 2; |
|
|
return add(x,y); |
|
|
} |
|
|
^} |
|
|
|
|
|
]]
|
|
|
|
local grid_with_inlay_hints = [[
|
|
auto add(int a, int b){1:-> int} { return a + b; } |
|
|
|
|
|
int main() { |
|
|
int x = 1; |
|
|
int y = 2; |
|
|
return add({1:a:} x,{1:b:} y); |
|
|
} |
|
|
^} |
|
|
|
|
|
]]
|
|
|
|
--- @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(
|
|
[[
|
|
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)
|
|
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)
|
|
|
|
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 })
|
|
end)
|
|
|
|
it('does not clear inlay hints when one of several clients detaches', function()
|
|
exec_lua([[
|
|
server2 = _create_server({
|
|
capabilities = {
|
|
inlayHintProvider = true,
|
|
},
|
|
handlers = {
|
|
['textDocument/inlayHint'] = function(_, _, callback)
|
|
callback(nil, {})
|
|
end,
|
|
}
|
|
})
|
|
client2 = vim.lsp.start({ name = 'dummy2', cmd = server2.cmd })
|
|
vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })
|
|
]])
|
|
|
|
exec_lua([[ vim.lsp.stop_client(client2) ]])
|
|
screen:expect({ grid = grid_with_inlay_hints, unchanged = true })
|
|
end)
|
|
|
|
describe('enable()', function()
|
|
it('validation', function()
|
|
t.matches(
|
|
'enable: expected boolean, got table',
|
|
t.pcall_err(exec_lua, [[vim.lsp.inlay_hint.enable({}, { bufnr = bufnr })]])
|
|
)
|
|
t.matches(
|
|
'enable: expected boolean, got number',
|
|
t.pcall_err(exec_lua, [[vim.lsp.inlay_hint.enable(42)]])
|
|
)
|
|
t.matches(
|
|
'filter: expected table, got number',
|
|
t.pcall_err(exec_lua, [[vim.lsp.inlay_hint.enable(true, 42)]])
|
|
)
|
|
end)
|
|
|
|
describe('clears/applies inlay hints when passed false/true/nil', function()
|
|
before_each(function()
|
|
exec_lua([[
|
|
bufnr2 = vim.api.nvim_create_buf(true, false)
|
|
vim.lsp.buf_attach_client(bufnr2, client_id)
|
|
vim.api.nvim_win_set_buf(0, bufnr2)
|
|
]])
|
|
insert(text)
|
|
exec_lua([[vim.lsp.inlay_hint.enable(true, { bufnr = bufnr2 })]])
|
|
exec_lua([[vim.api.nvim_win_set_buf(0, bufnr)]])
|
|
screen:expect({ grid = grid_with_inlay_hints })
|
|
end)
|
|
|
|
it('for one single buffer', function()
|
|
exec_lua([[
|
|
vim.lsp.inlay_hint.enable(false, { bufnr = bufnr })
|
|
vim.api.nvim_win_set_buf(0, bufnr2)
|
|
]])
|
|
screen:expect({ grid = grid_with_inlay_hints, unchanged = true })
|
|
exec_lua([[vim.api.nvim_win_set_buf(0, bufnr)]])
|
|
screen:expect({ grid = grid_without_inlay_hints, unchanged = true })
|
|
|
|
exec_lua([[vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })]])
|
|
screen:expect({ grid = grid_with_inlay_hints, unchanged = true })
|
|
|
|
exec_lua(
|
|
[[vim.lsp.inlay_hint.enable(not vim.lsp.inlay_hint.is_enabled({ bufnr = bufnr }), { bufnr = bufnr })]]
|
|
)
|
|
screen:expect({ grid = grid_without_inlay_hints, unchanged = true })
|
|
|
|
exec_lua([[vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })]])
|
|
screen:expect({ grid = grid_with_inlay_hints, unchanged = true })
|
|
end)
|
|
|
|
it('for all buffers', function()
|
|
exec_lua([[vim.lsp.inlay_hint.enable(false)]])
|
|
screen:expect({ grid = grid_without_inlay_hints, unchanged = true })
|
|
exec_lua([[vim.api.nvim_win_set_buf(0, bufnr2)]])
|
|
screen:expect({ grid = grid_without_inlay_hints, unchanged = true })
|
|
|
|
exec_lua([[vim.lsp.inlay_hint.enable(true)]])
|
|
screen:expect({ grid = grid_with_inlay_hints, unchanged = true })
|
|
exec_lua([[vim.api.nvim_win_set_buf(0, bufnr)]])
|
|
screen:expect({ grid = grid_with_inlay_hints, unchanged = true })
|
|
end)
|
|
end)
|
|
end)
|
|
|
|
describe('get()', function()
|
|
it('returns filtered inlay hints', function()
|
|
--- @type lsp.InlayHint[]
|
|
local expected = vim.json.decode(response)
|
|
local expected2 = {
|
|
kind = 1,
|
|
paddingLeft = false,
|
|
label = ': int',
|
|
position = {
|
|
character = 10,
|
|
line = 2,
|
|
},
|
|
paddingRight = false,
|
|
}
|
|
|
|
exec_lua(
|
|
[[
|
|
local expected2 = ...
|
|
server2 = _create_server({
|
|
capabilities = {
|
|
inlayHintProvider = true,
|
|
},
|
|
handlers = {
|
|
['textDocument/inlayHint'] = function(_, _, callback)
|
|
callback(nil, { expected2 })
|
|
end,
|
|
}
|
|
})
|
|
client2 = vim.lsp.start({ name = 'dummy2', cmd = server2.cmd })
|
|
vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })
|
|
]],
|
|
expected2
|
|
)
|
|
|
|
--- @type vim.lsp.inlay_hint.get.ret
|
|
local res = exec_lua([[return vim.lsp.inlay_hint.get()]])
|
|
eq({
|
|
{ bufnr = 1, client_id = 1, inlay_hint = expected[1] },
|
|
{ bufnr = 1, client_id = 1, inlay_hint = expected[2] },
|
|
{ bufnr = 1, client_id = 1, inlay_hint = expected[3] },
|
|
{ bufnr = 1, client_id = 2, inlay_hint = expected2 },
|
|
}, res)
|
|
|
|
--- @type vim.lsp.inlay_hint.get.ret
|
|
res = exec_lua([[return vim.lsp.inlay_hint.get({
|
|
range = {
|
|
start = { line = 2, character = 10 },
|
|
["end"] = { line = 2, character = 10 },
|
|
},
|
|
})]])
|
|
eq({
|
|
{ bufnr = 1, client_id = 2, inlay_hint = expected2 },
|
|
}, res)
|
|
|
|
--- @type vim.lsp.inlay_hint.get.ret
|
|
res = exec_lua([[return vim.lsp.inlay_hint.get({
|
|
bufnr = vim.api.nvim_get_current_buf(),
|
|
range = {
|
|
start = { line = 4, character = 18 },
|
|
["end"] = { line = 5, character = 17 },
|
|
},
|
|
})]])
|
|
eq({
|
|
{ bufnr = 1, client_id = 1, inlay_hint = expected[2] },
|
|
{ bufnr = 1, client_id = 1, inlay_hint = expected[3] },
|
|
}, res)
|
|
|
|
--- @type vim.lsp.inlay_hint.get.ret
|
|
res = exec_lua([[return vim.lsp.inlay_hint.get({
|
|
bufnr = vim.api.nvim_get_current_buf() + 1,
|
|
})]])
|
|
eq({}, res)
|
|
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)
|