inlay_hint_spec.lua (13134B)
1 local t = require('test.testutil') 2 local n = require('test.functional.testnvim')() 3 local Screen = require('test.functional.ui.screen') 4 local t_lsp = require('test.functional.plugin.lsp.testutil') 5 6 local eq = t.eq 7 local dedent = t.dedent 8 local exec_lua = n.exec_lua 9 local insert = n.insert 10 local feed = n.feed 11 local api = n.api 12 13 local clear_notrace = t_lsp.clear_notrace 14 local create_server_definition = t_lsp.create_server_definition 15 16 describe('vim.lsp.inlay_hint', function() 17 local text = dedent([[ 18 auto add(int a, int b) { return a + b; } 19 20 int main() { 21 int x = 1; 22 int y = 2; 23 return add(x,y); 24 } 25 }]]) 26 27 ---@type lsp.InlayHint[] 28 local response = { 29 { 30 kind = 1, 31 paddingLeft = false, 32 paddingRight = false, 33 label = '-> int', 34 position = { character = 22, line = 0 }, 35 }, 36 { 37 kind = 2, 38 paddingLeft = false, 39 paddingRight = true, 40 label = 'a:', 41 position = { character = 15, line = 5 }, 42 }, 43 { 44 kind = 2, 45 paddingLeft = false, 46 paddingRight = true, 47 label = 'b:', 48 position = { character = 17, line = 5 }, 49 }, 50 } 51 52 local grid_without_inlay_hints = [[ 53 auto add(int a, int b) { return a + b; } | 54 | 55 int main() { | 56 int x = 1; | 57 int y = 2; | 58 return add(x,y); | 59 } | 60 ^} | 61 | 62 ]] 63 64 local grid_with_inlay_hints = [[ 65 auto add(int a, int b){1:-> int} { return a + b; } | 66 | 67 int main() { | 68 int x = 1; | 69 int y = 2; | 70 return add({1:a:} x,{1:b:} y); | 71 } | 72 ^} | 73 | 74 ]] 75 76 --- @type test.functional.ui.screen 77 local screen 78 79 --- @type integer 80 local client_id 81 82 --- @type integer 83 local bufnr 84 85 before_each(function() 86 clear_notrace() 87 screen = Screen.new(50, 9) 88 89 bufnr = n.api.nvim_get_current_buf() 90 exec_lua(create_server_definition) 91 client_id = exec_lua(function() 92 _G.server = _G._create_server({ 93 capabilities = { 94 inlayHintProvider = true, 95 }, 96 handlers = { 97 ['textDocument/inlayHint'] = function(_, _, callback) 98 callback(nil, response) 99 end, 100 }, 101 }) 102 103 return vim.lsp.start({ name = 'dummy', cmd = _G.server.cmd }) 104 end) 105 106 insert(text) 107 exec_lua(function() 108 vim.lsp.inlay_hint.enable(true, { bufnr = bufnr }) 109 end) 110 screen:expect({ grid = grid_with_inlay_hints }) 111 end) 112 113 after_each(function() 114 api.nvim_exec_autocmds('VimLeavePre', { modeline = false }) 115 end) 116 117 it('clears inlay hints when sole client detaches', function() 118 exec_lua(function() 119 vim.lsp.get_client_by_id(client_id):stop() 120 end) 121 screen:expect({ grid = grid_without_inlay_hints, unchanged = true }) 122 end) 123 124 it('does not clear inlay hints when one of several clients detaches', function() 125 local client_id2 = exec_lua(function() 126 _G.server2 = _G._create_server({ 127 capabilities = { 128 inlayHintProvider = true, 129 }, 130 handlers = { 131 ['textDocument/inlayHint'] = function(_, _, callback) 132 callback(nil, {}) 133 end, 134 }, 135 }) 136 local client_id2 = vim.lsp.start({ name = 'dummy2', cmd = _G.server2.cmd }) 137 vim.lsp.inlay_hint.enable(true, { bufnr = bufnr }) 138 return client_id2 139 end) 140 141 exec_lua(function() 142 vim.lsp.get_client_by_id(client_id2):stop() 143 end) 144 screen:expect({ grid = grid_with_inlay_hints, unchanged = true }) 145 end) 146 147 describe('enable()', function() 148 it('validation', function() 149 t.matches( 150 'enable: expected boolean, got table', 151 t.pcall_err(exec_lua, function() 152 --- @diagnostic disable-next-line:param-type-mismatch 153 vim.lsp.inlay_hint.enable({}, { bufnr = bufnr }) 154 end) 155 ) 156 t.matches( 157 'enable: expected boolean, got number', 158 t.pcall_err(exec_lua, function() 159 --- @diagnostic disable-next-line:param-type-mismatch 160 vim.lsp.inlay_hint.enable(42) 161 end) 162 ) 163 t.matches( 164 'filter: expected table, got number', 165 t.pcall_err(exec_lua, function() 166 --- @diagnostic disable-next-line:param-type-mismatch 167 vim.lsp.inlay_hint.enable(true, 42) 168 end) 169 ) 170 end) 171 172 describe('clears/applies inlay hints when passed false/true/nil', function() 173 local bufnr2 --- @type integer 174 before_each(function() 175 bufnr2 = exec_lua(function() 176 local bufnr2_0 = vim.api.nvim_create_buf(true, false) 177 vim.lsp.buf_attach_client(bufnr2_0, client_id) 178 vim.api.nvim_win_set_buf(0, bufnr2_0) 179 return bufnr2_0 180 end) 181 insert(text) 182 exec_lua(function() 183 vim.lsp.inlay_hint.enable(true, { bufnr = bufnr2 }) 184 end) 185 n.api.nvim_win_set_buf(0, bufnr) 186 screen:expect({ grid = grid_with_inlay_hints }) 187 end) 188 189 it('for one single buffer', function() 190 exec_lua(function() 191 vim.lsp.inlay_hint.enable(false, { bufnr = bufnr }) 192 vim.api.nvim_win_set_buf(0, bufnr2) 193 end) 194 screen:expect({ grid = grid_with_inlay_hints, unchanged = true }) 195 n.api.nvim_win_set_buf(0, bufnr) 196 screen:expect({ grid = grid_without_inlay_hints, unchanged = true }) 197 198 exec_lua(function() 199 vim.lsp.inlay_hint.enable(true, { bufnr = bufnr }) 200 end) 201 screen:expect({ grid = grid_with_inlay_hints, unchanged = true }) 202 203 exec_lua(function() 204 vim.lsp.inlay_hint.enable( 205 not vim.lsp.inlay_hint.is_enabled({ bufnr = bufnr }), 206 { bufnr = bufnr } 207 ) 208 end) 209 screen:expect({ grid = grid_without_inlay_hints, unchanged = true }) 210 211 exec_lua(function() 212 vim.lsp.inlay_hint.enable(true, { bufnr = bufnr }) 213 end) 214 screen:expect({ grid = grid_with_inlay_hints, unchanged = true }) 215 end) 216 217 it('for all buffers', function() 218 exec_lua(function() 219 vim.lsp.inlay_hint.enable(false) 220 end) 221 screen:expect({ grid = grid_without_inlay_hints, unchanged = true }) 222 n.api.nvim_win_set_buf(0, bufnr2) 223 screen:expect({ grid = grid_without_inlay_hints, unchanged = true }) 224 225 exec_lua(function() 226 vim.lsp.inlay_hint.enable(true) 227 end) 228 screen:expect({ grid = grid_with_inlay_hints, unchanged = true }) 229 n.api.nvim_win_set_buf(0, bufnr) 230 screen:expect({ grid = grid_with_inlay_hints, unchanged = true }) 231 end) 232 end) 233 end) 234 235 describe('get()', function() 236 it('returns filtered inlay hints', function() 237 local expected2 = { 238 kind = 1, 239 paddingLeft = false, 240 label = ': int', 241 position = { 242 character = 10, 243 line = 2, 244 }, 245 paddingRight = false, 246 } 247 248 exec_lua(function() 249 _G.server2 = _G._create_server({ 250 capabilities = { 251 inlayHintProvider = true, 252 }, 253 handlers = { 254 ['textDocument/inlayHint'] = function(_, _, callback) 255 callback(nil, { expected2 }) 256 end, 257 }, 258 }) 259 _G.client2 = vim.lsp.start({ name = 'dummy2', cmd = _G.server2.cmd }) 260 vim.lsp.inlay_hint.enable(true, { bufnr = bufnr }) 261 end) 262 263 --- @type vim.lsp.inlay_hint.get.ret 264 eq( 265 { 266 { bufnr = 1, client_id = 1, inlay_hint = response[1] }, 267 { bufnr = 1, client_id = 1, inlay_hint = response[2] }, 268 { bufnr = 1, client_id = 1, inlay_hint = response[3] }, 269 { bufnr = 1, client_id = 2, inlay_hint = expected2 }, 270 }, 271 exec_lua(function() 272 return vim.lsp.inlay_hint.get() 273 end) 274 ) 275 276 eq( 277 { 278 { bufnr = 1, client_id = 2, inlay_hint = expected2 }, 279 }, 280 exec_lua(function() 281 return vim.lsp.inlay_hint.get({ 282 range = { 283 start = { line = 2, character = 10 }, 284 ['end'] = { line = 2, character = 10 }, 285 }, 286 }) 287 end) 288 ) 289 290 eq( 291 { 292 { bufnr = 1, client_id = 1, inlay_hint = response[2] }, 293 { bufnr = 1, client_id = 1, inlay_hint = response[3] }, 294 }, 295 exec_lua(function() 296 return vim.lsp.inlay_hint.get({ 297 bufnr = vim.api.nvim_get_current_buf(), 298 range = { 299 start = { line = 4, character = 18 }, 300 ['end'] = { line = 5, character = 17 }, 301 }, 302 }) 303 end) 304 ) 305 306 eq( 307 {}, 308 exec_lua(function() 309 return vim.lsp.inlay_hint.get({ 310 bufnr = vim.api.nvim_get_current_buf() + 1, 311 }) 312 end) 313 ) 314 end) 315 316 it('does not request hints from lsp when disabled', function() 317 exec_lua(function() 318 _G.server2 = _G._create_server({ 319 capabilities = { 320 inlayHintProvider = true, 321 }, 322 handlers = { 323 ['textDocument/inlayHint'] = function(_, _, callback) 324 _G.got_inlay_hint_request = true 325 callback(nil, {}) 326 end, 327 }, 328 }) 329 _G.client2 = vim.lsp.start({ name = 'dummy2', cmd = _G.server2.cmd }) 330 end) 331 332 local function was_request_sent() 333 return exec_lua(function() 334 return _G.got_inlay_hint_request == true 335 end) 336 end 337 338 eq(false, was_request_sent()) 339 340 exec_lua(function() 341 vim.lsp.inlay_hint.get() 342 end) 343 344 eq(false, was_request_sent()) 345 346 exec_lua(function() 347 vim.lsp.inlay_hint.enable(false, { bufnr = bufnr }) 348 vim.lsp.inlay_hint.get() 349 end) 350 351 eq(false, was_request_sent()) 352 353 exec_lua(function() 354 vim.lsp.inlay_hint.enable(true, { bufnr = bufnr }) 355 end) 356 357 eq(true, was_request_sent()) 358 end) 359 end) 360 end) 361 362 describe('Inlay hints handler', function() 363 local text = dedent([[ 364 test text 365 ]]) 366 367 local response = { 368 { position = { line = 0, character = 0 }, label = '0' }, 369 { position = { line = 0, character = 0 }, label = '1' }, 370 { position = { line = 0, character = 0 }, label = '2' }, 371 { position = { line = 0, character = 0 }, label = '3' }, 372 { position = { line = 0, character = 0 }, label = '4' }, 373 } 374 375 local grid_without_inlay_hints = [[ 376 test text | 377 ^ | 378 | 379 ]] 380 381 local grid_with_inlay_hints = [[ 382 {1:01234}test text | 383 ^ | 384 | 385 ]] 386 387 --- @type test.functional.ui.screen 388 local screen 389 390 --- @type integer 391 local client_id 392 393 --- @type integer 394 local bufnr 395 396 before_each(function() 397 clear_notrace() 398 screen = Screen.new(50, 3) 399 400 exec_lua(create_server_definition) 401 bufnr = n.api.nvim_get_current_buf() 402 client_id = exec_lua(function() 403 _G.server = _G._create_server({ 404 capabilities = { 405 inlayHintProvider = true, 406 }, 407 handlers = { 408 ['textDocument/inlayHint'] = function(_, _, callback) 409 callback(nil, response) 410 end, 411 }, 412 }) 413 414 vim.api.nvim_win_set_buf(0, bufnr) 415 416 return vim.lsp.start({ name = 'dummy', cmd = _G.server.cmd }) 417 end) 418 insert(text) 419 end) 420 421 it('renders hints with same position in received order', function() 422 exec_lua([[vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })]]) 423 screen:expect({ grid = grid_with_inlay_hints }) 424 exec_lua(function() 425 vim.lsp.get_client_by_id(client_id):stop() 426 end) 427 screen:expect({ grid = grid_without_inlay_hints, unchanged = true }) 428 end) 429 430 it('refreshes hints on request', function() 431 exec_lua([[vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })]]) 432 screen:expect({ grid = grid_with_inlay_hints }) 433 feed('kibefore <Esc>') 434 screen:expect([[ 435 before^ {1:01234}test text | 436 |*2 437 ]]) 438 exec_lua(function() 439 vim.lsp.inlay_hint.on_refresh( 440 nil, 441 nil, 442 { method = 'workspace/inlayHint/refresh', client_id = client_id } 443 ) 444 end) 445 screen:expect([[ 446 {1:01234}before^ test text | 447 |*2 448 ]]) 449 end) 450 451 after_each(function() 452 api.nvim_exec_autocmds('VimLeavePre', { modeline = false }) 453 end) 454 end)