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)