neovim

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

utils_spec.lua (15042B)


      1 local t = require('test.testutil')
      2 local n = require('test.functional.testnvim')()
      3 local Screen = require('test.functional.ui.screen')
      4 
      5 local feed = n.feed
      6 local eq = t.eq
      7 local exec_lua = n.exec_lua
      8 local command, api = n.command, n.api
      9 local pcall_err = t.pcall_err
     10 
     11 describe('vim.lsp.util', function()
     12  before_each(n.clear)
     13 
     14  describe('stylize_markdown', function()
     15    local stylize_markdown = function(content, opts)
     16      return exec_lua(function()
     17        local bufnr = vim.uri_to_bufnr('file:///fake/uri')
     18        vim.fn.bufload(bufnr)
     19        return vim.lsp.util.stylize_markdown(bufnr, content, opts)
     20      end)
     21    end
     22 
     23    it('code fences', function()
     24      local lines = {
     25        '```lua',
     26        "local hello = 'world'",
     27        '```',
     28      }
     29      local expected = {
     30        "local hello = 'world'",
     31      }
     32      local opts = {}
     33      eq(expected, stylize_markdown(lines, opts))
     34    end)
     35 
     36    it('code fences with whitespace surrounded info string', function()
     37      local lines = {
     38        '```   lua   ',
     39        "local hello = 'world'",
     40        '```',
     41      }
     42      local expected = {
     43        "local hello = 'world'",
     44      }
     45      local opts = {}
     46      eq(expected, stylize_markdown(lines, opts))
     47    end)
     48 
     49    it('adds separator after code block', function()
     50      local lines = {
     51        '```lua',
     52        "local hello = 'world'",
     53        '```',
     54        '',
     55        'something',
     56      }
     57      local expected = {
     58        "local hello = 'world'",
     59        '─────────────────────',
     60        'something',
     61      }
     62      local opts = { separator = true }
     63      eq(expected, stylize_markdown(lines, opts))
     64    end)
     65 
     66    it('replaces supported HTML entities', function()
     67      local lines = {
     68        '1 < 2',
     69        '3 > 2',
     70        '"quoted"',
     71        ''apos'',
     72        '   ',
     73        '&',
     74      }
     75      local expected = {
     76        '1 < 2',
     77        '3 > 2',
     78        '"quoted"',
     79        "'apos'",
     80        '   ',
     81        '&',
     82      }
     83      local opts = {}
     84      eq(expected, stylize_markdown(lines, opts))
     85    end)
     86  end)
     87 
     88  it('convert_input_to_markdown_lines', function()
     89    local r = exec_lua(function()
     90      local hover_data = {
     91        kind = 'markdown',
     92        value = '```lua\nfunction vim.api.nvim_buf_attach(buffer: integer, send_buffer: boolean, opts: vim.api.keyset.buf_attach)\n  -> boolean\n```\n\n---\n\n Activates buffer-update events. Example:\n\n\n\n ```lua\n events = {}\n vim.api.nvim_buf_attach(0, false, {\n   on_lines = function(...)\n     table.insert(events, {...})\n   end,\n })\n ```\n\n\n @see `nvim_buf_detach()`\n @see `api-buffer-updates-lua`\n@*param* `buffer` — Buffer handle, or 0 for current buffer\n\n\n\n@*param* `send_buffer` — True if whole buffer.\n Else the first notification will be `nvim_buf_changedtick_event`.\n\n\n@*param* `opts` — Optional parameters.\n\n - on_lines: Lua callback. Args:\n   - the string "lines"\n   - buffer handle\n   - b:changedtick\n@*return* — False if foo;\n\n otherwise True.\n\n@see foo\n@see bar\n\n',
     93      }
     94      return vim.lsp.util.convert_input_to_markdown_lines(hover_data)
     95    end)
     96    local expected = {
     97      '```lua',
     98      'function vim.api.nvim_buf_attach(buffer: integer, send_buffer: boolean, opts: vim.api.keyset.buf_attach)',
     99      '  -> boolean',
    100      '```',
    101      '',
    102      '---',
    103      '',
    104      ' Activates buffer-update events. Example:',
    105      '',
    106      '',
    107      '',
    108      ' ```lua',
    109      ' events = {}',
    110      ' vim.api.nvim_buf_attach(0, false, {',
    111      '   on_lines = function(...)',
    112      '     table.insert(events, {...})',
    113      '   end,',
    114      ' })',
    115      ' ```',
    116      '',
    117      '',
    118      ' @see `nvim_buf_detach()`',
    119      ' @see `api-buffer-updates-lua`',
    120      '',
    121      -- For each @param/@return: #30695
    122      --  - Separate each by one empty line.
    123      --  - Remove all other blank lines.
    124      '@*param* `buffer` — Buffer handle, or 0 for current buffer',
    125      '',
    126      '@*param* `send_buffer` — True if whole buffer.',
    127      ' Else the first notification will be `nvim_buf_changedtick_event`.',
    128      '',
    129      '@*param* `opts` — Optional parameters.',
    130      ' - on_lines: Lua callback. Args:',
    131      '   - the string "lines"',
    132      '   - buffer handle',
    133      '   - b:changedtick',
    134      '',
    135      '@*return* — False if foo;',
    136      ' otherwise True.',
    137      '@see foo',
    138      '@see bar',
    139    }
    140    eq(expected, r)
    141  end)
    142 
    143  describe('_normalize_markdown', function()
    144    it('collapses consecutive blank lines', function()
    145      local result = exec_lua(function()
    146        local lines = {
    147          'foo',
    148          '',
    149          '',
    150          '',
    151          'bar',
    152          '',
    153          'baz',
    154        }
    155        return vim.lsp.util._normalize_markdown(lines)
    156      end)
    157      local expected = { 'foo', '', 'bar', '', 'baz' }
    158      eq(expected, result)
    159    end)
    160 
    161    it('removes preceding and trailing empty lines', function()
    162      local result = exec_lua(function()
    163        local lines = {
    164          '',
    165          'foo',
    166          'bar',
    167          '',
    168          '',
    169        }
    170        return vim.lsp.util._normalize_markdown(lines)
    171      end)
    172      local expected = { 'foo', 'bar' }
    173      eq(expected, result)
    174    end)
    175  end)
    176 
    177  describe('make_floating_popup_options', function()
    178    local function assert_anchor(anchor_bias, expected_anchor)
    179      local opts = exec_lua(function()
    180        return vim.lsp.util.make_floating_popup_options(30, 10, { anchor_bias = anchor_bias })
    181      end)
    182 
    183      eq(expected_anchor, string.sub(opts.anchor, 1, 1))
    184    end
    185 
    186    before_each(function()
    187      local _ = Screen.new(80, 80)
    188      feed('79i<CR><Esc>') -- fill screen with empty lines
    189    end)
    190 
    191    describe('when on the first line it places window below', function()
    192      before_each(function()
    193        feed('gg')
    194      end)
    195 
    196      it('for anchor_bias = "auto"', function()
    197        assert_anchor('auto', 'N')
    198      end)
    199 
    200      it('for anchor_bias = "above"', function()
    201        assert_anchor('above', 'N')
    202      end)
    203 
    204      it('for anchor_bias = "below"', function()
    205        assert_anchor('below', 'N')
    206      end)
    207    end)
    208 
    209    describe('when on the last line it places window above', function()
    210      before_each(function()
    211        feed('G')
    212      end)
    213 
    214      it('for anchor_bias = "auto"', function()
    215        assert_anchor('auto', 'S')
    216      end)
    217 
    218      it('for anchor_bias = "above"', function()
    219        assert_anchor('above', 'S')
    220      end)
    221 
    222      it('for anchor_bias = "below"', function()
    223        assert_anchor('below', 'S')
    224      end)
    225    end)
    226 
    227    describe('with 20 lines above, 59 lines below', function()
    228      before_each(function()
    229        feed('gg20j')
    230      end)
    231 
    232      it('places window below for anchor_bias = "auto"', function()
    233        assert_anchor('auto', 'N')
    234      end)
    235 
    236      it('places window above for anchor_bias = "above"', function()
    237        assert_anchor('above', 'S')
    238      end)
    239 
    240      it('places window below for anchor_bias = "below"', function()
    241        assert_anchor('below', 'N')
    242      end)
    243    end)
    244 
    245    describe('with 59 lines above, 20 lines below', function()
    246      before_each(function()
    247        feed('G20k')
    248      end)
    249 
    250      it('places window above for anchor_bias = "auto"', function()
    251        assert_anchor('auto', 'S')
    252      end)
    253 
    254      it('places window above for anchor_bias = "above"', function()
    255        assert_anchor('above', 'S')
    256      end)
    257 
    258      it('places window below for anchor_bias = "below"', function()
    259        assert_anchor('below', 'N')
    260      end)
    261 
    262      it('bordered window truncates dimensions correctly', function()
    263        local opts = exec_lua(function()
    264          return vim.lsp.util.make_floating_popup_options(100, 100, { border = 'single' })
    265        end)
    266 
    267        eq(56, opts.height)
    268      end)
    269 
    270      it('title with winborder option #35179', function()
    271        local opts = exec_lua(function()
    272          vim.o.winborder = 'single'
    273          return vim.lsp.util.make_floating_popup_options(100, 100, { title = 'Title' })
    274        end)
    275        eq('Title', opts.title)
    276      end)
    277    end)
    278  end)
    279 
    280  describe('open_floating_preview', function()
    281    before_each(function()
    282      Screen.new(10, 10)
    283      feed('9i<CR><Esc>G4k')
    284    end)
    285 
    286    local var_name = 'lsp_floating_preview'
    287    local curbuf = api.nvim_get_current_buf()
    288 
    289    it('clean bufvar after fclose', function()
    290      exec_lua(function()
    291        vim.lsp.util.open_floating_preview({ 'test' }, '', { height = 5, width = 2 })
    292      end)
    293      eq(true, api.nvim_win_is_valid(api.nvim_buf_get_var(curbuf, var_name)))
    294      command('fclose')
    295      eq('Key not found: lsp_floating_preview', pcall_err(api.nvim_buf_get_var, curbuf, var_name))
    296    end)
    297 
    298    it('clean bufvar after CursorMoved', function()
    299      local result = exec_lua(function()
    300        vim.lsp.util.open_floating_preview({ 'test' }, '', { height = 5, width = 2 })
    301        local winnr = vim.b[vim.api.nvim_get_current_buf()].lsp_floating_preview
    302        local result = vim.api.nvim_win_is_valid(winnr)
    303        vim.api.nvim_feedkeys(vim.keycode('G'), 'txn', false)
    304        return result
    305      end)
    306      eq(true, result)
    307      eq('Key not found: lsp_floating_preview', pcall_err(api.nvim_buf_get_var, curbuf, var_name))
    308    end)
    309  end)
    310 
    311  it('open_floating_preview zindex greater than current window', function()
    312    local screen = Screen.new()
    313    exec_lua(function()
    314      vim.api.nvim_open_win(0, true, {
    315        relative = 'editor',
    316        border = 'single',
    317        height = 11,
    318        width = 51,
    319        row = 2,
    320        col = 2,
    321      })
    322      vim.keymap.set('n', 'K', function()
    323        vim.lsp.util.open_floating_preview({ 'foo' }, '', { border = 'single' })
    324      end, {})
    325    end)
    326    feed('K')
    327    screen:expect([[
    328      ┌───────────────────────────────────────────────────┐|
    329      {4:^                                                   }|
    330      │┌───┐{11:                                              }|
    331      ││{4:foo}{11:                                              }|
    332      │└───┘{11:                                              }|
    333      {11:~                                                  }|*7
    334      └───────────────────────────────────────────────────┘|
    335                                                           |
    336    ]])
    337  end)
    338 
    339  it('open_floating_preview height reduced for concealed lines', function()
    340    local screen = Screen.new()
    341    screen:add_extra_attr_ids({
    342      [100] = {
    343        background = Screen.colors.LightMagenta,
    344        foreground = Screen.colors.Brown,
    345        bold = true,
    346      },
    347      [101] = { background = Screen.colors.LightMagenta, foreground = Screen.colors.Blue },
    348      [102] = { background = Screen.colors.LightMagenta, foreground = Screen.colors.DarkCyan },
    349    })
    350    exec_lua([[
    351      vim.g.syntax_on = false
    352      vim.lsp.util.open_floating_preview({ '```lua', 'local foo', '```' }, 'markdown', {
    353        border = 'single',
    354        focus = false,
    355      })
    356    ]])
    357    screen:expect([[
    358      ^                                                     |
    359      ┌─────────┐{1:                                          }|
    360      {100:local}{101: }{102:foo}{1:                                          }|
    361      └─────────┘{1:                                          }|
    362      {1:~                                                    }|*9
    363                                                           |
    364    ]])
    365    -- Entering window keeps lines concealed and doesn't end up below inner window size.
    366    feed('<C-w>wG')
    367    screen:expect([[
    368                                                           |
    369      ┌─────────┐{1:                                          }|
    370      {101:^```}{4:      }{1:                                          }|
    371      └─────────┘{1:                                          }|
    372      {1:~                                                    }|*9
    373                                                           |
    374    ]])
    375    -- Correct height when float inherits 'conceallevel' >= 2 #32639
    376    command('close | set conceallevel=2')
    377    feed('<Ignore>') -- Prevent CursorMoved closing the next float immediately
    378    exec_lua([[
    379      vim.lsp.util.open_floating_preview({ '```lua', 'local foo', '```' }, 'markdown', {
    380        border = 'single',
    381        focus = false,
    382      })
    383    ]])
    384    screen:expect([[
    385      ^                                                     |
    386      ┌─────────┐{1:                                          }|
    387      {100:local}{101: }{102:foo}{1:                                          }|
    388      └─────────┘{1:                                          }|
    389      {1:~                                                    }|*9
    390                                                           |
    391    ]])
    392    -- This tests the valid winline code path (why doesn't the above?).
    393    exec_lua([[
    394      vim.cmd.only()
    395      vim.lsp.util.open_floating_preview({ 'foo', '```lua', 'local bar', '```' }, 'markdown', {
    396        border = 'single',
    397        focus = false,
    398      })
    399    ]])
    400    feed('<C-W>wG')
    401    screen:expect([[
    402                                                           |
    403      ┌─────────┐{1:                                          }|
    404      {100:local}{101: }{102:bar}{1:                                          }|
    405      {101:^```}{4:      }{1:                                          }|
    406      └─────────┘{1:                                          }|
    407      {1:~                                                    }|*8
    408                                                           |
    409    ]])
    410  end)
    411 
    412  it('open_floating_preview height does not exceed max_height', function()
    413    local screen = Screen.new()
    414    exec_lua([[
    415      vim.lsp.util.open_floating_preview(vim.fn.range(1, 10), 'markdown', {
    416        border = 'single',
    417        width = 5,
    418        max_height = 5,
    419        focus = false,
    420      })
    421    ]])
    422    screen:expect([[
    423      ^                                                     |
    424      ┌─────┐{1:                                              }|
    425      {4:1    }{1:                                              }|
    426      {4:2    }{1:                                              }|
    427      {4:3    }{1:                                              }|
    428      {4:4    }{1:                                              }|
    429      {4:5    }{1:                                              }|
    430      └─────┘{1:                                              }|
    431      {1:~                                                    }|*5
    432                                                           |
    433    ]])
    434  end)
    435 end)