neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

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)