neovim

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

inline_completion_spec.lua (11705B)


      1 local t = require('test.testutil')
      2 local n = require('test.functional.testnvim')()
      3 local t_lsp = require('test.functional.plugin.lsp.testutil')
      4 local Screen = require('test.functional.ui.screen')
      5 
      6 local dedent = t.dedent
      7 local eq = t.eq
      8 
      9 local api = n.api
     10 local exec_lua = n.exec_lua
     11 local insert = n.insert
     12 local feed = n.feed
     13 
     14 local clear_notrace = t_lsp.clear_notrace
     15 local create_server_definition = t_lsp.create_server_definition
     16 
     17 describe('vim.lsp.inline_completion', function()
     18  local text = dedent([[
     19    function fibonacci()
     20  ]])
     21 
     22  local grid_without_candidates = dedent([[
     23    function fibonacci()                                 |
     24    ^                                                     |
     25    {1:~                                                    }|*11
     26                                                         |
     27  ]])
     28 
     29  local grid_with_candidates = dedent([[
     30    function fibonacci({1:n) {}                              |
     31    {1:  if (n <= 0) return 0;}                              |
     32    {1:  if (n === 1) return 1;}                             |
     33                                                         |
     34    {1:  let a = 0, b = 1, c;}                               |
     35    {1:  for (let i = 2; i <= n; i++) {}                     |
     36    {1:    c = a + b;}                                       |
     37    {1:    a = b;}                                           |
     38    {1:    b = c;}                                           |
     39    {1:  }}                                                  |
     40    {1:  return b;}                                          |
     41    {1:}}                                                    |
     42    ^                                                     |
     43    {3:-- INSERT --}                                         |
     44  ]])
     45 
     46  local grid_applied_candidates = dedent([[
     47    function fibonacci(n) {                              |
     48      if (n <= 0) return 0;                              |
     49      if (n === 1) return 1;                             |
     50                                                         |
     51      let a = 0, b = 1, c;                               |
     52      for (let i = 2; i <= n; i++) {                     |
     53        c = a + b;                                       |
     54        a = b;                                           |
     55        b = c;                                           |
     56      }                                                  |
     57      return b;                                          |
     58    ^}                                                    |
     59                                                         |*2
     60  ]])
     61 
     62  --- @type test.functional.ui.screen
     63  local screen
     64 
     65  --- @type integer
     66  local client_id
     67 
     68  before_each(function()
     69    clear_notrace()
     70    exec_lua(create_server_definition)
     71 
     72    screen = Screen.new()
     73    screen:set_default_attr_ids({
     74      [1] = { bold = true, foreground = Screen.colors.Blue1 },
     75      [2] = { bold = true, foreground = Screen.colors.SeaGreen4 },
     76      [3] = { bold = true },
     77    })
     78 
     79    client_id = exec_lua(function()
     80      _G.server = _G._create_server({
     81        capabilities = {
     82          inlineCompletionProvider = true,
     83        },
     84        handlers = {
     85          ['textDocument/inlineCompletion'] = function(_, _, callback)
     86            if _G.empty then
     87              callback(nil, {
     88                items = {
     89                  {
     90                    insertText = 'foobar',
     91                    range = {
     92                      start = {
     93                        line = 0,
     94                        character = 19,
     95                      },
     96                      ['end'] = {
     97                        line = 0,
     98                        character = 19,
     99                      },
    100                    },
    101                  },
    102                },
    103              })
    104              return
    105            end
    106 
    107            callback(nil, {
    108              items = {
    109                {
    110                  command = {
    111                    command = 'dummy',
    112                    title = 'Completion Accepted',
    113                  },
    114                  insertText = 'function fibonacci(n) {\n  if (n <= 0) return 0;\n  if (n === 1) return 1;\n\n  let a = 0, b = 1, c;\n  for (let i = 2; i <= n; i++) {\n    c = a + b;\n    a = b;\n    b = c;\n  }\n  return b;\n}',
    115                  range = {
    116                    ['end'] = {
    117                      character = 20,
    118                      line = 0,
    119                    },
    120                    start = {
    121                      character = 0,
    122                      line = 0,
    123                    },
    124                  },
    125                },
    126                {
    127                  command = {
    128                    command = 'dummy',
    129                    title = 'Completion Accepted',
    130                  },
    131                  insertText = 'function fibonacci(n) {\n  if (n <= 0) return 0;\n  if (n === 1) return 1;\n\n  let a = 0, b = 1, c;\n  for (let i = 2; i <= n; i++) {\n    c = a + b;\n    a = b;\n    b = c;\n  }\n  return c;\n}',
    132                  range = {
    133                    ['end'] = {
    134                      character = 20,
    135                      line = 0,
    136                    },
    137                    start = {
    138                      character = 0,
    139                      line = 0,
    140                    },
    141                  },
    142                },
    143                {
    144                  command = {
    145                    command = 'dummy',
    146                    title = 'Completion Accepted',
    147                  },
    148                  insertText = 'function fibonacci(n) {\n  if (n < 0) {\n    throw new Error("Input must be a non-negative integer.");\n  }\n  if (n === 0) return 0;\n  if (n === 1) return 1;\n\n  let a = 0, b = 1, c;\n  for (let i = 2; i <= n; i++) {\n    c = a + b;\n    a = b;\n    b = c;\n  }\n  return b;\n}',
    149                  range = {
    150                    ['end'] = {
    151                      character = 20,
    152                      line = 0,
    153                    },
    154                    start = {
    155                      character = 0,
    156                      line = 0,
    157                    },
    158                  },
    159                },
    160              },
    161            })
    162          end,
    163        },
    164      })
    165 
    166      return vim.lsp.start({ name = 'dummy', cmd = _G.server.cmd })
    167    end)
    168 
    169    exec_lua(function()
    170      local client = assert(vim.lsp.get_client_by_id(client_id))
    171      _G.called = false
    172      client.commands.dummy = function()
    173        _G.called = true
    174      end
    175    end)
    176 
    177    insert(text)
    178    feed('$')
    179    exec_lua(function()
    180      vim.lsp.inline_completion.enable()
    181    end)
    182  end)
    183 
    184  after_each(function()
    185    api.nvim_exec_autocmds('VimLeavePre', { modeline = false })
    186  end)
    187 
    188  describe('enable()', function()
    189    it('requests or abort when entered/left insert mode', function()
    190      screen:expect({ grid = grid_without_candidates })
    191      feed('i')
    192      screen:expect({ grid = grid_with_candidates })
    193      feed('<Esc>')
    194      screen:expect({ grid = grid_without_candidates })
    195    end)
    196 
    197    it('no request when leaving insert mode immediately after typing', function()
    198      screen:expect({ grid = grid_without_candidates })
    199      feed('ifoobar<Esc>')
    200      screen:expect([[
    201        function fibonacci()                                 |
    202        fooba^r                                               |
    203        {1:~                                                    }|*11
    204                                                             |
    205      ]])
    206      screen:expect_unchanged(false, 500)
    207    end)
    208  end)
    209 
    210  describe('get()', function()
    211    it('applies the current candidate', function()
    212      feed('i')
    213      screen:expect({ grid = grid_with_candidates })
    214      exec_lua(function()
    215        vim.lsp.inline_completion.get()
    216      end)
    217      n.poke_eventloop()
    218      feed('<Esc>')
    219      screen:expect({ grid = grid_applied_candidates })
    220    end)
    221 
    222    it('correctly displays with absent/empty range', function()
    223      exec_lua(function()
    224        _G.empty = true
    225      end)
    226      feed('I')
    227      screen:expect([[
    228        function fibonacci({1:foobar})                           |
    229        ^                                                     |
    230        {1:~                                                    }|*11
    231        {3:-- INSERT --}                                         |
    232      ]])
    233    end)
    234 
    235    it('accepts on_accept callback', function()
    236      feed('i')
    237      screen:expect({ grid = grid_with_candidates })
    238      local result = exec_lua(function()
    239        ---@type vim.lsp.inline_completion.Item
    240        local result
    241        vim.lsp.inline_completion.get({
    242          on_accept = function(item)
    243            result = item
    244          end,
    245        })
    246        vim.wait(1000, function()
    247          return result ~= nil
    248        end) -- Wait for async callback.
    249        return result
    250      end)
    251      feed('<Esc>')
    252      screen:expect({ grid = grid_without_candidates })
    253      eq({
    254        _index = 1,
    255        client_id = 1,
    256        command = {
    257          command = 'dummy',
    258          title = 'Completion Accepted',
    259        },
    260        insert_text = dedent([[
    261        function fibonacci(n) {
    262          if (n <= 0) return 0;
    263          if (n === 1) return 1;
    264 
    265          let a = 0, b = 1, c;
    266          for (let i = 2; i <= n; i++) {
    267            c = a + b;
    268            a = b;
    269            b = c;
    270          }
    271          return b;
    272        }]]),
    273        range = {
    274          end_ = {
    275            buf = 1,
    276            col = 20,
    277            row = 0,
    278          },
    279          start = {
    280            buf = 1,
    281            col = 0,
    282            row = 0,
    283          },
    284        },
    285      }, result)
    286    end)
    287  end)
    288 
    289  describe('select()', function()
    290    it('selects the next candidate', function()
    291      feed('i')
    292      screen:expect({ grid = grid_with_candidates })
    293 
    294      exec_lua(function()
    295        vim.lsp.inline_completion.select()
    296      end)
    297 
    298      screen:expect([[
    299        function fibonacci({1:n) {}                              |
    300        {1:  if (n <= 0) return 0;}                              |
    301        {1:  if (n === 1) return 1;}                             |
    302                                                             |
    303        {1:  let a = 0, b = 1, c;}                               |
    304        {1:  for (let i = 2; i <= n; i++) {}                     |
    305        {1:    c = a + b;}                                       |
    306        {1:    a = b;}                                           |
    307        {1:    b = c;}                                           |
    308        {1:  }}                                                  |
    309        {1:  return c;}                                          |
    310        {1:}}{2: (2/3)}                                              |
    311        ^                                                     |
    312        {3:-- INSERT --}                                         |
    313      ]])
    314      exec_lua(function()
    315        vim.lsp.inline_completion.get()
    316      end)
    317      n.poke_eventloop()
    318      feed('<Esc>')
    319      screen:expect([[
    320        function fibonacci(n) {                              |
    321          if (n <= 0) return 0;                              |
    322          if (n === 1) return 1;                             |
    323                                                             |
    324          let a = 0, b = 1, c;                               |
    325          for (let i = 2; i <= n; i++) {                     |
    326            c = a + b;                                       |
    327            a = b;                                           |
    328            b = c;                                           |
    329          }                                                  |
    330          return c;                                          |
    331        ^}                                                    |
    332                                                             |*2
    333      ]])
    334    end)
    335  end)
    336 end)