neovim/test/functional/plugin/lsp/inlay_hint_spec.lua
bfredl e61228a214 fix(tests): needing two calls to setup a screen is cringe
Before calling "attach" a screen object is just a dummy container for
(row, col) values whose purpose is to be sent as part of the "attach"
function call anyway.

Just create the screen in an attached state directly. Keep the complete
(row, col, options) config together. It is still completely valid to
later detach and re-attach as needed, including to another session.
2024-11-14 12:40:57 +01:00

376 lines
11 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
--- @type integer
local client_id
--- @type integer
local bufnr
before_each(function()
clear_notrace()
screen = Screen.new(50, 9)
bufnr = n.api.nvim_get_current_buf()
exec_lua(create_server_definition)
client_id = exec_lua(function()
_G.server = _G._create_server({
capabilities = {
inlayHintProvider = true,
},
handlers = {
['textDocument/inlayHint'] = function(_, _, callback)
callback(nil, vim.json.decode(response))
end,
},
})
return vim.lsp.start({ name = 'dummy', cmd = _G.server.cmd })
end)
insert(text)
exec_lua(function()
vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })
end)
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(function()
vim.lsp.stop_client(client_id)
end)
screen:expect({ grid = grid_without_inlay_hints, unchanged = true })
end)
it('does not clear inlay hints when one of several clients detaches', function()
local client_id2 = exec_lua(function()
_G.server2 = _G._create_server({
capabilities = {
inlayHintProvider = true,
},
handlers = {
['textDocument/inlayHint'] = function(_, _, callback)
callback(nil, {})
end,
},
})
local client_id2 = vim.lsp.start({ name = 'dummy2', cmd = _G.server2.cmd })
vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })
return client_id2
end)
exec_lua(function()
vim.lsp.stop_client(client_id2)
end)
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, function()
--- @diagnostic disable-next-line:param-type-mismatch
vim.lsp.inlay_hint.enable({}, { bufnr = bufnr })
end)
)
t.matches(
'enable: expected boolean, got number',
t.pcall_err(exec_lua, function()
--- @diagnostic disable-next-line:param-type-mismatch
vim.lsp.inlay_hint.enable(42)
end)
)
t.matches(
'filter: expected table, got number',
t.pcall_err(exec_lua, function()
--- @diagnostic disable-next-line:param-type-mismatch
vim.lsp.inlay_hint.enable(true, 42)
end)
)
end)
describe('clears/applies inlay hints when passed false/true/nil', function()
local bufnr2 --- @type integer
before_each(function()
bufnr2 = exec_lua(function()
local bufnr2_0 = vim.api.nvim_create_buf(true, false)
vim.lsp.buf_attach_client(bufnr2_0, client_id)
vim.api.nvim_win_set_buf(0, bufnr2_0)
return bufnr2_0
end)
insert(text)
exec_lua(function()
vim.lsp.inlay_hint.enable(true, { bufnr = bufnr2 })
end)
n.api.nvim_win_set_buf(0, bufnr)
screen:expect({ grid = grid_with_inlay_hints })
end)
it('for one single buffer', function()
exec_lua(function()
vim.lsp.inlay_hint.enable(false, { bufnr = bufnr })
vim.api.nvim_win_set_buf(0, bufnr2)
end)
screen:expect({ grid = grid_with_inlay_hints, unchanged = true })
n.api.nvim_win_set_buf(0, bufnr)
screen:expect({ grid = grid_without_inlay_hints, unchanged = true })
exec_lua(function()
vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })
end)
screen:expect({ grid = grid_with_inlay_hints, unchanged = true })
exec_lua(function()
vim.lsp.inlay_hint.enable(
not vim.lsp.inlay_hint.is_enabled({ bufnr = bufnr }),
{ bufnr = bufnr }
)
end)
screen:expect({ grid = grid_without_inlay_hints, unchanged = true })
exec_lua(function()
vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })
end)
screen:expect({ grid = grid_with_inlay_hints, unchanged = true })
end)
it('for all buffers', function()
exec_lua(function()
vim.lsp.inlay_hint.enable(false)
end)
screen:expect({ grid = grid_without_inlay_hints, unchanged = true })
n.api.nvim_win_set_buf(0, bufnr2)
screen:expect({ grid = grid_without_inlay_hints, unchanged = true })
exec_lua(function()
vim.lsp.inlay_hint.enable(true)
end)
screen:expect({ grid = grid_with_inlay_hints, unchanged = true })
n.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(function()
_G.server2 = _G._create_server({
capabilities = {
inlayHintProvider = true,
},
handlers = {
['textDocument/inlayHint'] = function(_, _, callback)
callback(nil, { expected2 })
end,
},
})
_G.client2 = vim.lsp.start({ name = 'dummy2', cmd = _G.server2.cmd })
vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })
end)
--- @type vim.lsp.inlay_hint.get.ret
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 },
},
exec_lua(function()
return vim.lsp.inlay_hint.get()
end)
)
eq(
{
{ bufnr = 1, client_id = 2, inlay_hint = expected2 },
},
exec_lua(function()
return vim.lsp.inlay_hint.get({
range = {
start = { line = 2, character = 10 },
['end'] = { line = 2, character = 10 },
},
})
end)
)
eq(
{
{ bufnr = 1, client_id = 1, inlay_hint = expected[2] },
{ bufnr = 1, client_id = 1, inlay_hint = expected[3] },
},
exec_lua(function()
return vim.lsp.inlay_hint.get({
bufnr = vim.api.nvim_get_current_buf(),
range = {
start = { line = 4, character = 18 },
['end'] = { line = 5, character = 17 },
},
})
end)
)
eq(
{},
exec_lua(function()
return vim.lsp.inlay_hint.get({
bufnr = vim.api.nvim_get_current_buf() + 1,
})
end)
)
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
--- @type integer
local client_id
--- @type integer
local bufnr
before_each(function()
clear_notrace()
screen = Screen.new(50, 3)
exec_lua(create_server_definition)
bufnr = n.api.nvim_get_current_buf()
client_id = exec_lua(function()
_G.server = _G._create_server({
capabilities = {
inlayHintProvider = true,
},
handlers = {
['textDocument/inlayHint'] = function(_, _, callback)
callback(nil, vim.json.decode(response))
end,
},
})
vim.api.nvim_win_set_buf(0, bufnr)
return vim.lsp.start({ name = 'dummy', cmd = _G.server.cmd })
end)
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(function()
vim.lsp.stop_client(client_id)
end)
screen:expect({ grid = grid_without_inlay_hints, unchanged = true })
end)
after_each(function()
api.nvim_exec_autocmds('VimLeavePre', { modeline = false })
end)
end)