neovim

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

vim_spec.lua (95861B)


      1 -- Test suite for testing interactions with API bindings
      2 local t = require('test.testutil')
      3 local n = require('test.functional.testnvim')()
      4 local Screen = require('test.functional.ui.screen')
      5 
      6 local nvim_prog = n.nvim_prog
      7 local fn = n.fn
      8 local api = n.api
      9 local command = n.command
     10 local dedent = t.dedent
     11 local insert = n.insert
     12 local clear = n.clear
     13 local eq = t.eq
     14 local ok = t.ok
     15 local pesc = vim.pesc
     16 local eval = n.eval
     17 local feed = n.feed
     18 local pcall_err = t.pcall_err
     19 local exec_lua = n.exec_lua
     20 local matches = t.matches
     21 local exec = n.exec
     22 local NIL = vim.NIL
     23 local retry = t.retry
     24 local next_msg = n.next_msg
     25 local remove_trace = t.remove_trace
     26 local poke_eventloop = n.poke_eventloop
     27 local assert_alive = n.assert_alive
     28 local expect = n.expect
     29 
     30 describe('lua stdlib', function()
     31  before_each(clear)
     32  -- İ: `tolower("İ")` is `i` which has length 1 while `İ` itself has
     33  --    length 2 (in bytes).
     34  -- Ⱥ: `tolower("Ⱥ")` is `ⱥ` which has length 2 while `Ⱥ` itself has
     35  --    length 3 (in bytes).
     36  --
     37  -- Note: 'i' !=? 'İ' and 'ⱥ' !=? 'Ⱥ' on some systems.
     38  -- Note: Built-in Nvim comparison (on systems lacking `strcasecmp`) works
     39  --       only on ASCII characters.
     40  it('vim.stricmp', function()
     41    eq(0, fn.luaeval('vim.stricmp("a", "A")'))
     42    eq(0, fn.luaeval('vim.stricmp("A", "a")'))
     43    eq(0, fn.luaeval('vim.stricmp("a", "a")'))
     44    eq(0, fn.luaeval('vim.stricmp("A", "A")'))
     45 
     46    eq(0, fn.luaeval('vim.stricmp("", "")'))
     47    eq(0, fn.luaeval('vim.stricmp("\\0", "\\0")'))
     48    eq(0, fn.luaeval('vim.stricmp("\\0\\0", "\\0\\0")'))
     49    eq(0, fn.luaeval('vim.stricmp("\\0\\0\\0", "\\0\\0\\0")'))
     50    eq(0, fn.luaeval('vim.stricmp("\\0\\0\\0A", "\\0\\0\\0a")'))
     51    eq(0, fn.luaeval('vim.stricmp("\\0\\0\\0a", "\\0\\0\\0A")'))
     52    eq(0, fn.luaeval('vim.stricmp("\\0\\0\\0a", "\\0\\0\\0a")'))
     53 
     54    eq(0, fn.luaeval('vim.stricmp("a\\0", "A\\0")'))
     55    eq(0, fn.luaeval('vim.stricmp("A\\0", "a\\0")'))
     56    eq(0, fn.luaeval('vim.stricmp("a\\0", "a\\0")'))
     57    eq(0, fn.luaeval('vim.stricmp("A\\0", "A\\0")'))
     58 
     59    eq(0, fn.luaeval('vim.stricmp("\\0a", "\\0A")'))
     60    eq(0, fn.luaeval('vim.stricmp("\\0A", "\\0a")'))
     61    eq(0, fn.luaeval('vim.stricmp("\\0a", "\\0a")'))
     62    eq(0, fn.luaeval('vim.stricmp("\\0A", "\\0A")'))
     63 
     64    eq(0, fn.luaeval('vim.stricmp("\\0a\\0", "\\0A\\0")'))
     65    eq(0, fn.luaeval('vim.stricmp("\\0A\\0", "\\0a\\0")'))
     66    eq(0, fn.luaeval('vim.stricmp("\\0a\\0", "\\0a\\0")'))
     67    eq(0, fn.luaeval('vim.stricmp("\\0A\\0", "\\0A\\0")'))
     68 
     69    eq(-1, fn.luaeval('vim.stricmp("a", "B")'))
     70    eq(-1, fn.luaeval('vim.stricmp("A", "b")'))
     71    eq(-1, fn.luaeval('vim.stricmp("a", "b")'))
     72    eq(-1, fn.luaeval('vim.stricmp("A", "B")'))
     73 
     74    eq(-1, fn.luaeval('vim.stricmp("", "\\0")'))
     75    eq(-1, fn.luaeval('vim.stricmp("\\0", "\\0\\0")'))
     76    eq(-1, fn.luaeval('vim.stricmp("\\0\\0", "\\0\\0\\0")'))
     77    eq(-1, fn.luaeval('vim.stricmp("\\0\\0\\0A", "\\0\\0\\0b")'))
     78    eq(-1, fn.luaeval('vim.stricmp("\\0\\0\\0a", "\\0\\0\\0B")'))
     79    eq(-1, fn.luaeval('vim.stricmp("\\0\\0\\0a", "\\0\\0\\0b")'))
     80 
     81    eq(-1, fn.luaeval('vim.stricmp("a\\0", "B\\0")'))
     82    eq(-1, fn.luaeval('vim.stricmp("A\\0", "b\\0")'))
     83    eq(-1, fn.luaeval('vim.stricmp("a\\0", "b\\0")'))
     84    eq(-1, fn.luaeval('vim.stricmp("A\\0", "B\\0")'))
     85 
     86    eq(-1, fn.luaeval('vim.stricmp("\\0a", "\\0B")'))
     87    eq(-1, fn.luaeval('vim.stricmp("\\0A", "\\0b")'))
     88    eq(-1, fn.luaeval('vim.stricmp("\\0a", "\\0b")'))
     89    eq(-1, fn.luaeval('vim.stricmp("\\0A", "\\0B")'))
     90 
     91    eq(-1, fn.luaeval('vim.stricmp("\\0a\\0", "\\0B\\0")'))
     92    eq(-1, fn.luaeval('vim.stricmp("\\0A\\0", "\\0b\\0")'))
     93    eq(-1, fn.luaeval('vim.stricmp("\\0a\\0", "\\0b\\0")'))
     94    eq(-1, fn.luaeval('vim.stricmp("\\0A\\0", "\\0B\\0")'))
     95 
     96    eq(1, fn.luaeval('vim.stricmp("c", "B")'))
     97    eq(1, fn.luaeval('vim.stricmp("C", "b")'))
     98    eq(1, fn.luaeval('vim.stricmp("c", "b")'))
     99    eq(1, fn.luaeval('vim.stricmp("C", "B")'))
    100 
    101    eq(1, fn.luaeval('vim.stricmp("\\0", "")'))
    102    eq(1, fn.luaeval('vim.stricmp("\\0\\0", "\\0")'))
    103    eq(1, fn.luaeval('vim.stricmp("\\0\\0\\0", "\\0\\0")'))
    104    eq(1, fn.luaeval('vim.stricmp("\\0\\0\\0\\0", "\\0\\0\\0")'))
    105    eq(1, fn.luaeval('vim.stricmp("\\0\\0\\0C", "\\0\\0\\0b")'))
    106    eq(1, fn.luaeval('vim.stricmp("\\0\\0\\0c", "\\0\\0\\0B")'))
    107    eq(1, fn.luaeval('vim.stricmp("\\0\\0\\0c", "\\0\\0\\0b")'))
    108 
    109    eq(1, fn.luaeval('vim.stricmp("c\\0", "B\\0")'))
    110    eq(1, fn.luaeval('vim.stricmp("C\\0", "b\\0")'))
    111    eq(1, fn.luaeval('vim.stricmp("c\\0", "b\\0")'))
    112    eq(1, fn.luaeval('vim.stricmp("C\\0", "B\\0")'))
    113 
    114    eq(1, fn.luaeval('vim.stricmp("c\\0", "B")'))
    115    eq(1, fn.luaeval('vim.stricmp("C\\0", "b")'))
    116    eq(1, fn.luaeval('vim.stricmp("c\\0", "b")'))
    117    eq(1, fn.luaeval('vim.stricmp("C\\0", "B")'))
    118 
    119    eq(1, fn.luaeval('vim.stricmp("\\0c", "\\0B")'))
    120    eq(1, fn.luaeval('vim.stricmp("\\0C", "\\0b")'))
    121    eq(1, fn.luaeval('vim.stricmp("\\0c", "\\0b")'))
    122    eq(1, fn.luaeval('vim.stricmp("\\0C", "\\0B")'))
    123 
    124    eq(1, fn.luaeval('vim.stricmp("\\0c\\0", "\\0B\\0")'))
    125    eq(1, fn.luaeval('vim.stricmp("\\0C\\0", "\\0b\\0")'))
    126    eq(1, fn.luaeval('vim.stricmp("\\0c\\0", "\\0b\\0")'))
    127    eq(1, fn.luaeval('vim.stricmp("\\0C\\0", "\\0B\\0")'))
    128  end)
    129 
    130  --- @param prerel string | nil
    131  local function test_vim_deprecate(prerel)
    132    -- vim.deprecate(name, alternative, version, plugin, backtrace)
    133    -- See MAINTAIN.md for the soft/hard deprecation policy
    134 
    135    describe(('vim.deprecate prerel=%s,'):format(prerel or 'nil'), function()
    136      local curver --- @type {major:number, minor:number}
    137 
    138      before_each(function()
    139        curver = exec_lua('return vim.version()')
    140      end)
    141 
    142      it('plugin=nil, same message skipped', function()
    143        -- "0.10" or "0.10-dev+xxx"
    144        local curstr = ('%s.%s%s'):format(curver.major, curver.minor, prerel or '')
    145        eq(
    146          ([[foo.bar() is deprecated. Run ":checkhealth vim.deprecated" for more information]]):format(
    147            curstr
    148          ),
    149          exec_lua('return vim.deprecate(...)', 'foo.bar()', 'zub.wooo{ok=yay}', curstr)
    150        )
    151        -- Same message as above; skipped this time.
    152        eq(vim.NIL, exec_lua('return vim.deprecate(...)', 'foo.bar()', 'zub.wooo{ok=yay}', curstr))
    153      end)
    154 
    155      it('plugin=nil, no error if soft-deprecated', function()
    156        eq(vim.NIL, exec_lua [[return vim.deprecate('old1', 'new1', '0.99.0')]])
    157        -- Major version > current Nvim major is always "soft-deprecated".
    158        -- XXX: This is also a reminder to update the hardcoded `nvim_major`, when Nvim reaches 1.0.
    159        eq(vim.NIL, exec_lua [[return vim.deprecate('old2', 'new2', '1.0.0')]])
    160      end)
    161 
    162      it('plugin=nil, show error if hard-deprecated', function()
    163        -- "0.10" or "0.11"
    164        local nextver = ('%s.%s'):format(curver.major, curver.minor + (prerel and 0 or 1))
    165 
    166        local was_removed = prerel and 'was removed' or 'will be removed'
    167        eq(
    168          dedent(
    169            [[
    170            foo.hard_dep() is deprecated. Run ":checkhealth vim.deprecated" for more information]]
    171          ):format(was_removed, nextver),
    172          exec_lua('return vim.deprecate(...)', 'foo.hard_dep()', 'vim.new_api()', nextver)
    173        )
    174      end)
    175 
    176      it('plugin specified', function()
    177        -- When `plugin` is specified, don't show ":help deprecated". #22235
    178        eq(
    179          dedent [[
    180            foo.bar() is deprecated, use zub.wooo{ok=yay} instead.
    181            Feature will be removed in my-plugin.nvim 0.3.0]],
    182          exec_lua(
    183            'return vim.deprecate(...)',
    184            'foo.bar()',
    185            'zub.wooo{ok=yay}',
    186            '0.3.0',
    187            'my-plugin.nvim',
    188            false
    189          )
    190        )
    191 
    192        -- plugins: no soft deprecation period
    193        eq(
    194          dedent [[
    195            foo.bar() is deprecated, use zub.wooo{ok=yay} instead.
    196            Feature will be removed in my-plugin.nvim 0.11.0]],
    197          exec_lua(
    198            'return vim.deprecate(...)',
    199            'foo.bar()',
    200            'zub.wooo{ok=yay}',
    201            '0.11.0',
    202            'my-plugin.nvim',
    203            false
    204          )
    205        )
    206      end)
    207    end)
    208  end
    209 
    210  test_vim_deprecate()
    211  test_vim_deprecate('-dev+g0000000')
    212 
    213  it('vim.startswith', function()
    214    eq(true, fn.luaeval('vim.startswith("123", "1")'))
    215    eq(true, fn.luaeval('vim.startswith("123", "")'))
    216    eq(true, fn.luaeval('vim.startswith("123", "123")'))
    217    eq(true, fn.luaeval('vim.startswith("", "")'))
    218 
    219    eq(false, fn.luaeval('vim.startswith("123", " ")'))
    220    eq(false, fn.luaeval('vim.startswith("123", "2")'))
    221    eq(false, fn.luaeval('vim.startswith("123", "1234")'))
    222 
    223    matches(
    224      'prefix: expected string, got nil',
    225      pcall_err(exec_lua, 'return vim.startswith("123", nil)')
    226    )
    227    matches('s: expected string, got nil', pcall_err(exec_lua, 'return vim.startswith(nil, "123")'))
    228  end)
    229 
    230  it('vim.endswith', function()
    231    eq(true, fn.luaeval('vim.endswith("123", "3")'))
    232    eq(true, fn.luaeval('vim.endswith("123", "")'))
    233    eq(true, fn.luaeval('vim.endswith("123", "123")'))
    234    eq(true, fn.luaeval('vim.endswith("", "")'))
    235 
    236    eq(false, fn.luaeval('vim.endswith("123", " ")'))
    237    eq(false, fn.luaeval('vim.endswith("123", "2")'))
    238    eq(false, fn.luaeval('vim.endswith("123", "1234")'))
    239 
    240    matches(
    241      'suffix: expected string, got nil',
    242      pcall_err(exec_lua, 'return vim.endswith("123", nil)')
    243    )
    244    matches('s: expected string, got nil', pcall_err(exec_lua, 'return vim.endswith(nil, "123")'))
    245  end)
    246 
    247  it('vim.str_utfindex/str_byteindex', function()
    248    exec_lua([[_G.test_text = "xy åäö ɧ 汉语 ↥ 🤦x🦄 å بِيَّ\000ъ"]])
    249    local indices32 = {
    250      [0] = 0,
    251      1,
    252      2,
    253      3,
    254      5,
    255      7,
    256      9,
    257      10,
    258      12,
    259      13,
    260      16,
    261      19,
    262      20,
    263      23,
    264      24,
    265      28,
    266      29,
    267      33,
    268      34,
    269      35,
    270      37,
    271      38,
    272      40,
    273      42,
    274      44,
    275      46,
    276      48,
    277      49,
    278      51,
    279    }
    280    local indices16 = {
    281      [0] = 0,
    282      1,
    283      2,
    284      3,
    285      5,
    286      7,
    287      9,
    288      10,
    289      12,
    290      13,
    291      16,
    292      19,
    293      20,
    294      23,
    295      24,
    296      28,
    297      28,
    298      29,
    299      33,
    300      33,
    301      34,
    302      35,
    303      37,
    304      38,
    305      40,
    306      42,
    307      44,
    308      46,
    309      48,
    310      49,
    311      51,
    312    }
    313    local indices8 = {
    314      [0] = 0,
    315      1,
    316      2,
    317      3,
    318      4,
    319      5,
    320      6,
    321      7,
    322      8,
    323      9,
    324      10,
    325      11,
    326      12,
    327      13,
    328      14,
    329      15,
    330      16,
    331      17,
    332      18,
    333      19,
    334      20,
    335      21,
    336      22,
    337      23,
    338      24,
    339      25,
    340      26,
    341      27,
    342      28,
    343      29,
    344      30,
    345      31,
    346      32,
    347      33,
    348      34,
    349      35,
    350      36,
    351      37,
    352      38,
    353      39,
    354      40,
    355      41,
    356      42,
    357      43,
    358      44,
    359      45,
    360      46,
    361      47,
    362      48,
    363      49,
    364      50,
    365      51,
    366    }
    367    for i, k in pairs(indices32) do
    368      eq(k, exec_lua('return vim.str_byteindex(_G.test_text, ...)', i), i)
    369      eq(k, exec_lua('return vim.str_byteindex(_G.test_text, ..., false)', i), i)
    370      eq(k, exec_lua('return vim.str_byteindex(_G.test_text, "utf-32", ...)', i), i)
    371    end
    372    for i, k in pairs(indices16) do
    373      eq(k, exec_lua('return vim.str_byteindex(_G.test_text, ..., true)', i), i)
    374      eq(k, exec_lua('return vim.str_byteindex(_G.test_text, "utf-16", ...)', i), i)
    375    end
    376    for i, k in pairs(indices8) do
    377      eq(k, exec_lua('return vim.str_byteindex(_G.test_text, "utf-8", ...)', i), i)
    378    end
    379    matches(
    380      'index out of range',
    381      pcall_err(exec_lua, 'return vim.str_byteindex(_G.test_text, ...)', #indices32 + 1)
    382    )
    383    matches(
    384      'index out of range',
    385      pcall_err(exec_lua, 'return vim.str_byteindex(_G.test_text, ..., true)', #indices16 + 1)
    386    )
    387    matches(
    388      'index out of range',
    389      pcall_err(exec_lua, 'return vim.str_byteindex(_G.test_text, "utf-16", ...)', #indices16 + 1)
    390    )
    391    matches(
    392      'index out of range',
    393      pcall_err(exec_lua, 'return vim.str_byteindex(_G.test_text, "utf-32", ...)', #indices32 + 1)
    394    )
    395    matches(
    396      'invalid encoding',
    397      pcall_err(exec_lua, 'return vim.str_byteindex("hello", "madeupencoding", 1)')
    398    )
    399    eq(
    400      indices32[#indices32],
    401      exec_lua('return vim.str_byteindex(_G.test_text, "utf-32", 99999, false)')
    402    )
    403    eq(
    404      indices16[#indices16],
    405      exec_lua('return vim.str_byteindex(_G.test_text, "utf-16", 99999, false)')
    406    )
    407    eq(
    408      indices8[#indices8],
    409      exec_lua('return vim.str_byteindex(_G.test_text, "utf-8", 99999, false)')
    410    )
    411    eq(2, exec_lua('return vim.str_byteindex("é", "utf-16", 2, false)'))
    412    local i32, i16, i8 = 0, 0, 0
    413    local len = 51
    414    for k = 0, len do
    415      if indices32[i32] < k then
    416        i32 = i32 + 1
    417      end
    418      if indices16[i16] < k then
    419        i16 = i16 + 1
    420        if indices16[i16 + 1] == indices16[i16] then
    421          i16 = i16 + 1
    422        end
    423      end
    424      if indices8[i8] < k then
    425        i8 = i8 + 1
    426      end
    427      eq({ i32, i16 }, exec_lua('return {vim.str_utfindex(_G.test_text, ...)}', k), k)
    428      eq({ i32 }, exec_lua('return {vim.str_utfindex(_G.test_text, "utf-32", ...)}', k), k)
    429      eq({ i16 }, exec_lua('return {vim.str_utfindex(_G.test_text, "utf-16", ...)}', k), k)
    430      eq({ i8 }, exec_lua('return {vim.str_utfindex(_G.test_text, "utf-8", ...)}', k), k)
    431    end
    432 
    433    eq({ #indices32, #indices16 }, exec_lua('return {vim.str_utfindex(_G.test_text)}'))
    434 
    435    eq(#indices32, exec_lua('return vim.str_utfindex(_G.test_text, "utf-32", math.huge, false)'))
    436    eq(#indices16, exec_lua('return vim.str_utfindex(_G.test_text, "utf-16", math.huge, false)'))
    437    eq(#indices8, exec_lua('return vim.str_utfindex(_G.test_text, "utf-8", math.huge, false)'))
    438 
    439    eq(#indices32, exec_lua('return vim.str_utfindex(_G.test_text, "utf-32")'))
    440    eq(#indices16, exec_lua('return vim.str_utfindex(_G.test_text, "utf-16")'))
    441    eq(#indices8, exec_lua('return vim.str_utfindex(_G.test_text, "utf-8")'))
    442    matches(
    443      'invalid encoding',
    444      pcall_err(exec_lua, 'return vim.str_utfindex(_G.test_text, "madeupencoding", ...)', 1)
    445    )
    446    matches(
    447      'index out of range',
    448      pcall_err(exec_lua, 'return vim.str_utfindex(_G.test_text, ...)', len + 1)
    449    )
    450  end)
    451 
    452  it('vim.str_utf_start', function()
    453    exec_lua([[_G.test_text = "xy åäö ɧ 汉语 ↥ 🤦x🦄 å بِيَّ"]])
    454    local expected_positions = {
    455      0,
    456      0,
    457      0,
    458      0,
    459      -1,
    460      0,
    461      -1,
    462      0,
    463      -1,
    464      0,
    465      0,
    466      -1,
    467      0,
    468      0,
    469      -1,
    470      -2,
    471      0,
    472      -1,
    473      -2,
    474      0,
    475      0,
    476      -1,
    477      -2,
    478      0,
    479      0,
    480      -1,
    481      -2,
    482      -3,
    483      0,
    484      0,
    485      -1,
    486      -2,
    487      -3,
    488      0,
    489      0,
    490      0,
    491      -1,
    492      0,
    493      0,
    494      -1,
    495      0,
    496      -1,
    497      0,
    498      -1,
    499      0,
    500      -1,
    501      0,
    502      -1,
    503    }
    504    eq(
    505      expected_positions,
    506      exec_lua([[
    507      local start_codepoint_positions = {}
    508      for idx = 1, #_G.test_text do
    509        table.insert(start_codepoint_positions, vim.str_utf_start(_G.test_text, idx))
    510      end
    511      return start_codepoint_positions
    512    ]])
    513    )
    514  end)
    515 
    516  it('vim.str_utf_end', function()
    517    exec_lua([[_G.test_text = "xy åäö ɧ 汉语 ↥ 🤦x🦄 å بِيَّ"]])
    518    local expected_positions = {
    519      0,
    520      0,
    521      0,
    522      1,
    523      0,
    524      1,
    525      0,
    526      1,
    527      0,
    528      0,
    529      1,
    530      0,
    531      0,
    532      2,
    533      1,
    534      0,
    535      2,
    536      1,
    537      0,
    538      0,
    539      2,
    540      1,
    541      0,
    542      0,
    543      3,
    544      2,
    545      1,
    546      0,
    547      0,
    548      3,
    549      2,
    550      1,
    551      0,
    552      0,
    553      0,
    554      1,
    555      0,
    556      0,
    557      1,
    558      0,
    559      1,
    560      0,
    561      1,
    562      0,
    563      1,
    564      0,
    565      1,
    566      0,
    567    }
    568    eq(
    569      expected_positions,
    570      exec_lua([[
    571      local end_codepoint_positions = {}
    572      for idx = 1, #_G.test_text do
    573        table.insert(end_codepoint_positions, vim.str_utf_end(_G.test_text, idx))
    574      end
    575      return end_codepoint_positions
    576    ]])
    577    )
    578  end)
    579 
    580  it('vim.str_utf_pos', function()
    581    exec_lua([[_G.test_text = "xy åäö ɧ 汉语 ↥ 🤦x🦄 å بِيَّ"]])
    582    local expected_positions = {
    583      1,
    584      2,
    585      3,
    586      4,
    587      6,
    588      8,
    589      10,
    590      11,
    591      13,
    592      14,
    593      17,
    594      20,
    595      21,
    596      24,
    597      25,
    598      29,
    599      30,
    600      34,
    601      35,
    602      36,
    603      38,
    604      39,
    605      41,
    606      43,
    607      45,
    608      47,
    609    }
    610    eq(expected_positions, exec_lua('return vim.str_utf_pos(_G.test_text)'))
    611  end)
    612 
    613  it('vim.schedule', function()
    614    exec_lua([[
    615      test_table = {}
    616      vim.schedule(function()
    617        table.insert(test_table, "xx")
    618      end)
    619      table.insert(test_table, "yy")
    620    ]])
    621    eq({ 'yy', 'xx' }, exec_lua('return test_table'))
    622 
    623    -- Validates args.
    624    matches('vim.schedule: expected function', pcall_err(exec_lua, "vim.schedule('stringly')"))
    625    matches('vim.schedule: expected function', pcall_err(exec_lua, 'vim.schedule()'))
    626 
    627    exec_lua([[
    628      vim.schedule(function()
    629        error("big failure\nvery async")
    630      end)
    631    ]])
    632 
    633    feed('<cr>')
    634    matches('big failure\nvery async', remove_trace(eval('v:errmsg')))
    635 
    636    local screen = Screen.new(60, 5)
    637    screen:expect {
    638      grid = [[
    639      ^                                                            |
    640      {1:~                                                           }|*3
    641                                                                  |
    642    ]],
    643    }
    644 
    645    -- nvim_command causes a Vimscript exception, check that it is properly caught
    646    -- and propagated as an error message in async contexts.. #10809
    647    exec_lua([[
    648      vim.schedule(function()
    649        vim.api.nvim_command(":echo 'err")
    650      end)
    651    ]])
    652    screen:expect {
    653      grid = [[
    654      {9:stack traceback:}                                            |
    655      {9:        [C]: in function 'nvim_command'}                     |
    656      {9:        [string "<nvim>"]:2: in function <[string "<nvim>"]:}|
    657      {9:1>}                                                          |
    658      {6:Press ENTER or type command to continue}^                     |
    659    ]],
    660    }
    661  end)
    662 
    663  it('vim.gsplit, vim.split', function()
    664    local tests = {
    665      --                            plain  trimempty
    666      { 'a,b', ',', false, false, { 'a', 'b' } },
    667      { ':aa::::bb:', ':', false, false, { '', 'aa', '', '', '', 'bb', '' } },
    668      { ':aa::::bb:', ':', false, true, { 'aa', '', '', '', 'bb' } },
    669      { 'aa::::bb:', ':', false, true, { 'aa', '', '', '', 'bb' } },
    670      { ':aa::bb:', ':', false, true, { 'aa', '', 'bb' } },
    671      { '/a/b:/b/\n', '[:\n]', false, true, { '/a/b', '/b/' } },
    672      { '::ee::ff:', ':', false, false, { '', '', 'ee', '', 'ff', '' } },
    673      { '::ee::ff::', ':', false, true, { 'ee', '', 'ff' } },
    674      { 'ab', '.', false, false, { '', '', '' } },
    675      { 'a1b2c', '[0-9]', false, false, { 'a', 'b', 'c' } },
    676      { 'xy', '', false, false, { 'x', 'y' } },
    677      { 'here be dragons', ' ', false, false, { 'here', 'be', 'dragons' } },
    678      { 'axaby', 'ab?', false, false, { '', 'x', 'y' } },
    679      { 'f v2v v3v w2w ', '([vw])2%1', false, false, { 'f ', ' v3v ', ' ' } },
    680      { '', '', false, false, {} },
    681      { '', '', false, true, {} },
    682      { '\n', '[:\n]', false, true, {} },
    683      { '', 'a', false, false, { '' } },
    684      { 'x*yz*oo*l', '*', true, false, { 'x', 'yz', 'oo', 'l' } },
    685    }
    686 
    687    for _, q in ipairs(tests) do
    688      eq(q[5], vim.split(q[1], q[2], { plain = q[3], trimempty = q[4] }), q[1])
    689    end
    690 
    691    -- Test old signature
    692    eq({ 'x', 'yz', 'oo', 'l' }, vim.split('x*yz*oo*l', '*', true))
    693 
    694    local loops = {
    695      { 'abc', '.-' },
    696    }
    697 
    698    for _, q in ipairs(loops) do
    699      matches('Infinite loop detected', pcall_err(vim.split, q[1], q[2]))
    700    end
    701 
    702    -- Validates args.
    703    eq(true, pcall(vim.split, 'string', 'string'))
    704    matches('s: expected string, got number', pcall_err(vim.split, 1, 'string'))
    705    matches('sep: expected string, got number', pcall_err(vim.split, 'string', 1))
    706    matches('opts: expected table, got number', pcall_err(vim.split, 'string', 'string', 1))
    707  end)
    708 
    709  it('vim.trim', function()
    710    local trim = function(s)
    711      return exec_lua('return vim.trim(...)', s)
    712    end
    713 
    714    local trims = {
    715      { '   a', 'a' },
    716      { ' b  ', 'b' },
    717      { '\tc', 'c' },
    718      { 'r\n', 'r' },
    719      { '', '' },
    720      { ' \t \n', '' },
    721    }
    722 
    723    for _, q in ipairs(trims) do
    724      eq(q[2], trim(q[1]))
    725    end
    726 
    727    -- Validates args.
    728    matches('s: expected string, got number', pcall_err(trim, 2))
    729  end)
    730 
    731  it('vim.inspect', function()
    732    -- just make sure it basically works, it has its own test suite
    733    local inspect = function(q, opts)
    734      return exec_lua('return vim.inspect(...)', q, opts)
    735    end
    736 
    737    eq('2', inspect(2))
    738    eq('{+a = {+b = 1+}+}', inspect({ a = { b = 1 } }, { newline = '+', indent = '' }))
    739 
    740    -- special value vim.inspect.KEY works
    741    eq(
    742      '{  KEY_a = "x",  KEY_b = "y"}',
    743      exec_lua([[
    744      return vim.inspect({a="x", b="y"}, {newline = '', process = function(item, path)
    745        if path[#path] == vim.inspect.KEY then
    746          return 'KEY_'..item
    747        end
    748        return item
    749      end})
    750    ]])
    751    )
    752  end)
    753 
    754  it('vim.deepcopy', function()
    755    ok(exec_lua([[
    756      local a = { x = { 1, 2 }, y = 5}
    757      local b = vim.deepcopy(a)
    758 
    759      return b.x[1] == 1 and b.x[2] == 2 and b.y == 5 and vim.tbl_count(b) == 2
    760             and tostring(a) ~= tostring(b)
    761    ]]))
    762 
    763    ok(exec_lua([[
    764      local a = {}
    765      local b = vim.deepcopy(a)
    766 
    767      return vim.islist(b) and vim.tbl_count(b) == 0 and tostring(a) ~= tostring(b)
    768    ]]))
    769 
    770    ok(exec_lua([[
    771      local a = vim.empty_dict()
    772      local b = vim.deepcopy(a)
    773 
    774      return not vim.islist(b) and vim.tbl_count(b) == 0
    775    ]]))
    776 
    777    ok(exec_lua([[
    778      local a = {x = vim.empty_dict(), y = {}}
    779      local b = vim.deepcopy(a)
    780 
    781      return not vim.islist(b.x) and vim.islist(b.y)
    782        and vim.tbl_count(b) == 2
    783        and tostring(a) ~= tostring(b)
    784    ]]))
    785 
    786    ok(exec_lua([[
    787      local f1 = function() return 1 end
    788      local f2 = function() return 2 end
    789      local t1 = {f = f1}
    790      local t2 = vim.deepcopy(t1)
    791      t1.f = f2
    792      return t1.f() ~= t2.f()
    793    ]]))
    794 
    795    ok(exec_lua([[
    796      local t1 = {a = 5}
    797      t1.self = t1
    798      local t2 = vim.deepcopy(t1)
    799      return t2.self == t2 and t2.self ~= t1
    800    ]]))
    801 
    802    ok(exec_lua([[
    803      local mt = {mt=true}
    804      local t1 = setmetatable({a = 5}, mt)
    805      local t2 = vim.deepcopy(t1)
    806      return getmetatable(t2) == mt
    807    ]]))
    808 
    809    ok(exec_lua([[
    810      local t1 = {a = vim.NIL}
    811      local t2 = vim.deepcopy(t1)
    812      return t2.a == vim.NIL
    813    ]]))
    814 
    815    matches(
    816      'Cannot deepcopy object of type thread',
    817      pcall_err(
    818        exec_lua,
    819        [[
    820        local thread = coroutine.create(function () return 0 end)
    821        local t = {thr = thread}
    822        vim.deepcopy(t)
    823      ]]
    824      )
    825    )
    826  end)
    827 
    828  it('vim.pesc', function()
    829    eq('foo%-bar', exec_lua([[return vim.pesc('foo-bar')]]))
    830    eq('foo%%%-bar', exec_lua([[return vim.pesc(vim.pesc('foo-bar'))]]))
    831    -- pesc() returns one result. #20751
    832    eq({ 'x' }, exec_lua([[return {vim.pesc('x')}]]))
    833 
    834    -- Validates args.
    835    matches('s: expected string, got number', pcall_err(exec_lua, [[return vim.pesc(2)]]))
    836  end)
    837 
    838  it('vim.list_contains', function()
    839    eq(true, exec_lua("return vim.list_contains({'a','b','c'}, 'c')"))
    840    eq(false, exec_lua("return vim.list_contains({'a','b','c'}, 'd')"))
    841  end)
    842 
    843  it('vim.tbl_contains', function()
    844    eq(true, exec_lua("return vim.tbl_contains({'a','b','c'}, 'c')"))
    845    eq(false, exec_lua("return vim.tbl_contains({'a','b','c'}, 'd')"))
    846    eq(true, exec_lua("return vim.tbl_contains({[2]='a',foo='b',[5] = 'c'}, 'c')"))
    847    eq(
    848      true,
    849      exec_lua([[
    850        return vim.tbl_contains({ 'a', { 'b', 'c' } }, function(v)
    851          return vim.deep_equal(v, { 'b', 'c' })
    852        end, { predicate = true })
    853    ]])
    854    )
    855  end)
    856 
    857  it('vim.tbl_keys', function()
    858    eq({}, exec_lua('return vim.tbl_keys({})'))
    859    for _, v in pairs(exec_lua("return vim.tbl_keys({'a', 'b', 'c'})")) do
    860      eq(true, exec_lua('return vim.tbl_contains({ 1, 2, 3 }, ...)', v))
    861    end
    862    for _, v in pairs(exec_lua('return vim.tbl_keys({a=1, b=2, c=3})')) do
    863      eq(true, exec_lua("return vim.tbl_contains({ 'a', 'b', 'c' }, ...)", v))
    864    end
    865  end)
    866 
    867  it('vim.tbl_values', function()
    868    eq({}, exec_lua('return vim.tbl_values({})'))
    869    for _, v in pairs(exec_lua("return vim.tbl_values({'a', 'b', 'c'})")) do
    870      eq(true, exec_lua("return vim.tbl_contains({ 'a', 'b', 'c' }, ...)", v))
    871    end
    872    for _, v in pairs(exec_lua('return vim.tbl_values({a=1, b=2, c=3})')) do
    873      eq(true, exec_lua('return vim.tbl_contains({ 1, 2, 3 }, ...)', v))
    874    end
    875  end)
    876 
    877  it('vim.tbl_map', function()
    878    eq(
    879      {},
    880      exec_lua([[
    881      return vim.tbl_map(function(v) return v * 2 end, {})
    882    ]])
    883    )
    884    eq(
    885      { 2, 4, 6 },
    886      exec_lua([[
    887      return vim.tbl_map(function(v) return v * 2 end, {1, 2, 3})
    888    ]])
    889    )
    890    eq(
    891      { { i = 2 }, { i = 4 }, { i = 6 } },
    892      exec_lua([[
    893      return vim.tbl_map(function(v) return { i = v.i * 2 } end, {{i=1}, {i=2}, {i=3}})
    894    ]])
    895    )
    896  end)
    897 
    898  it('vim.tbl_filter', function()
    899    eq(
    900      {},
    901      exec_lua([[
    902      return vim.tbl_filter(function(v) return (v % 2) == 0 end, {})
    903    ]])
    904    )
    905    eq(
    906      { 2 },
    907      exec_lua([[
    908      return vim.tbl_filter(function(v) return (v % 2) == 0 end, {1, 2, 3})
    909    ]])
    910    )
    911    eq(
    912      { { i = 2 } },
    913      exec_lua([[
    914      return vim.tbl_filter(function(v) return (v.i % 2) == 0 end, {{i=1}, {i=2}, {i=3}})
    915    ]])
    916    )
    917  end)
    918 
    919  it('vim.isarray', function()
    920    eq(true, exec_lua('return vim.isarray({})'))
    921    eq(false, exec_lua('return vim.isarray(vim.empty_dict())'))
    922    eq(true, exec_lua("return vim.isarray({'a', 'b', 'c'})"))
    923    eq(false, exec_lua("return vim.isarray({'a', '32', a='hello', b='baz'})"))
    924    eq(false, exec_lua("return vim.isarray({1, a='hello', b='baz'})"))
    925    eq(false, exec_lua("return vim.isarray({a='hello', b='baz', 1})"))
    926    eq(false, exec_lua("return vim.isarray({1, 2, nil, a='hello'})"))
    927    eq(true, exec_lua('return vim.isarray({1, 2, nil, 4})'))
    928    eq(true, exec_lua('return vim.isarray({nil, 2, 3, 4})'))
    929    eq(false, exec_lua('return vim.isarray({1, [1.5]=2, [3]=3})'))
    930  end)
    931 
    932  it('vim.islist', function()
    933    eq(true, exec_lua('return vim.islist({})'))
    934    eq(false, exec_lua('return vim.islist(vim.empty_dict())'))
    935    eq(true, exec_lua("return vim.islist({'a', 'b', 'c'})"))
    936    eq(false, exec_lua("return vim.islist({'a', '32', a='hello', b='baz'})"))
    937    eq(false, exec_lua("return vim.islist({1, a='hello', b='baz'})"))
    938    eq(false, exec_lua("return vim.islist({a='hello', b='baz', 1})"))
    939    eq(false, exec_lua("return vim.islist({1, 2, nil, a='hello'})"))
    940    eq(false, exec_lua('return vim.islist({1, 2, nil, 4})'))
    941    eq(false, exec_lua('return vim.islist({nil, 2, 3, 4})'))
    942    eq(false, exec_lua('return vim.islist({1, [1.5]=2, [3]=3})'))
    943  end)
    944 
    945  it('vim.tbl_isempty', function()
    946    eq(true, exec_lua('return vim.tbl_isempty({})'))
    947    eq(false, exec_lua('return vim.tbl_isempty({ 1, 2, 3 })'))
    948    eq(false, exec_lua('return vim.tbl_isempty({a=1, b=2, c=3})'))
    949  end)
    950 
    951  it('vim.tbl_get', function()
    952    eq(
    953      true,
    954      exec_lua("return vim.tbl_get({ test = { nested_test = true }}, 'test', 'nested_test')")
    955    )
    956    eq(NIL, exec_lua("return vim.tbl_get({ unindexable = true }, 'unindexable', 'missing_key')"))
    957    eq(NIL, exec_lua("return vim.tbl_get({ unindexable = 1 }, 'unindexable', 'missing_key')"))
    958    eq(
    959      NIL,
    960      exec_lua(
    961        "return vim.tbl_get({ unindexable = coroutine.create(function () end) }, 'unindexable', 'missing_key')"
    962      )
    963    )
    964    eq(
    965      NIL,
    966      exec_lua(
    967        "return vim.tbl_get({ unindexable = function () end }, 'unindexable', 'missing_key')"
    968      )
    969    )
    970    eq(NIL, exec_lua("return vim.tbl_get({}, 'missing_key')"))
    971    eq(NIL, exec_lua('return vim.tbl_get({})'))
    972    eq(NIL, exec_lua("return vim.tbl_get({}, nil, 'key')"))
    973    eq(1, exec_lua("return select('#', vim.tbl_get({}))"))
    974    eq(1, exec_lua("return select('#', vim.tbl_get({ nested = {} }, 'nested', 'missing_key'))"))
    975  end)
    976 
    977  it('vim.tbl_extend', function()
    978    ok(exec_lua([[
    979      local a = {x = 1}
    980      local b = {y = 2}
    981      local c = vim.tbl_extend("keep", a, b)
    982 
    983      return c.x == 1 and b.y == 2 and vim.tbl_count(c) == 2
    984    ]]))
    985 
    986    ok(exec_lua([[
    987      local a = {x = 1}
    988      local b = {y = 2}
    989      local c = {z = 3}
    990      local d = vim.tbl_extend("keep", a, b, c)
    991 
    992      return d.x == 1 and d.y == 2 and d.z == 3 and vim.tbl_count(d) == 3
    993    ]]))
    994 
    995    ok(exec_lua([[
    996      local a = {x = 1}
    997      local b = {x = 3}
    998      local c = vim.tbl_extend("keep", a, b)
    999 
   1000      return c.x == 1 and vim.tbl_count(c) == 1
   1001    ]]))
   1002 
   1003    ok(exec_lua([[
   1004      local a = {x = 1}
   1005      local b = {x = 3}
   1006      local c = vim.tbl_extend("force", a, b)
   1007 
   1008      return c.x == 3 and vim.tbl_count(c) == 1
   1009    ]]))
   1010 
   1011    ok(exec_lua([[
   1012      local a = vim.empty_dict()
   1013      local b = {}
   1014      local c = vim.tbl_extend("keep", a, b)
   1015 
   1016      return not vim.islist(c) and vim.tbl_count(c) == 0
   1017    ]]))
   1018 
   1019    ok(exec_lua([[
   1020      local a = {}
   1021      local b = vim.empty_dict()
   1022      local c = vim.tbl_extend("keep", a, b)
   1023 
   1024      return vim.islist(c) and vim.tbl_count(c) == 0
   1025    ]]))
   1026 
   1027    ok(exec_lua([[
   1028      local a = {x = {a = 1, b = 2}}
   1029      local b = {x = {a = 2, c = {y = 3}}}
   1030      local c = vim.tbl_extend("keep", a, b)
   1031 
   1032      local count = 0
   1033      for _ in pairs(c) do count = count + 1 end
   1034 
   1035      return c.x.a == 1 and c.x.b == 2 and c.x.c == nil and count == 1
   1036    ]]))
   1037 
   1038    ok(exec_lua([[
   1039      local a = { a = 1, b = 2, c = 1 }
   1040      local b = { a = -1, b = 5, c = 3, d = 4 }
   1041      -- Return the maximum value for each key.
   1042      local c = vim.tbl_extend(function(k, prev_v, v)
   1043        if prev_v then
   1044          return v > prev_v and v or prev_v
   1045        else
   1046          return v
   1047        end
   1048      end, a, b)
   1049      return vim.deep_equal(c, { a = 1, b = 5, c = 3, d = 4 })
   1050    ]]))
   1051 
   1052    matches(
   1053      'invalid "behavior": nil',
   1054      pcall_err(
   1055        exec_lua,
   1056        [[
   1057        return vim.tbl_extend()
   1058      ]]
   1059      )
   1060    )
   1061 
   1062    matches(
   1063      'wrong number of arguments %(given 1, expected at least 3%)',
   1064      pcall_err(
   1065        exec_lua,
   1066        [[
   1067        return vim.tbl_extend("keep")
   1068      ]]
   1069      )
   1070    )
   1071 
   1072    matches(
   1073      'wrong number of arguments %(given 2, expected at least 3%)',
   1074      pcall_err(
   1075        exec_lua,
   1076        [[
   1077        return vim.tbl_extend("keep", {})
   1078      ]]
   1079      )
   1080    )
   1081  end)
   1082 
   1083  it('vim.tbl_deep_extend', function()
   1084    ok(exec_lua([[
   1085      local a = {x = {a = 1, b = 2}}
   1086      local b = {x = {a = 2, c = {y = 3}}}
   1087      local c = vim.tbl_deep_extend("keep", a, b)
   1088 
   1089      local count = 0
   1090      for _ in pairs(c) do count = count + 1 end
   1091 
   1092      return c.x.a == 1 and c.x.b == 2 and c.x.c.y == 3 and count == 1
   1093    ]]))
   1094 
   1095    ok(exec_lua([[
   1096      local a = {x = {a = 1, b = 2}}
   1097      local b = {x = {a = 2, c = {y = 3}}}
   1098      local c = vim.tbl_deep_extend("force", a, b)
   1099 
   1100      local count = 0
   1101      for _ in pairs(c) do count = count + 1 end
   1102 
   1103      return c.x.a == 2 and c.x.b == 2 and c.x.c.y == 3 and count == 1
   1104    ]]))
   1105 
   1106    ok(exec_lua([[
   1107      local a = {x = {a = 1, b = 2}}
   1108      local b = {x = {a = 2, c = {y = 3}}}
   1109      local c = {x = {c = 4, d = {y = 4}}}
   1110      local d = vim.tbl_deep_extend("keep", a, b, c)
   1111 
   1112      local count = 0
   1113      for _ in pairs(c) do count = count + 1 end
   1114 
   1115      return d.x.a == 1 and d.x.b == 2 and d.x.c.y == 3 and d.x.d.y == 4 and count == 1
   1116    ]]))
   1117 
   1118    ok(exec_lua([[
   1119      local a = {x = {a = 1, b = 2}}
   1120      local b = {x = {a = 2, c = {y = 3}}}
   1121      local c = {x = {c = 4, d = {y = 4}}}
   1122      local d = vim.tbl_deep_extend("force", a, b, c)
   1123 
   1124      local count = 0
   1125      for _ in pairs(c) do count = count + 1 end
   1126 
   1127      return d.x.a == 2 and d.x.b == 2 and d.x.c == 4 and d.x.d.y == 4 and count == 1
   1128    ]]))
   1129 
   1130    ok(exec_lua([[
   1131      local a = vim.empty_dict()
   1132      local b = {}
   1133      local c = vim.tbl_deep_extend("keep", a, b)
   1134 
   1135      local count = 0
   1136      for _ in pairs(c) do count = count + 1 end
   1137 
   1138      return not vim.islist(c) and count == 0
   1139    ]]))
   1140 
   1141    ok(exec_lua([[
   1142      local a = {}
   1143      local b = vim.empty_dict()
   1144      local c = vim.tbl_deep_extend("keep", a, b)
   1145 
   1146      local count = 0
   1147      for _ in pairs(c) do count = count + 1 end
   1148 
   1149      return vim.islist(c) and count == 0
   1150    ]]))
   1151 
   1152    eq(
   1153      { a = { b = 1 } },
   1154      exec_lua([[
   1155      local a = { a = { b = 1 } }
   1156      local b = { a = {} }
   1157      return vim.tbl_deep_extend("force", a, b)
   1158    ]])
   1159    )
   1160 
   1161    eq(
   1162      { a = { b = 1 } },
   1163      exec_lua([[
   1164      local a = { a = 123 }
   1165      local b = { a = { b = 1} }
   1166      return vim.tbl_deep_extend("force", a, b)
   1167    ]])
   1168    )
   1169 
   1170    ok(exec_lua([[
   1171      local a = { a = {[2] = 3} }
   1172      local b = { a = {[3] = 3} }
   1173      local c = vim.tbl_deep_extend("force", a, b)
   1174      return vim.deep_equal(c, {a = {[2] = 3, [3] = 3}})
   1175    ]]))
   1176 
   1177    eq(
   1178      { a = 123 },
   1179      exec_lua([[
   1180      local a = { a = { b = 1} }
   1181      local b = { a = 123 }
   1182      return vim.tbl_deep_extend("force", a, b)
   1183    ]])
   1184    )
   1185 
   1186    ok(exec_lua([[
   1187      local a = { sub = { 'a', 'b' } }
   1188      local b = { sub = { 'b', 'c' } }
   1189      local c = vim.tbl_deep_extend('force', a, b)
   1190      return vim.deep_equal(c, { sub = { 'b', 'c' } })
   1191    ]]))
   1192 
   1193    ok(exec_lua([[
   1194      local a = { a = 1, b = 2, c = { d = 1, e = -2} }
   1195      local b = { a = -1, b = 5, c = { d = 6 } }
   1196      -- Return the maximum value for each key.
   1197      local c = vim.tbl_deep_extend(function(k, prev_v, v)
   1198        if prev_v then
   1199          return v > prev_v and v or prev_v
   1200        else
   1201          return v
   1202        end
   1203      end, a, b)
   1204      return vim.deep_equal(c, { a = 1, b = 5, c = { d = 6, e = -2 } })
   1205    ]]))
   1206 
   1207    matches('invalid "behavior": nil', pcall_err(exec_lua, [[return vim.tbl_deep_extend()]]))
   1208 
   1209    matches(
   1210      'wrong number of arguments %(given 1, expected at least 3%)',
   1211      pcall_err(exec_lua, [[return vim.tbl_deep_extend("keep")]])
   1212    )
   1213 
   1214    matches(
   1215      'wrong number of arguments %(given 2, expected at least 3%)',
   1216      pcall_err(exec_lua, [[return vim.tbl_deep_extend("keep", {})]])
   1217    )
   1218 
   1219    matches(
   1220      'after the second argument%: expected table, got number',
   1221      pcall_err(exec_lua, [[return vim.tbl_deep_extend("keep", {}, 42)]])
   1222    )
   1223  end)
   1224 
   1225  it('vim.tbl_count', function()
   1226    eq(0, exec_lua [[ return vim.tbl_count({}) ]])
   1227    eq(0, exec_lua [[ return vim.tbl_count(vim.empty_dict()) ]])
   1228    eq(0, exec_lua [[ return vim.tbl_count({nil}) ]])
   1229    eq(0, exec_lua [[ return vim.tbl_count({a=nil}) ]])
   1230    eq(1, exec_lua [[ return vim.tbl_count({1}) ]])
   1231    eq(2, exec_lua [[ return vim.tbl_count({1, 2}) ]])
   1232    eq(2, exec_lua [[ return vim.tbl_count({1, nil, 3}) ]])
   1233    eq(1, exec_lua [[ return vim.tbl_count({a=1}) ]])
   1234    eq(2, exec_lua [[ return vim.tbl_count({a=1, b=2}) ]])
   1235    eq(2, exec_lua [[ return vim.tbl_count({a=1, b=nil, c=3}) ]])
   1236  end)
   1237 
   1238  it('vim.deep_equal', function()
   1239    eq(true, exec_lua [[ return vim.deep_equal({a=1}, {a=1}) ]])
   1240    eq(true, exec_lua [[ return vim.deep_equal({a={b=1}}, {a={b=1}}) ]])
   1241    eq(true, exec_lua [[ return vim.deep_equal({a={b={nil}}}, {a={b={}}}) ]])
   1242    eq(true, exec_lua [[ return vim.deep_equal({a=1, [5]=5}, {nil,nil,nil,nil,5,a=1}) ]])
   1243    eq(false, exec_lua [[ return vim.deep_equal(1, {nil,nil,nil,nil,5,a=1}) ]])
   1244    eq(false, exec_lua [[ return vim.deep_equal(1, 3) ]])
   1245    eq(false, exec_lua [[ return vim.deep_equal(nil, 3) ]])
   1246    eq(false, exec_lua [[ return vim.deep_equal({a=1}, {a=2}) ]])
   1247  end)
   1248 
   1249  it('vim.list_extend', function()
   1250    eq({ 1, 2, 3 }, exec_lua [[ return vim.list_extend({1}, {2,3}) ]])
   1251    matches(
   1252      'src: expected table, got nil',
   1253      pcall_err(exec_lua, [[ return vim.list_extend({1}, nil) ]])
   1254    )
   1255    eq({ 1, 2 }, exec_lua [[ return vim.list_extend({1}, {2;a=1}) ]])
   1256    eq(true, exec_lua [[ local a = {1} return vim.list_extend(a, {2;a=1}) == a ]])
   1257    eq({ 2 }, exec_lua [[ return vim.list_extend({}, {2;a=1}, 1) ]])
   1258    eq({}, exec_lua [[ return vim.list_extend({}, {2;a=1}, 2) ]])
   1259    eq({}, exec_lua [[ return vim.list_extend({}, {2;a=1}, 1, -1) ]])
   1260    eq({ 2 }, exec_lua [[ return vim.list_extend({}, {2;a=1}, -1, 2) ]])
   1261  end)
   1262 
   1263  it('vim.tbl_add_reverse_lookup', function()
   1264    eq(
   1265      true,
   1266      exec_lua [[
   1267    local a = { A = 1 }
   1268    vim.tbl_add_reverse_lookup(a)
   1269    return vim.deep_equal(a, { A = 1; [1] = 'A'; })
   1270    ]]
   1271    )
   1272    -- Throw an error for trying to do it twice (run into an existing key)
   1273    local code = [[
   1274    local res = {}
   1275    local a = { A = 1 }
   1276    vim.tbl_add_reverse_lookup(a)
   1277    assert(vim.deep_equal(a, { A = 1; [1] = 'A'; }))
   1278    vim.tbl_add_reverse_lookup(a)
   1279    ]]
   1280    matches(
   1281      'The reverse lookup found an existing value for "[1A]" while processing key "[1A]"$',
   1282      pcall_err(exec_lua, code)
   1283    )
   1284  end)
   1285 
   1286  it('vim.spairs', function()
   1287    local res = ''
   1288    local table = {
   1289      ccc = 1,
   1290      bbb = 2,
   1291      ddd = 3,
   1292      aaa = 4,
   1293    }
   1294    for key, _ in vim.spairs(table) do
   1295      res = res .. key
   1296    end
   1297    matches('aaabbbcccddd', res)
   1298  end)
   1299 
   1300  it('vim.call, vim.fn', function()
   1301    eq(true, exec_lua([[return vim.call('sin', 0.0) == 0.0 ]]))
   1302    eq(true, exec_lua([[return vim.fn.sin(0.0) == 0.0 ]]))
   1303    -- compat: nvim_call_function uses "special" value for Vimscript float
   1304    eq(false, exec_lua([[return vim.api.nvim_call_function('sin', {0.0}) == 0.0 ]]))
   1305 
   1306    exec([[
   1307      func! FooFunc(test)
   1308        let g:test = a:test
   1309        return {}
   1310      endfunc
   1311      func! VarArg(...)
   1312        return a:000
   1313      endfunc
   1314      func! Nilly()
   1315        return [v:null, v:null]
   1316      endfunc
   1317    ]])
   1318    eq(true, exec_lua([[return next(vim.fn.FooFunc(3)) == nil ]]))
   1319    eq(3, eval('g:test'))
   1320    eq(true, exec_lua([[return vim.tbl_isempty(vim.api.nvim_call_function("FooFunc", {5}))]]))
   1321    eq(5, eval('g:test'))
   1322 
   1323    eq({ 2, 'foo', true }, exec_lua([[return vim.fn.VarArg(2, "foo", true)]]))
   1324 
   1325    eq(
   1326      true,
   1327      exec_lua([[
   1328      local x = vim.fn.Nilly()
   1329      return #x == 2 and x[1] == vim.NIL and x[2] == vim.NIL
   1330    ]])
   1331    )
   1332    eq({ NIL, NIL }, exec_lua([[return vim.fn.Nilly()]]))
   1333 
   1334    -- error handling
   1335    eq(
   1336      { false, 'Vim:E897: List or Blob required' },
   1337      exec_lua([[return {pcall(vim.fn.add, "aa", "bb")}]])
   1338    )
   1339 
   1340    -- conversion between LuaRef and Vim Funcref
   1341    eq(
   1342      true,
   1343      exec_lua([[
   1344      local x = vim.fn.VarArg(function() return 'foo' end, function() return 'bar' end)
   1345      return #x == 2 and x[1]() == 'foo' and x[2]() == 'bar'
   1346    ]])
   1347    )
   1348 
   1349    -- Test for #20211
   1350    eq(
   1351      'a (b) c',
   1352      exec_lua([[
   1353      return vim.fn.substitute('a b c', 'b', function(m) return '(' .. m[1] .. ')' end, 'g')
   1354    ]])
   1355    )
   1356  end)
   1357 
   1358  it('vim.call fails in fast context', function()
   1359    local screen = Screen.new(120, 10)
   1360    exec_lua([[
   1361      local timer = vim.uv.new_timer()
   1362      timer:start(0, 0, function()
   1363        timer:close()
   1364        vim.call('sin', 0.0)
   1365      end)
   1366    ]])
   1367    screen:expect({
   1368      any = pesc('E5560: Vimscript function "sin" must not be called in a fast event context'),
   1369    })
   1370    feed('<CR>')
   1371    assert_alive()
   1372  end)
   1373 
   1374  it('vim.fn errors when calling API function', function()
   1375    matches(
   1376      'Tried to call API function with vim.fn: use vim.api.nvim_get_current_line instead',
   1377      pcall_err(exec_lua, 'vim.fn.nvim_get_current_line()')
   1378    )
   1379  end)
   1380 
   1381  it('vim.fn is allowed in "fast" context by some functions #18306', function()
   1382    exec_lua([[
   1383      local timer = vim.uv.new_timer()
   1384      timer:start(0, 0, function()
   1385        timer:close()
   1386        assert(vim.in_fast_event())
   1387        vim.g.fnres = vim.fn.iconv('hello', 'utf-8', 'utf-8')
   1388      end)
   1389    ]])
   1390 
   1391    poke_eventloop()
   1392    eq('hello', exec_lua [[return vim.g.fnres]])
   1393  end)
   1394 
   1395  it('vim.rpcrequest and vim.rpcnotify', function()
   1396    exec_lua([[
   1397      chan = vim.fn.jobstart({'cat'}, {rpc=true})
   1398      vim.rpcrequest(chan, 'nvim_set_current_line', 'meow')
   1399    ]])
   1400    eq('meow', api.nvim_get_current_line())
   1401    command("let x = [3, 'aa', v:true, v:null]")
   1402    eq(
   1403      true,
   1404      exec_lua([[
   1405      ret = vim.rpcrequest(chan, 'nvim_get_var', 'x')
   1406      return #ret == 4 and ret[1] == 3 and ret[2] == 'aa' and ret[3] == true and ret[4] == vim.NIL
   1407    ]])
   1408    )
   1409    eq({ 3, 'aa', true, NIL }, exec_lua([[return ret]]))
   1410 
   1411    eq(
   1412      { {}, {}, false, true },
   1413      exec_lua([[
   1414      vim.rpcrequest(chan, 'nvim_exec', 'let xx = {}\nlet yy = []', false)
   1415      local dict = vim.rpcrequest(chan, 'nvim_eval', 'xx')
   1416      local list = vim.rpcrequest(chan, 'nvim_eval', 'yy')
   1417      return {dict, list, vim.islist(dict), vim.islist(list)}
   1418     ]])
   1419    )
   1420 
   1421    exec_lua([[
   1422       vim.rpcrequest(chan, 'nvim_set_var', 'aa', {})
   1423       vim.rpcrequest(chan, 'nvim_set_var', 'bb', vim.empty_dict())
   1424     ]])
   1425    eq({ 1, 1 }, eval('[type(g:aa) == type([]), type(g:bb) == type({})]'))
   1426 
   1427    -- error handling
   1428    eq({ false, 'Invalid channel: 23' }, exec_lua([[return {pcall(vim.rpcrequest, 23, 'foo')}]]))
   1429    eq({ false, 'Invalid channel: 23' }, exec_lua([[return {pcall(vim.rpcnotify, 23, 'foo')}]]))
   1430 
   1431    eq(
   1432      { false, 'Vim:E121: Undefined variable: foobar' },
   1433      exec_lua([[return {pcall(vim.rpcrequest, chan, 'nvim_eval', "foobar")}]])
   1434    )
   1435 
   1436    -- rpcnotify doesn't wait on request
   1437    eq(
   1438      'meow',
   1439      exec_lua([[
   1440      vim.rpcnotify(chan, 'nvim_set_current_line', 'foo')
   1441      return vim.api.nvim_get_current_line()
   1442    ]])
   1443    )
   1444    retry(10, nil, function()
   1445      eq('foo', api.nvim_get_current_line())
   1446    end)
   1447 
   1448    local screen = Screen.new(50, 7)
   1449    exec_lua([[
   1450      timer = vim.uv.new_timer()
   1451      timer:start(20, 0, function ()
   1452        -- notify ok (executed later when safe)
   1453        vim.rpcnotify(chan, 'nvim_set_var', 'yy', {3, vim.NIL})
   1454        -- rpcrequest an error
   1455        vim.rpcrequest(chan, 'nvim_set_current_line', 'bork')
   1456      end)
   1457    ]])
   1458    screen:expect {
   1459      grid = [[
   1460      {9:[string "<nvim>"]:6: E5560: rpcrequest must not be}|
   1461      {9: called in a fast event context}                   |
   1462      {9:stack traceback:}                                  |
   1463      {9:        [C]: in function 'rpcrequest'}             |
   1464      {9:        [string "<nvim>"]:6: in function <[string }|
   1465      {9:"<nvim>"]:2>}                                      |
   1466      {6:Press ENTER or type command to continue}^           |
   1467    ]],
   1468    }
   1469    feed('<cr>')
   1470    retry(10, nil, function()
   1471      eq({ 3, NIL }, api.nvim_get_var('yy'))
   1472    end)
   1473 
   1474    exec_lua([[timer:close()]])
   1475  end)
   1476 
   1477  it('vim.empty_dict()', function()
   1478    eq(
   1479      { true, false, true, true },
   1480      exec_lua([[
   1481      vim.api.nvim_set_var('listy', {})
   1482      vim.api.nvim_set_var('dicty', vim.empty_dict())
   1483      local listy = vim.fn.eval("listy")
   1484      local dicty = vim.fn.eval("dicty")
   1485      return {vim.islist(listy), vim.islist(dicty), next(listy) == nil, next(dicty) == nil}
   1486    ]])
   1487    )
   1488 
   1489    -- vim.empty_dict() gives new value each time
   1490    -- equality is not overridden (still by ref)
   1491    -- non-empty table uses the usual heuristics (ignores the tag)
   1492    eq(
   1493      { false, { 'foo' }, { namey = 'bar' } },
   1494      exec_lua([[
   1495      local aa = vim.empty_dict()
   1496      local bb = vim.empty_dict()
   1497      local equally = (aa == bb)
   1498      aa[1] = "foo"
   1499      bb["namey"] = "bar"
   1500      return {equally, aa, bb}
   1501    ]])
   1502    )
   1503 
   1504    eq('{ {}, vim.empty_dict() }', exec_lua('return vim.inspect({{}, vim.empty_dict()})'))
   1505    eq('{}', exec_lua([[ return vim.fn.json_encode(vim.empty_dict()) ]]))
   1506    eq('{"a": {}, "b": []}', exec_lua([[ return vim.fn.json_encode({a=vim.empty_dict(), b={}}) ]]))
   1507  end)
   1508 
   1509  it('vim.validate (fast form)', function()
   1510    exec_lua("vim.validate('arg1', {}, 'table')")
   1511    exec_lua("vim.validate('arg1', nil, 'table', true)")
   1512    exec_lua("vim.validate('arg1', { foo='foo' }, 'table')")
   1513    exec_lua("vim.validate('arg1', { 'foo' }, 'table')")
   1514    exec_lua("vim.validate('arg1', 'foo', 'string')")
   1515    exec_lua("vim.validate('arg1', nil, 'string', true)")
   1516    exec_lua("vim.validate('arg1', 1, 'number')")
   1517    exec_lua("vim.validate('arg1', 0, 'number')")
   1518    exec_lua("vim.validate('arg1', 0.1, 'number')")
   1519    exec_lua("vim.validate('arg1', nil, 'number', true)")
   1520    exec_lua("vim.validate('arg1', true, 'boolean')")
   1521    exec_lua("vim.validate('arg1', false, 'boolean')")
   1522    exec_lua("vim.validate('arg1', nil, 'boolean', true)")
   1523    exec_lua("vim.validate('arg1', function()end, 'function')")
   1524    exec_lua("vim.validate('arg1', nil, 'function', true)")
   1525    exec_lua("vim.validate('arg1', nil, 'nil')")
   1526    exec_lua("vim.validate('arg1', nil, 'nil', true)")
   1527    exec_lua("vim.validate('arg1', coroutine.create(function()end), 'thread')")
   1528    exec_lua("vim.validate('arg1', nil, 'thread', true)")
   1529    exec_lua("vim.validate('arg1', 2, function(a) return (a % 2) == 0  end, 'even number')")
   1530    exec_lua("vim.validate('arg1', 5, {'number', 'string'})")
   1531    exec_lua("vim.validate('arg2', 'foo', {'number', 'string'})")
   1532 
   1533    matches('arg1: expected number, got nil', pcall_err(vim.validate, 'arg1', nil, 'number'))
   1534    matches('arg1: expected string, got nil', pcall_err(vim.validate, 'arg1', nil, 'string'))
   1535    matches('arg1: expected table, got nil', pcall_err(vim.validate, 'arg1', nil, 'table'))
   1536    matches('arg1: expected function, got nil', pcall_err(vim.validate, 'arg1', nil, 'function'))
   1537    matches('arg1: expected string, got number', pcall_err(vim.validate, 'arg1', 5, 'string'))
   1538    matches('arg1: expected table, got number', pcall_err(vim.validate, 'arg1', 5, 'table'))
   1539    matches('arg1: expected function, got number', pcall_err(vim.validate, 'arg1', 5, 'function'))
   1540    matches('arg1: expected number, got string', pcall_err(vim.validate, 'arg1', '5', 'number'))
   1541    matches('arg1: expected x, got number', pcall_err(exec_lua, "vim.validate('arg1', 1, 'x')"))
   1542    matches('invalid validator: 1', pcall_err(exec_lua, "vim.validate('arg1', 1, 1)"))
   1543    matches('invalid arguments', pcall_err(exec_lua, "vim.validate('arg1', { 1 })"))
   1544 
   1545    -- Validated parameters are required by default.
   1546    matches(
   1547      'arg1: expected string, got nil',
   1548      pcall_err(exec_lua, "vim.validate('arg1',  nil, 'string')")
   1549    )
   1550    -- Explicitly required.
   1551    matches(
   1552      'arg1: expected string, got nil',
   1553      pcall_err(exec_lua, "vim.validate('arg1', nil, 'string', false)")
   1554    )
   1555 
   1556    matches(
   1557      'arg1: expected table, got number',
   1558      pcall_err(exec_lua, "vim.validate('arg1', 1, 'table')")
   1559    )
   1560 
   1561    matches(
   1562      'arg1: expected even number, got 3',
   1563      pcall_err(exec_lua, "vim.validate('arg1', 3, function(a) return a == 1 end, 'even number')")
   1564    )
   1565    matches(
   1566      'arg1: expected %?, got 3',
   1567      pcall_err(exec_lua, "vim.validate('arg1', 3, function(a) return a == 1 end)")
   1568    )
   1569    matches(
   1570      'arg1: expected number|string, got nil',
   1571      pcall_err(exec_lua, "vim.validate('arg1', nil, {'number', 'string'})")
   1572    )
   1573 
   1574    -- Validator func can return an extra "Info" message.
   1575    matches(
   1576      'arg1: expected %?, got 3. Info: TEST_MSG',
   1577      pcall_err(exec_lua, "vim.validate('arg1', 3, function(a) return a == 1, 'TEST_MSG' end)")
   1578    )
   1579    -- Caller can override the "expected" message.
   1580    eq(
   1581      'arg1: expected TEST_MSG, got nil',
   1582      pcall_err(exec_lua, "vim.validate('arg1', nil, 'table', 'TEST_MSG')")
   1583    )
   1584  end)
   1585 
   1586  it('vim.validate (spec form)', function()
   1587    exec_lua("vim.validate{arg1={{}, 'table' }}")
   1588    exec_lua("vim.validate{arg1={{}, 't' }}")
   1589    exec_lua("vim.validate{arg1={nil, 't', true }}")
   1590    exec_lua("vim.validate{arg1={{ foo='foo' }, 't' }}")
   1591    exec_lua("vim.validate{arg1={{ 'foo' }, 't' }}")
   1592    exec_lua("vim.validate{arg1={'foo', 'string' }}")
   1593    exec_lua("vim.validate{arg1={'foo', 's' }}")
   1594    exec_lua("vim.validate{arg1={'', 's' }}")
   1595    exec_lua("vim.validate{arg1={nil, 's', true }}")
   1596    exec_lua("vim.validate{arg1={1, 'number' }}")
   1597    exec_lua("vim.validate{arg1={1, 'n' }}")
   1598    exec_lua("vim.validate{arg1={0, 'n' }}")
   1599    exec_lua("vim.validate{arg1={0.1, 'n' }}")
   1600    exec_lua("vim.validate{arg1={nil, 'n', true }}")
   1601    exec_lua("vim.validate{arg1={true, 'boolean' }}")
   1602    exec_lua("vim.validate{arg1={true, 'b' }}")
   1603    exec_lua("vim.validate{arg1={false, 'b' }}")
   1604    exec_lua("vim.validate{arg1={nil, 'b', true }}")
   1605    exec_lua("vim.validate{arg1={function()end, 'function' }}")
   1606    exec_lua("vim.validate{arg1={function()end, 'f' }}")
   1607    exec_lua("vim.validate{arg1={nil, 'f', true }}")
   1608    exec_lua("vim.validate{arg1={nil, 'nil' }}")
   1609    exec_lua("vim.validate{arg1={nil, 'nil', true }}")
   1610    exec_lua("vim.validate{arg1={coroutine.create(function()end), 'thread' }}")
   1611    exec_lua("vim.validate{arg1={nil, 'thread', true }}")
   1612    exec_lua("vim.validate{arg1={{}, 't' }, arg2={ 'foo', 's' }}")
   1613    exec_lua("vim.validate{arg1={2, function(a) return (a % 2) == 0  end, 'even number' }}")
   1614    exec_lua("vim.validate{arg1={5, {'n', 's'} }, arg2={ 'foo', {'n', 's'} }}")
   1615 
   1616    matches('expected table, got number', pcall_err(exec_lua, "vim.validate{ 1, 'x' }"))
   1617    matches('arg1: expected x, got number', pcall_err(exec_lua, "vim.validate{ arg1={ 1, 'x' }}"))
   1618    matches('invalid validator: 1', pcall_err(exec_lua, 'vim.validate{ arg1={ 1, 1 }}'))
   1619    matches('invalid validator: nil', pcall_err(exec_lua, 'vim.validate{ arg1={ 1 }}'))
   1620 
   1621    -- Validated parameters are required by default.
   1622    matches(
   1623      'arg1: expected string, got nil',
   1624      pcall_err(exec_lua, "vim.validate{ arg1={ nil, 's' }}")
   1625    )
   1626    -- Explicitly required.
   1627    matches(
   1628      'arg1: expected string, got nil',
   1629      pcall_err(exec_lua, "vim.validate{ arg1={ nil, 's', false }}")
   1630    )
   1631 
   1632    matches('arg1: expected table, got number', pcall_err(exec_lua, "vim.validate{arg1={1, 't'}}"))
   1633    matches(
   1634      'arg2: expected string, got number',
   1635      pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={1, 's'}}")
   1636    )
   1637    matches(
   1638      'arg2: expected string, got nil',
   1639      pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={nil, 's'}}")
   1640    )
   1641    matches(
   1642      'arg2: expected string, got nil',
   1643      pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={nil, 's'}}")
   1644    )
   1645    matches(
   1646      'arg1: expected even number, got 3',
   1647      pcall_err(exec_lua, "vim.validate{arg1={3, function(a) return a == 1 end, 'even number'}}")
   1648    )
   1649    matches(
   1650      'arg1: expected %?, got 3',
   1651      pcall_err(exec_lua, 'vim.validate{arg1={3, function(a) return a == 1 end}}')
   1652    )
   1653    matches(
   1654      'arg1: expected number|string, got nil',
   1655      pcall_err(exec_lua, "vim.validate{ arg1={ nil, {'n', 's'} }}")
   1656    )
   1657 
   1658    -- Pass an additional message back.
   1659    matches(
   1660      'arg1: expected %?, got 3. Info: TEST_MSG',
   1661      pcall_err(exec_lua, "vim.validate{arg1={3, function(a) return a == 1, 'TEST_MSG' end}}")
   1662    )
   1663  end)
   1664 
   1665  it('vim.is_callable', function()
   1666    eq(true, exec_lua('return vim.is_callable(function()end)'))
   1667    eq(
   1668      true,
   1669      exec_lua([[
   1670      local meta = { __call = function()end }
   1671      local function new_callable()
   1672        return setmetatable({}, meta)
   1673      end
   1674      local callable = new_callable()
   1675      return vim.is_callable(callable)
   1676    ]])
   1677    )
   1678 
   1679    eq(
   1680      { false, false },
   1681      exec_lua([[
   1682      local meta = { __call = {} }
   1683      assert(meta.__call)
   1684      local function new()
   1685        return setmetatable({}, meta)
   1686      end
   1687      local not_callable = new()
   1688      return { pcall(function() not_callable() end), vim.is_callable(not_callable) }
   1689    ]])
   1690    )
   1691    eq(
   1692      { false, false },
   1693      exec_lua([[
   1694      local function new()
   1695        return { __call = function()end }
   1696      end
   1697      local not_callable = new()
   1698      assert(not_callable.__call)
   1699      return { pcall(function() not_callable() end), vim.is_callable(not_callable) }
   1700    ]])
   1701    )
   1702    eq(
   1703      { false, false },
   1704      exec_lua([[
   1705      local meta = setmetatable(
   1706        { __index = { __call = function() end } },
   1707        { __index = { __call = function() end } }
   1708      )
   1709      assert(meta.__call)
   1710      local not_callable = setmetatable({}, meta)
   1711      assert(not_callable.__call)
   1712      return { pcall(function() not_callable() end), vim.is_callable(not_callable) }
   1713    ]])
   1714    )
   1715    eq(
   1716      { false, false },
   1717      exec_lua([[
   1718      local meta = setmetatable({
   1719        __index = function()
   1720          return function() end
   1721        end,
   1722      }, {
   1723        __index = function()
   1724          return function() end
   1725        end,
   1726      })
   1727      assert(meta.__call)
   1728      local not_callable = setmetatable({}, meta)
   1729      assert(not_callable.__call)
   1730      return { pcall(function() not_callable() end), vim.is_callable(not_callable) }
   1731    ]])
   1732    )
   1733    eq(false, exec_lua('return vim.is_callable(1)'))
   1734    eq(false, exec_lua("return vim.is_callable('foo')"))
   1735    eq(false, exec_lua('return vim.is_callable({})'))
   1736  end)
   1737 
   1738  it('vim.cmd', function()
   1739    exec_lua [[
   1740    vim.cmd "autocmd BufNew * ++once lua BUF = vim.fn.expand('<abuf>')"
   1741    vim.cmd "new"
   1742    ]]
   1743    eq('2', fn.luaeval 'BUF')
   1744    eq(2, fn.luaeval '#vim.api.nvim_list_bufs()')
   1745 
   1746    -- vim.cmd can be indexed with a command name
   1747    exec_lua [[
   1748      vim.cmd.let 'g:var = 2'
   1749    ]]
   1750 
   1751    eq(2, fn.luaeval 'vim.g.var')
   1752  end)
   1753 
   1754  it('vim.regex', function()
   1755    exec_lua [[
   1756      re1 = vim.regex"ab\\+c"
   1757      vim.cmd "set nomagic ignorecase"
   1758      re2 = vim.regex"xYz"
   1759    ]]
   1760    eq({}, exec_lua [[return {re1:match_str("x ac")}]])
   1761    eq({ 3, 7 }, exec_lua [[return {re1:match_str("ac abbc")}]])
   1762 
   1763    api.nvim_buf_set_lines(0, 0, -1, true, { 'yy', 'abc abbc' })
   1764    eq({}, exec_lua [[return {re1:match_line(0, 0)}]])
   1765    eq({ 0, 3 }, exec_lua [[return {re1:match_line(0, 1)}]])
   1766    eq({ 3, 7 }, exec_lua [[return {re1:match_line(0, 1, 1)}]])
   1767    eq({ 3, 7 }, exec_lua [[return {re1:match_line(0, 1, 1, 8)}]])
   1768    eq({}, exec_lua [[return {re1:match_line(0, 1, 1, 7)}]])
   1769    eq({ 0, 3 }, exec_lua [[return {re1:match_line(0, 1, 0, 7)}]])
   1770 
   1771    -- vim.regex() error inside :silent! should not crash. #20546
   1772    command([[silent! lua vim.regex('\\z')]])
   1773    assert_alive()
   1774  end)
   1775 
   1776  it('vim.defer_fn', function()
   1777    eq(
   1778      false,
   1779      exec_lua [[
   1780      vim.g.test = false
   1781      vim.defer_fn(function() vim.g.test = true end, 150)
   1782      return vim.g.test
   1783    ]]
   1784    )
   1785    exec_lua [[vim.wait(1000, function() return vim.g.test end)]]
   1786    eq(true, exec_lua [[return vim.g.test]])
   1787  end)
   1788 
   1789  it('nested vim.defer_fn does not leak handles on exit #19727', function()
   1790    n.expect_exit(exec_lua, function()
   1791      vim.defer_fn(function()
   1792        vim.defer_fn(function()
   1793          vim.defer_fn(function() end, 0)
   1794        end, 0)
   1795      end, 0)
   1796      vim.cmd('qall')
   1797    end)
   1798  end)
   1799 
   1800  it('vim.defer_fn with timeout does not leak handles on exit', function()
   1801    n.expect_exit(exec_lua, function()
   1802      vim.defer_fn(function() end, 50)
   1803      vim.cmd('qall')
   1804    end)
   1805  end)
   1806 
   1807  describe('vim.region', function()
   1808    it('charwise', function()
   1809      insert(dedent([[
   1810      text tααt tααt text
   1811      text tαxt txtα tex
   1812      text tαxt tαxt
   1813      ]]))
   1814      eq({ 5, 13 }, exec_lua [[ return vim.region(0,{0,5},{0,13},'v',false)[0] ]])
   1815      eq({ 5, 15 }, exec_lua [[ return vim.region(0,{0,5},{0,13},'v',true)[0] ]])
   1816      eq({ 5, 15 }, exec_lua [[ return vim.region(0,{0,5},{0,14},'v',true)[0] ]])
   1817      eq({ 5, 15 }, exec_lua [[ return vim.region(0,{0,5},{0,15},'v',false)[0] ]])
   1818      eq({ 5, 17 }, exec_lua [[ return vim.region(0,{0,5},{0,15},'v',true)[0] ]])
   1819      eq({ 5, 17 }, exec_lua [[ return vim.region(0,{0,5},{0,16},'v',true)[0] ]])
   1820      eq({ 5, 17 }, exec_lua [[ return vim.region(0,{0,5},{0,17},'v',false)[0] ]])
   1821      eq({ 5, 18 }, exec_lua [[ return vim.region(0,{0,5},{0,17},'v',true)[0] ]])
   1822    end)
   1823    it('blockwise', function()
   1824      insert([[αα]])
   1825      eq({ 0, 5 }, exec_lua [[ return vim.region(0,{0,0},{0,4},'3',true)[0] ]])
   1826    end)
   1827    it('linewise', function()
   1828      insert(dedent([[
   1829      text tααt tααt text
   1830      text tαxt txtα tex
   1831      text tαxt tαxt
   1832      ]]))
   1833      eq({ 0, -1 }, exec_lua [[ return vim.region(0,{1,5},{1,14},'V',true)[1] ]])
   1834    end)
   1835    it('getpos() input', function()
   1836      insert('getpos')
   1837      eq({ 0, 6 }, exec_lua [[ return vim.region(0,{0,0},'.','v',true)[0] ]])
   1838    end)
   1839  end)
   1840 
   1841  describe('vim.on_key', function()
   1842    it('tracks Unicode input', function()
   1843      insert([[hello world ]])
   1844 
   1845      exec_lua [[
   1846        keys = {}
   1847        typed = {}
   1848 
   1849        vim.on_key(function(buf, typed_buf)
   1850          if buf:byte() == 27 then
   1851            buf = "<ESC>"
   1852          end
   1853          if typed_buf:byte() == 27 then
   1854            typed_buf = "<ESC>"
   1855          end
   1856 
   1857          table.insert(keys, buf)
   1858          table.insert(typed, typed_buf)
   1859        end)
   1860      ]]
   1861 
   1862      insert([[next 🤦 lines å ]])
   1863 
   1864      -- It has escape in the keys pressed
   1865      eq('inext 🤦 lines å …<ESC>', exec_lua [[return table.concat(keys, '')]])
   1866      eq('inext 🤦 lines å …<ESC>', exec_lua [[return table.concat(typed, '')]])
   1867    end)
   1868 
   1869    it('tracks input with modifiers', function()
   1870      exec_lua [[
   1871        keys = {}
   1872        typed = {}
   1873 
   1874        vim.on_key(function(buf, typed_buf)
   1875          table.insert(keys, vim.fn.keytrans(buf))
   1876          table.insert(typed, vim.fn.keytrans(typed_buf))
   1877        end)
   1878      ]]
   1879 
   1880      feed([[i<C-V><C-;><C-V><C-><Esc>]])
   1881 
   1882      eq('i<C-V><C-;><C-V><C-…><Esc>', exec_lua [[return table.concat(keys, '')]])
   1883      eq('i<C-V><C-;><C-V><C-…><Esc>', exec_lua [[return table.concat(typed, '')]])
   1884    end)
   1885 
   1886    it('works with character find and Select mode', function()
   1887      insert('12345')
   1888 
   1889      exec_lua [[
   1890        typed = {}
   1891 
   1892        vim.cmd('snoremap # @')
   1893 
   1894        vim.on_key(function(buf, typed_buf)
   1895          table.insert(typed, vim.fn.keytrans(typed_buf))
   1896        end)
   1897      ]]
   1898 
   1899      feed('F3gHβγδεζ<Esc>gH…<Esc>gH#$%^')
   1900      eq('F3gHβγδεζ<Esc>gH…<Esc>gH#$%^', exec_lua [[return table.concat(typed, '')]])
   1901    end)
   1902 
   1903    it('allows removing on_key listeners', function()
   1904      -- Create some unused namespaces
   1905      api.nvim_create_namespace('unused1')
   1906      api.nvim_create_namespace('unused2')
   1907      api.nvim_create_namespace('unused3')
   1908      api.nvim_create_namespace('unused4')
   1909 
   1910      insert([[hello world]])
   1911 
   1912      exec_lua [[
   1913        keys = {}
   1914 
   1915        return vim.on_key(function(buf)
   1916          if buf:byte() == 27 then
   1917            buf = "<ESC>"
   1918          end
   1919 
   1920          table.insert(keys, buf)
   1921        end, vim.api.nvim_create_namespace("logger"))
   1922      ]]
   1923 
   1924      insert([[next lines]])
   1925 
   1926      eq(1, exec_lua('return vim.on_key()'))
   1927      exec_lua("vim.on_key(nil, vim.api.nvim_create_namespace('logger'))")
   1928      eq(0, exec_lua('return vim.on_key()'))
   1929 
   1930      insert([[more lines]])
   1931 
   1932      -- It has escape in the keys pressed
   1933      eq('inext lines<ESC>', exec_lua [[return table.concat(keys, '')]])
   1934    end)
   1935 
   1936    it('skips any function that caused an error and shows stacktrace', function()
   1937      insert([[hello world]])
   1938 
   1939      exec_lua [[
   1940        local function ErrF2()
   1941          error("Dumb Error")
   1942        end
   1943        local function ErrF1()
   1944          ErrF2()
   1945        end
   1946 
   1947        keys = {}
   1948 
   1949        return vim.on_key(function(buf)
   1950          if buf:byte() == 27 then
   1951            buf = "<ESC>"
   1952          end
   1953 
   1954          table.insert(keys, buf)
   1955 
   1956          if buf == 'l' then
   1957            ErrF1()
   1958          end
   1959        end)
   1960      ]]
   1961 
   1962      insert([[next lines]])
   1963      insert([[more lines]])
   1964 
   1965      -- Only the first letter gets added. After that we remove the callback
   1966      eq('inext l', exec_lua [[ return table.concat(keys, '') ]])
   1967 
   1968      local errmsg = api.nvim_get_vvar('errmsg')
   1969      matches(
   1970        [[
   1971 ^vim%.on%_key%(%) callbacks:.*
   1972 With ns%_id %d+: .*: Dumb Error
   1973 stack traceback:
   1974 .*: in function 'error'
   1975 .*: in function 'ErrF2'
   1976 .*: in function 'ErrF1'
   1977 .*]],
   1978        errmsg
   1979      )
   1980    end)
   1981 
   1982    it('argument 1 is keys after mapping, argument 2 is typed keys', function()
   1983      exec_lua [[
   1984        keys = {}
   1985        typed = {}
   1986 
   1987        vim.cmd("inoremap hello world")
   1988 
   1989        vim.on_key(function(buf, typed_buf)
   1990          if buf:byte() == 27 then
   1991            buf = "<ESC>"
   1992          end
   1993          if typed_buf:byte() == 27 then
   1994            typed_buf = "<ESC>"
   1995          end
   1996 
   1997          table.insert(keys, buf)
   1998          table.insert(typed, typed_buf)
   1999        end)
   2000      ]]
   2001      insert('hello')
   2002 
   2003      eq('iworld<ESC>', exec_lua [[return table.concat(keys, '')]])
   2004      eq('ihello<ESC>', exec_lua [[return table.concat(typed, '')]])
   2005    end)
   2006 
   2007    it('can call vim.fn functions on Ctrl-C #17273', function()
   2008      exec_lua([[
   2009        _G.ctrl_c_cmdtype = ''
   2010 
   2011        vim.on_key(function(c)
   2012          if c == '\3' then
   2013            _G.ctrl_c_cmdtype = vim.fn.getcmdtype()
   2014          end
   2015        end)
   2016      ]])
   2017      feed('/')
   2018      poke_eventloop() -- This is needed because Ctrl-C flushes input
   2019      feed('<C-C>')
   2020      eq('/', exec_lua([[return _G.ctrl_c_cmdtype]]))
   2021    end)
   2022 
   2023    it('callback is not invoked recursively #30752', function()
   2024      local screen = Screen.new(60, 10)
   2025      exec_lua([[
   2026        vim.on_key(function(key, typed)
   2027          vim.api.nvim_echo({
   2028            { 'key_cb\n' },
   2029            { ("KEYCB: key '%s', typed '%s'\n"):format(key, typed) },
   2030          }, false, {})
   2031        end)
   2032      ]])
   2033      feed('^')
   2034      screen:expect([[
   2035                                                                    |
   2036        {1:~                                                           }|*5
   2037        {3:                                                            }|
   2038        key_cb                                                      |
   2039        KEYCB: key '^', typed '^'                                   |
   2040        {6:Press ENTER or type command to continue}^                     |
   2041      ]])
   2042      feed('<C-C>')
   2043      screen:expect([[
   2044                                                                    |
   2045        {1:~                                                           }|*3
   2046        {3:                                                            }|
   2047        key_cb                                                      |
   2048        KEYCB: key '^', typed '^'                                   |
   2049        key_cb                                                      |
   2050        KEYCB: key '{18:^C}', typed '{18:^C}'                                 |
   2051        {6:Press ENTER or type command to continue}^                     |
   2052      ]])
   2053      feed('<C-C>')
   2054      screen:expect([[
   2055        ^                                                            |
   2056        {1:~                                                           }|*8
   2057                                                                    |
   2058      ]])
   2059    end)
   2060 
   2061    it('can discard input', function()
   2062      -- discard the first key produced by every other 'x' key typed
   2063      exec_lua [[
   2064        n_key = 0
   2065 
   2066        vim.on_key(function(buf, typed_buf)
   2067          if typed_buf == 'x' then
   2068            n_key = n_key + 1
   2069            return (n_key % 2 == 0) and '' or nil
   2070          end
   2071        end)
   2072      ]]
   2073 
   2074      api.nvim_buf_set_lines(0, 0, -1, true, { '54321' })
   2075 
   2076      feed('x') -- 'x' not discarded
   2077      expect('4321')
   2078      feed('x') -- 'x' discarded
   2079      expect('4321')
   2080      feed('x') -- 'x' not discarded
   2081      expect('321')
   2082      feed('x') -- 'x' discarded
   2083      expect('321')
   2084 
   2085      api.nvim_buf_set_lines(0, 0, -1, true, { '54321' })
   2086 
   2087      -- only the first key from the mapping is discarded
   2088      command('nnoremap x $x')
   2089      feed('0x') -- '$' not discarded
   2090      expect('5432')
   2091      feed('0x') -- '$' discarded
   2092      expect('432')
   2093      feed('0x') -- '$' not discarded
   2094      expect('43')
   2095      feed('0x') -- '$' discarded
   2096      expect('3')
   2097 
   2098      feed('i')
   2099      -- when discarding <Cmd>, the following command is also discarded.
   2100      command([[inoremap x <Cmd>call append('$', 'foo')<CR>]])
   2101      feed('x') -- not discarded
   2102      expect('3\nfoo')
   2103      feed('x') -- discarded
   2104      expect('3\nfoo')
   2105      feed('x') -- not discarded
   2106      expect('3\nfoo\nfoo')
   2107      feed('x') -- discarded
   2108      expect('3\nfoo\nfoo')
   2109 
   2110      -- K_LUA is handled similarly to <Cmd>
   2111      exec_lua([[vim.keymap.set('i', 'x', function() vim.fn.append('$', 'bar') end)]])
   2112      feed('x') -- not discarded
   2113      expect('3\nfoo\nfoo\nbar')
   2114      feed('x') -- discarded
   2115      expect('3\nfoo\nfoo\nbar')
   2116      feed('x') -- not discarded
   2117      expect('3\nfoo\nfoo\nbar\nbar')
   2118      feed('x') -- discarded
   2119      expect('3\nfoo\nfoo\nbar\nbar')
   2120    end)
   2121 
   2122    it('behaves consistently with <Cmd>, K_LUA, nvim_paste', function()
   2123      exec_lua([[
   2124        vim.keymap.set('i', '<F2>', "<Cmd>call append('$', 'FOO')<CR>")
   2125        vim.keymap.set('i', '<F3>', function() vim.fn.append('$', 'BAR') end)
   2126      ]])
   2127 
   2128      feed('qrafoo<F2><F3>')
   2129      api.nvim_paste('bar', false, -1)
   2130      feed('<Esc>q')
   2131      expect('foobar\nFOO\nBAR')
   2132 
   2133      exec_lua([[
   2134        keys = {}
   2135        typed = {}
   2136 
   2137        vim.on_key(function(buf, typed_buf)
   2138          table.insert(keys, buf)
   2139          table.insert(typed, typed_buf)
   2140        end)
   2141      ]])
   2142 
   2143      feed('@r')
   2144      local keys = exec_lua('return keys')
   2145      eq('@r', exec_lua([[return table.concat(typed, '')]]))
   2146      expect('foobarfoobar\nFOO\nBAR\nFOO\nBAR')
   2147 
   2148      -- Add a new callback that discards most special keys as well as 'f'.
   2149      -- The old callback is still active.
   2150      exec_lua([[
   2151        vim.on_key(function(buf, _)
   2152          if not buf:find('^[@rao\27]$') then
   2153            return ''
   2154          end
   2155        end)
   2156 
   2157        keys = {}
   2158        typed = {}
   2159      ]])
   2160 
   2161      feed('@r')
   2162      eq(keys, exec_lua('return keys'))
   2163      eq('@r', exec_lua([[return table.concat(typed, '')]]))
   2164      -- The "bar" paste is discarded as a whole.
   2165      expect('foobarfoobaroo\nFOO\nBAR\nFOO\nBAR')
   2166    end)
   2167 
   2168    it('callback invalid return', function()
   2169      -- second key produces an error which removes the callback
   2170      exec_lua [[
   2171        n_call = 0
   2172 
   2173        vim.on_key(function(buf, typed_buf)
   2174          if typed_buf == 'x' then
   2175            n_call = n_call + 1
   2176          end
   2177          return n_call >= 2 and '!' or nil
   2178        end)
   2179      ]]
   2180 
   2181      api.nvim_buf_set_lines(0, 0, -1, true, { '54321' })
   2182 
   2183      feed('x')
   2184      eq(1, exec_lua [[ return n_call ]])
   2185      eq(1, exec_lua [[ return vim.on_key(nil, nil) ]])
   2186      eq('', eval('v:errmsg'))
   2187      feed('x')
   2188      eq(2, exec_lua [[ return n_call ]])
   2189      matches('return string must be empty', eval('v:errmsg'))
   2190      command('let v:errmsg = ""')
   2191 
   2192      eq(0, exec_lua [[ return vim.on_key(nil, nil) ]])
   2193 
   2194      feed('x')
   2195      eq(2, exec_lua [[ return n_call ]])
   2196      expect('21')
   2197      eq('', eval('v:errmsg'))
   2198    end)
   2199  end)
   2200 
   2201  describe('vim.wait', function()
   2202    before_each(function()
   2203      exec_lua [[
   2204        -- high precision timer
   2205        get_time = function()
   2206          return vim.fn.reltimefloat(vim.fn.reltime())
   2207        end
   2208      ]]
   2209    end)
   2210 
   2211    it('runs from lua', function()
   2212      exec_lua [[vim.wait(100, function() return true end)]]
   2213    end)
   2214 
   2215    it('returns all (multiple) callback results', function()
   2216      eq({ true, false }, exec_lua [[return { vim.wait(200, function() return true, false end) }]])
   2217      eq(
   2218        { true, 'a', 42, { ok = { 'yes' } } },
   2219        exec_lua [[
   2220          local ok, rv1, rv2, rv3 = vim.wait(200, function()
   2221            return true, 'a', 42, { ok = { 'yes' } }
   2222          end)
   2223 
   2224          return { ok, rv1, rv2, rv3 }
   2225        ]]
   2226      )
   2227    end)
   2228 
   2229    it('does not return callback results on timeout', function()
   2230      eq(
   2231        { false, -1 },
   2232        exec_lua [[
   2233          return { vim.wait(1, function()
   2234            return false, 'a', 42, { ok = { 'yes' } }
   2235          end) }
   2236        ]]
   2237      )
   2238    end)
   2239 
   2240    it('waits the expected time if false', function()
   2241      eq(
   2242        { time = true, wait_result = { false, -1 } },
   2243        exec_lua [[
   2244        start_time = get_time()
   2245        wait_succeed, wait_fail_val = vim.wait(200, function() return false end)
   2246 
   2247        return {
   2248          -- 150ms waiting or more results in true. Flaky tests are bad.
   2249          time = (start_time + 0.15) < get_time(),
   2250          wait_result = {wait_succeed, wait_fail_val}
   2251        }
   2252      ]]
   2253      )
   2254    end)
   2255 
   2256    it('does not block other events', function()
   2257      eq(
   2258        { time = true, wait_result = true },
   2259        exec_lua [[
   2260        start_time = get_time()
   2261 
   2262        vim.g.timer_result = false
   2263        timer = vim.uv.new_timer()
   2264        timer:start(100, 0, vim.schedule_wrap(function()
   2265          vim.g.timer_result = true
   2266        end))
   2267 
   2268        -- Would wait ten seconds if results blocked.
   2269        wait_result = vim.wait(10000, function() return vim.g.timer_result end)
   2270 
   2271        timer:close()
   2272 
   2273        return {
   2274          time = (start_time + 5) > get_time(),
   2275          wait_result = wait_result,
   2276        }
   2277      ]]
   2278      )
   2279    end)
   2280 
   2281    it('does not process non-fast events when commanded', function()
   2282      eq(
   2283        { wait_result = false },
   2284        exec_lua [[
   2285        start_time = get_time()
   2286 
   2287        vim.g.timer_result = false
   2288        timer = vim.uv.new_timer()
   2289        timer:start(100, 0, vim.schedule_wrap(function()
   2290          vim.g.timer_result = true
   2291        end))
   2292 
   2293        wait_result = vim.wait(300, function() return vim.g.timer_result end, nil, true)
   2294 
   2295        timer:close()
   2296 
   2297        return {
   2298          wait_result = wait_result,
   2299        }
   2300      ]]
   2301      )
   2302    end)
   2303 
   2304    it('works with vim.defer_fn', function()
   2305      eq(
   2306        { time = true, wait_result = true },
   2307        exec_lua [[
   2308        start_time = get_time()
   2309 
   2310        vim.defer_fn(function() vim.g.timer_result = true end, 100)
   2311        wait_result = vim.wait(10000, function() return vim.g.timer_result end)
   2312 
   2313        return {
   2314          time = (start_time + 5) > get_time(),
   2315          wait_result = wait_result,
   2316        }
   2317      ]]
   2318      )
   2319    end)
   2320 
   2321    it('does not crash when callback errors', function()
   2322      local result = exec_lua [[
   2323        return {pcall(function() vim.wait(1000, function() error("As Expected") end) end)}
   2324      ]]
   2325      eq({ false, '[string "<nvim>"]:1: As Expected' }, { result[1], remove_trace(result[2]) })
   2326    end)
   2327 
   2328    it('callback must be a function', function()
   2329      eq(
   2330        { false, 'vim.wait: callback must be callable' },
   2331        exec_lua [[return {pcall(function() vim.wait(1000, 13) end)}]]
   2332      )
   2333    end)
   2334 
   2335    it('waits if callback arg is nil', function()
   2336      eq(
   2337        true,
   2338        exec_lua [[
   2339        local start_time = vim.uv.hrtime()
   2340        vim.wait(50.1, nil) -- select('#', ...) == 1
   2341        return vim.uv.hrtime() - start_time > 25000
   2342      ]]
   2343      )
   2344    end)
   2345 
   2346    it('waits if callback arg is omitted', function()
   2347      eq(
   2348        true,
   2349        exec_lua [[
   2350        local start_time = vim.uv.hrtime()
   2351        vim.wait(50) -- select('#', ...) == 0
   2352        return vim.uv.hrtime() - start_time > 25000
   2353      ]]
   2354      )
   2355    end)
   2356 
   2357    it('invokes callback exactly once if it returns true immediately', function()
   2358      eq(
   2359        true,
   2360        exec_lua [[
   2361        vim.g.wait_count = 0
   2362        vim.wait(1000, function()
   2363          vim.g.wait_count = vim.g.wait_count + 1
   2364          return true
   2365        end, 20)
   2366        return vim.g.wait_count == 1
   2367      ]]
   2368      )
   2369    end)
   2370 
   2371    it('calls callbacks few times with large `interval`', function()
   2372      eq(
   2373        true,
   2374        exec_lua [[
   2375        vim.g.wait_count = 0
   2376        vim.wait(50, function() vim.g.wait_count = vim.g.wait_count + 1 end, 200)
   2377        return vim.g.wait_count < 5
   2378      ]]
   2379      )
   2380    end)
   2381 
   2382    it('does not leak when Nvim exits while waiting', function()
   2383      n.expect_exit(500, exec_lua, function()
   2384        vim.defer_fn(function()
   2385          vim.cmd('qall!')
   2386        end, 10)
   2387        vim.wait(10000)
   2388      end)
   2389    end)
   2390 
   2391    it('plays nice with `not` when fails', function()
   2392      eq(
   2393        true,
   2394        exec_lua [[
   2395        if not vim.wait(50, function() end) then
   2396          return true
   2397        end
   2398 
   2399        return false
   2400      ]]
   2401      )
   2402    end)
   2403 
   2404    it('plays nice with `if` when success', function()
   2405      eq(
   2406        true,
   2407        exec_lua [[
   2408        if vim.wait(50, function() return true end) then
   2409          return true
   2410        end
   2411 
   2412        return false
   2413      ]]
   2414      )
   2415    end)
   2416 
   2417    it('returns immediately with false if timeout is 0', function()
   2418      eq(
   2419        { false, -1 },
   2420        exec_lua [[
   2421        return {
   2422          vim.wait(0, function() return false end)
   2423        }
   2424      ]]
   2425      )
   2426    end)
   2427 
   2428    it('works with tables with __call', function()
   2429      eq(
   2430        true,
   2431        exec_lua [[
   2432        local t = setmetatable({}, {__call = function(...) return true end})
   2433        return vim.wait(100, t, 10)
   2434      ]]
   2435      )
   2436    end)
   2437 
   2438    it('works with tables with __call that change', function()
   2439      eq(
   2440        true,
   2441        exec_lua [[
   2442        local t = {count = 0}
   2443        setmetatable(t, {
   2444          __call = function()
   2445            t.count = t.count + 1
   2446            return t.count > 3
   2447          end
   2448        })
   2449 
   2450        return vim.wait(1000, t, 10)
   2451      ]]
   2452      )
   2453    end)
   2454 
   2455    it('fails with negative intervals', function()
   2456      local pcall_result = exec_lua [[
   2457        return pcall(function() vim.wait(1000, function() return false end, -1) end)
   2458      ]]
   2459 
   2460      eq(false, pcall_result)
   2461    end)
   2462 
   2463    it('fails with weird intervals', function()
   2464      local pcall_result = exec_lua [[
   2465        return pcall(function() vim.wait(1000, function() return false end, 'a string value') end)
   2466      ]]
   2467 
   2468      eq(false, pcall_result)
   2469    end)
   2470 
   2471    describe('returns -2 when interrupted', function()
   2472      before_each(function()
   2473        local channel = api.nvim_get_chan_info(0).id
   2474        api.nvim_set_var('channel', channel)
   2475      end)
   2476 
   2477      it('without callback', function()
   2478        exec_lua([[
   2479          function _G.Wait()
   2480            vim.rpcnotify(vim.g.channel, 'ready')
   2481            -- handles math.huge #36854
   2482            local _, interrupted = vim.wait(math.huge)
   2483            vim.rpcnotify(vim.g.channel, 'wait', interrupted)
   2484          end
   2485        ]])
   2486        feed(':lua _G.Wait()<CR>')
   2487        eq({ 'notification', 'ready', {} }, next_msg(500))
   2488        feed('<C-C>')
   2489        eq({ 'notification', 'wait', { -2 } }, next_msg(500))
   2490      end)
   2491 
   2492      it('with callback', function()
   2493        exec_lua([[
   2494          function _G.Wait()
   2495            vim.rpcnotify(vim.g.channel, 'ready')
   2496            local _, interrupted = vim.wait(math.huge, function() end)
   2497            vim.rpcnotify(vim.g.channel, 'wait', interrupted)
   2498          end
   2499        ]])
   2500        feed(':lua _G.Wait()<CR>')
   2501        eq({ 'notification', 'ready', {} }, next_msg(500))
   2502        feed('<C-C>')
   2503        eq({ 'notification', 'wait', { -2 } }, next_msg(500))
   2504      end)
   2505    end)
   2506 
   2507    it('fails in fast callbacks #26122', function()
   2508      local screen = Screen.new(80, 10)
   2509      exec_lua([[
   2510        local timer = vim.uv.new_timer()
   2511        timer:start(0, 0, function()
   2512          timer:close()
   2513          vim.wait(100, function() end)
   2514        end)
   2515      ]])
   2516      screen:expect({
   2517        any = pesc('E5560: vim.wait must not be called in a fast event context'),
   2518      })
   2519      feed('<CR>')
   2520      assert_alive()
   2521    end)
   2522  end)
   2523 
   2524  it('vim.notify_once', function()
   2525    local screen = Screen.new(60, 5)
   2526    screen:expect {
   2527      grid = [[
   2528      ^                                                            |
   2529      {1:~                                                           }|*3
   2530                                                                  |
   2531    ]],
   2532    }
   2533    exec_lua [[vim.notify_once("I'll only tell you this once...", vim.log.levels.WARN)]]
   2534    screen:expect {
   2535      grid = [[
   2536      ^                                                            |
   2537      {1:~                                                           }|*3
   2538      {19:I'll only tell you this once...}                             |
   2539    ]],
   2540    }
   2541    feed('<C-l>')
   2542    screen:expect {
   2543      grid = [[
   2544      ^                                                            |
   2545      {1:~                                                           }|*3
   2546                                                                  |
   2547    ]],
   2548    }
   2549    exec_lua [[vim.notify_once("I'll only tell you this once...")]]
   2550    screen:expect_unchanged()
   2551  end)
   2552 
   2553  describe('vim.schedule_wrap', function()
   2554    it('preserves argument lists', function()
   2555      exec_lua [[
   2556        local fun = vim.schedule_wrap(function(kling, klang, klonk)
   2557          vim.rpcnotify(1, 'mayday_mayday', {a=kling, b=klang, c=klonk})
   2558        end)
   2559        fun("BOB", nil, "MIKE")
   2560      ]]
   2561      eq({ 'notification', 'mayday_mayday', { { a = 'BOB', c = 'MIKE' } } }, next_msg())
   2562 
   2563      -- let's gooooo
   2564      exec_lua [[
   2565        vim.schedule_wrap(function(...) vim.rpcnotify(1, 'boogalo', select('#', ...)) end)(nil,nil,nil,nil)
   2566      ]]
   2567      eq({ 'notification', 'boogalo', { 4 } }, next_msg())
   2568    end)
   2569  end)
   2570 
   2571  describe('vim.api.nvim_buf_call', function()
   2572    it('can access buf options', function()
   2573      local buf1 = api.nvim_get_current_buf()
   2574      local buf2 = exec_lua [[
   2575        buf2 = vim.api.nvim_create_buf(false, true)
   2576        return buf2
   2577      ]]
   2578 
   2579      eq(false, api.nvim_get_option_value('autoindent', { buf = buf1 }))
   2580      eq(false, api.nvim_get_option_value('autoindent', { buf = buf2 }))
   2581 
   2582      local val = exec_lua [[
   2583        return vim.api.nvim_buf_call(buf2, function()
   2584          vim.cmd "set autoindent"
   2585          return vim.api.nvim_get_current_buf()
   2586        end)
   2587      ]]
   2588 
   2589      eq(false, api.nvim_get_option_value('autoindent', { buf = buf1 }))
   2590      eq(true, api.nvim_get_option_value('autoindent', { buf = buf2 }))
   2591      eq(buf1, api.nvim_get_current_buf())
   2592      eq(buf2, val)
   2593    end)
   2594 
   2595    it('does not cause ml_get errors with invalid visual selection', function()
   2596      -- Should be fixed by vim-patch:8.2.4028.
   2597      exec_lua [[
   2598        local api = vim.api
   2599        local t = function(s) return api.nvim_replace_termcodes(s, true, true, true) end
   2600        api.nvim_buf_set_lines(0, 0, -1, true, {"a", "b", "c"})
   2601        api.nvim_feedkeys(t "G<C-V>", "txn", false)
   2602        api.nvim_buf_call(api.nvim_create_buf(false, true), function() vim.cmd "redraw" end)
   2603      ]]
   2604    end)
   2605 
   2606    it('can be nested crazily with hidden buffers', function()
   2607      eq(
   2608        true,
   2609        exec_lua([[
   2610        local function scratch_buf_call(fn)
   2611          local buf = vim.api.nvim_create_buf(false, true)
   2612          vim.api.nvim_set_option_value('cindent', true, {buf = buf})
   2613          return vim.api.nvim_buf_call(buf, function()
   2614            return vim.api.nvim_get_current_buf() == buf
   2615              and vim.api.nvim_get_option_value('cindent', {buf = buf})
   2616              and fn()
   2617          end) and vim.api.nvim_buf_delete(buf, {}) == nil
   2618        end
   2619 
   2620        return scratch_buf_call(function()
   2621          return scratch_buf_call(function()
   2622            return scratch_buf_call(function()
   2623              return scratch_buf_call(function()
   2624                return scratch_buf_call(function()
   2625                  return scratch_buf_call(function()
   2626                    return scratch_buf_call(function()
   2627                      return scratch_buf_call(function()
   2628                        return scratch_buf_call(function()
   2629                          return scratch_buf_call(function()
   2630                            return scratch_buf_call(function()
   2631                              return scratch_buf_call(function()
   2632                                return true
   2633                              end)
   2634                            end)
   2635                          end)
   2636                        end)
   2637                      end)
   2638                    end)
   2639                  end)
   2640                end)
   2641              end)
   2642            end)
   2643          end)
   2644        end)
   2645      ]])
   2646      )
   2647    end)
   2648 
   2649    it('can return values by reference', function()
   2650      eq(
   2651        { 4, 7 },
   2652        exec_lua [[
   2653        local val = {4, 10}
   2654        local ref = vim.api.nvim_buf_call(0, function() return val end)
   2655        ref[2] = 7
   2656        return val
   2657      ]]
   2658      )
   2659    end)
   2660 
   2661    it('can get Visual selection in current buffer #34162', function()
   2662      insert('foo bar baz')
   2663      feed('gg0fbvtb')
   2664      local text = exec_lua([[
   2665        return vim.api.nvim_buf_call(0, function()
   2666          return vim.fn.getregion(vim.fn.getpos('.'), vim.fn.getpos('v'))
   2667        end)
   2668      ]])
   2669      eq({ 'bar ' }, text)
   2670    end)
   2671  end)
   2672 
   2673  describe('vim.api.nvim_win_call', function()
   2674    it('can access window options', function()
   2675      command('vsplit')
   2676      local win1 = api.nvim_get_current_win()
   2677      command('wincmd w')
   2678      local win2 = exec_lua [[
   2679        win2 = vim.api.nvim_get_current_win()
   2680        return win2
   2681      ]]
   2682      command('wincmd p')
   2683 
   2684      eq('', api.nvim_get_option_value('winhighlight', { win = win1 }))
   2685      eq('', api.nvim_get_option_value('winhighlight', { win = win2 }))
   2686 
   2687      local val = exec_lua [[
   2688        return vim.api.nvim_win_call(win2, function()
   2689          vim.cmd "setlocal winhighlight=Normal:Normal"
   2690          return vim.api.nvim_get_current_win()
   2691        end)
   2692      ]]
   2693 
   2694      eq('', api.nvim_get_option_value('winhighlight', { win = win1 }))
   2695      eq('Normal:Normal', api.nvim_get_option_value('winhighlight', { win = win2 }))
   2696      eq(win1, api.nvim_get_current_win())
   2697      eq(win2, val)
   2698    end)
   2699 
   2700    it('failure modes', function()
   2701      matches(
   2702        'nvim_exec2%(%), line 1: Vim:E492: Not an editor command: fooooo',
   2703        pcall_err(exec_lua, [[vim.api.nvim_win_call(0, function() vim.cmd 'fooooo' end)]])
   2704      )
   2705      eq(
   2706        'Lua: [string "<nvim>"]:0: fooooo',
   2707        pcall_err(exec_lua, [[vim.api.nvim_win_call(0, function() error('fooooo') end)]])
   2708      )
   2709    end)
   2710 
   2711    it('does not cause ml_get errors with invalid visual selection', function()
   2712      -- Add lines to the current buffer and make another window looking into an empty buffer.
   2713      exec_lua [[
   2714        _G.api = vim.api
   2715        _G.t = function(s) return api.nvim_replace_termcodes(s, true, true, true) end
   2716        _G.win_lines = api.nvim_get_current_win()
   2717        vim.cmd "new"
   2718        _G.win_empty = api.nvim_get_current_win()
   2719        api.nvim_set_current_win(win_lines)
   2720        api.nvim_buf_set_lines(0, 0, -1, true, {"a", "b", "c"})
   2721      ]]
   2722 
   2723      -- Start Visual in current window, redraw in other window with fewer lines.
   2724      -- Should be fixed by vim-patch:8.2.4018.
   2725      exec_lua [[
   2726        api.nvim_feedkeys(t "G<C-V>", "txn", false)
   2727        api.nvim_win_call(win_empty, function() vim.cmd "redraw" end)
   2728      ]]
   2729 
   2730      -- Start Visual in current window, extend it in other window with more lines.
   2731      -- Fixed for win_execute by vim-patch:8.2.4026, but nvim_win_call should also not be affected.
   2732      exec_lua [[
   2733        api.nvim_feedkeys(t "<Esc>gg", "txn", false)
   2734        api.nvim_set_current_win(win_empty)
   2735        api.nvim_feedkeys(t "gg<C-V>", "txn", false)
   2736        api.nvim_win_call(win_lines, function() api.nvim_feedkeys(t "G<C-V>", "txn", false) end)
   2737        vim.cmd "redraw"
   2738      ]]
   2739    end)
   2740 
   2741    it('updates ruler if cursor moved', function()
   2742      -- Fixed for win_execute in vim-patch:8.1.2124, but should've applied to nvim_win_call too!
   2743      local screen = Screen.new(30, 5)
   2744      exec_lua [[
   2745        _G.api = vim.api
   2746        vim.opt.ruler = true
   2747        local lines = {}
   2748        for i = 0, 499 do lines[#lines + 1] = tostring(i) end
   2749        api.nvim_buf_set_lines(0, 0, -1, true, lines)
   2750        api.nvim_win_set_cursor(0, {20, 0})
   2751        vim.cmd "split"
   2752        _G.win = api.nvim_get_current_win()
   2753        vim.cmd "wincmd w | redraw"
   2754      ]]
   2755      screen:expect [[
   2756        19                            |
   2757        {2:< Name] [+] 20,1            3%}|
   2758        ^19                            |
   2759        {3:< Name] [+] 20,1            3%}|
   2760                                      |
   2761      ]]
   2762      exec_lua [[
   2763        api.nvim_win_call(win, function() api.nvim_win_set_cursor(0, {100, 0}) end)
   2764        vim.cmd "redraw"
   2765      ]]
   2766      screen:expect [[
   2767        99                            |
   2768        {2:< Name] [+] 100,1          19%}|
   2769        ^19                            |
   2770        {3:< Name] [+] 20,1            3%}|
   2771                                      |
   2772      ]]
   2773    end)
   2774 
   2775    it('can return values by reference', function()
   2776      eq(
   2777        { 7, 10 },
   2778        exec_lua [[
   2779        local val = {4, 10}
   2780        local ref = vim.api.nvim_win_call(0, function() return val end)
   2781        ref[1] = 7
   2782        return val
   2783      ]]
   2784      )
   2785    end)
   2786 
   2787    it('layout in current tabpage does not affect windows in others', function()
   2788      command('tab split')
   2789      local t2_move_win = api.nvim_get_current_win()
   2790      command('vsplit')
   2791      local t2_other_win = api.nvim_get_current_win()
   2792      command('tabprevious')
   2793      matches('E36: Not enough room$', pcall_err(command, 'execute "split|"->repeat(&lines)'))
   2794      command('vsplit')
   2795 
   2796      -- Without vim-patch:8.2.3862, this gives E36, despite just the 1st tabpage being full.
   2797      exec_lua('vim.api.nvim_win_call(..., function() vim.cmd.wincmd "J" end)', t2_move_win)
   2798      eq({ 'col', { { 'leaf', t2_other_win }, { 'leaf', t2_move_win } } }, fn.winlayout(2))
   2799    end)
   2800  end)
   2801 
   2802  describe('vim.iconv', function()
   2803    it('can convert strings', function()
   2804      eq(
   2805        'hello',
   2806        exec_lua [[
   2807        return vim.iconv('hello', 'latin1', 'utf-8')
   2808      ]]
   2809      )
   2810    end)
   2811 
   2812    it('can validate arguments', function()
   2813      eq(
   2814        { false, 'Expected at least 3 arguments' },
   2815        exec_lua [[
   2816        return {pcall(vim.iconv, 'hello')}
   2817      ]]
   2818      )
   2819 
   2820      eq(
   2821        { false, "bad argument #3 to '?' (expected string)" },
   2822        exec_lua [[
   2823        return {pcall(vim.iconv, 'hello', 'utf-8', true)}
   2824      ]]
   2825      )
   2826    end)
   2827 
   2828    it('can handle bad encodings', function()
   2829      eq(
   2830        NIL,
   2831        exec_lua [[
   2832        return vim.iconv('hello', 'foo', 'bar')
   2833      ]]
   2834      )
   2835    end)
   2836 
   2837    it('can handle strings with NUL bytes', function()
   2838      eq(
   2839        7,
   2840        exec_lua [[
   2841        local a = string.char(97, 98, 99, 0, 100, 101, 102) -- abc\0def
   2842        return string.len(vim.iconv(a, 'latin1', 'utf-8'))
   2843      ]]
   2844      )
   2845    end)
   2846  end)
   2847 
   2848  describe('vim.defaulttable', function()
   2849    it('creates nested table by default', function()
   2850      eq(
   2851        { b = { c = 1 } },
   2852        exec_lua [[
   2853        local a = vim.defaulttable()
   2854        a.b.c = 1
   2855        return a
   2856      ]]
   2857      )
   2858    end)
   2859 
   2860    it('allows to create default objects', function()
   2861      eq(
   2862        { b = 1 },
   2863        exec_lua [[
   2864        local a = vim.defaulttable(function() return 0 end)
   2865        a.b = a.b + 1
   2866        return a
   2867      ]]
   2868      )
   2869    end)
   2870 
   2871    it('accepts the key name', function()
   2872      eq(
   2873        { b = 'b', c = 'c' },
   2874        exec_lua [[
   2875        local a = vim.defaulttable(function(k) return k end)
   2876        local _ = a.b
   2877        local _ = a.c
   2878        return a
   2879      ]]
   2880      )
   2881    end)
   2882  end)
   2883 
   2884  it('vim.lua_omnifunc', function()
   2885    local screen = Screen.new(60, 5)
   2886    command [[ set omnifunc=v:lua.vim.lua_omnifunc ]]
   2887 
   2888    -- Note: the implementation is shared with lua command line completion.
   2889    -- More tests for completion in lua/command_line_completion_spec.lua
   2890    feed [[ivim.insp<c-x><c-o>]]
   2891    screen:expect {
   2892      grid = [[
   2893      vim.inspect^                                                 |
   2894      {1:~  }{12: inspect        }{1:                                         }|
   2895      {1:~  }{4: inspect_pos    }{1:                                         }|
   2896      {1:~                                                           }|
   2897      {5:-- Omni completion (^O^N^P) }{6:match 1 of 2}                    |
   2898    ]],
   2899    }
   2900  end)
   2901 
   2902  it('vim.print', function()
   2903    -- vim.print() returns its args.
   2904    eq(
   2905      { 42, 'abc', { a = { b = 77 } } },
   2906      exec_lua [[return {vim.print(42, 'abc', { a = { b = 77 }})}]]
   2907    )
   2908 
   2909    -- vim.print() pretty-prints the args.
   2910    eq(
   2911      dedent [[
   2912 
   2913      42
   2914      abc
   2915      {
   2916        a = {
   2917          b = 77
   2918        }
   2919      }]],
   2920      eval [[execute('lua vim.print(42, "abc", { a = { b = 77 }})')]]
   2921    )
   2922  end)
   2923 
   2924  it('vim.F.if_nil', function()
   2925    local function if_nil(...)
   2926      return exec_lua(
   2927        [[
   2928        local args = {...}
   2929        local nargs = select('#', ...)
   2930        for i = 1, nargs do
   2931          if args[i] == vim.NIL then
   2932            args[i] = nil
   2933          end
   2934        end
   2935        return vim.F.if_nil(unpack(args, 1, nargs))
   2936      ]],
   2937        ...
   2938      )
   2939    end
   2940 
   2941    local a = NIL
   2942    local b = NIL
   2943    local c = 42
   2944    local d = false
   2945    eq(42, if_nil(a, c))
   2946    eq(false, if_nil(d, b))
   2947    eq(42, if_nil(a, b, c, d))
   2948    eq(false, if_nil(d))
   2949    eq(false, if_nil(d, c))
   2950    eq(NIL, if_nil(a))
   2951  end)
   2952 
   2953  it('lpeg', function()
   2954    eq(
   2955      5,
   2956      exec_lua [[
   2957      local m = vim.lpeg
   2958      return m.match(m.R'09'^1, '4504ab')
   2959    ]]
   2960    )
   2961 
   2962    eq(4, exec_lua [[ return vim.re.match("abcde", '[a-c]+') ]])
   2963  end)
   2964 
   2965  it('vim.ringbuf', function()
   2966    local results = exec_lua([[
   2967      local ringbuf = vim.ringbuf(3)
   2968      ringbuf:push("a") -- idx: 0
   2969      local peeka1 = ringbuf:peek()
   2970      local peeka2 = ringbuf:peek()
   2971      local popa = ringbuf:pop()
   2972      local popnil = ringbuf:pop()
   2973      ringbuf:push("a") -- idx: 1
   2974      ringbuf:push("b") -- idx: 2
   2975 
   2976      -- doesn't read last added item, but uses separate read index
   2977      local pop_after_add_b = ringbuf:pop()
   2978 
   2979      ringbuf:push("c") -- idx: 3 wraps around, overrides idx: 0 "a"
   2980      ringbuf:push("d") -- idx: 4 wraps around, overrides idx: 1 "a"
   2981      return {
   2982        peeka1 = peeka1,
   2983        peeka2 = peeka2,
   2984        pop1 = popa,
   2985        pop2 = popnil,
   2986        pop3 = ringbuf:pop(),
   2987        pop4 = ringbuf:pop(),
   2988        pop5 = ringbuf:pop(),
   2989        pop_after_add_b = pop_after_add_b,
   2990      }
   2991    ]])
   2992    local expected = {
   2993      peeka1 = 'a',
   2994      peeka2 = 'a',
   2995      pop1 = 'a',
   2996      pop2 = nil,
   2997      pop3 = 'b',
   2998      pop4 = 'c',
   2999      pop5 = 'd',
   3000      pop_after_add_b = 'a',
   3001    }
   3002    eq(expected, results)
   3003  end)
   3004 end)
   3005 
   3006 describe('lua: builtin modules', function()
   3007  local function do_tests()
   3008    eq(2, exec_lua [[return vim.tbl_count {x=1,y=2}]])
   3009    eq('{ 10, "spam" }', exec_lua [[return vim.inspect {10, 'spam'}]])
   3010  end
   3011 
   3012  it('works', function()
   3013    clear()
   3014    do_tests()
   3015  end)
   3016 
   3017  it('works when disabled', function()
   3018    clear('--luamod-dev')
   3019    do_tests()
   3020  end)
   3021 
   3022  it('works without runtime', function()
   3023    clear { env = { VIMRUNTIME = 'fixtures/a' } }
   3024    do_tests()
   3025  end)
   3026 
   3027  it('fails when disabled without runtime', function()
   3028    clear()
   3029    command("let $VIMRUNTIME='fixtures/a'")
   3030    -- Use system([nvim,…]) instead of clear() to avoid stderr noise. #21844
   3031    local out = fn.system({
   3032      nvim_prog,
   3033      '--clean',
   3034      '--luamod-dev',
   3035      [[+call nvim_exec_lua('return vim.tbl_count {x=1,y=2}')]],
   3036      '+qa!',
   3037    }):gsub('\r\n', '\n')
   3038    eq(1, eval('v:shell_error'))
   3039    matches("'vim%._core.shared' not found", out)
   3040  end)
   3041 end)
   3042 
   3043 describe('lua: require("mod") from packages', function()
   3044  before_each(function()
   3045    clear('--cmd', 'set rtp+=test/functional/fixtures pp+=test/functional/fixtures')
   3046  end)
   3047 
   3048  it('propagates syntax error', function()
   3049    local syntax_error_msg = exec_lua [[
   3050      local _, err = pcall(require, "syntax_error")
   3051      return err
   3052    ]]
   3053 
   3054    matches('unexpected symbol', syntax_error_msg)
   3055  end)
   3056 
   3057  it('uses the right order of mod.lua vs mod/init.lua', function()
   3058    -- lua/fancy_x.lua takes precedence over lua/fancy_x/init.lua
   3059    eq('I am fancy_x.lua', exec_lua [[ return require'fancy_x' ]])
   3060    -- but lua/fancy_y/init.lua takes precedence over after/lua/fancy_y.lua
   3061    eq('I am init.lua of fancy_y!', exec_lua [[ return require'fancy_y' ]])
   3062    -- safety check: after/lua/fancy_z.lua is still loaded
   3063    eq('I am fancy_z.lua', exec_lua [[ return require'fancy_z' ]])
   3064  end)
   3065 end)
   3066 
   3067 describe('vim.keymap', function()
   3068  before_each(clear)
   3069 
   3070  it('validates', function()
   3071    matches(
   3072      'modes: expected string|table, got number',
   3073      pcall_err(exec_lua, [[vim.keymap.set(42, 'x', print)]])
   3074    )
   3075 
   3076    matches(
   3077      'rhs: expected string|function, got nil',
   3078      pcall_err(exec_lua, [[vim.keymap.set('n', 'x')]])
   3079    )
   3080 
   3081    matches(
   3082      'lhs: expected string, got table',
   3083      pcall_err(exec_lua, [[vim.keymap.set('n', {}, print)]])
   3084    )
   3085 
   3086    matches(
   3087      'rhs: expected string|function, got number',
   3088      pcall_err(exec_lua, [[vim.keymap.set({}, 'x', 42, function() end)]])
   3089    )
   3090 
   3091    matches(
   3092      'opts: expected table, got function',
   3093      pcall_err(exec_lua, [[vim.keymap.set({}, 'x', 'x', function() end)]])
   3094    )
   3095 
   3096    matches(
   3097      'rhs: expected string|function, got number',
   3098      pcall_err(exec_lua, [[vim.keymap.set('z', 'x', 42)]])
   3099    )
   3100 
   3101    matches('Invalid mode shortname: "z"', pcall_err(exec_lua, [[vim.keymap.set('z', 'x', 'y')]]))
   3102  end)
   3103 
   3104  it('mapping', function()
   3105    eq(
   3106      0,
   3107      exec_lua [[
   3108      GlobalCount = 0
   3109      vim.keymap.set('n', 'asdf', function() GlobalCount = GlobalCount + 1 end)
   3110      return GlobalCount
   3111    ]]
   3112    )
   3113 
   3114    feed('asdf\n')
   3115 
   3116    eq(1, exec_lua [[return GlobalCount]])
   3117  end)
   3118 
   3119  it('expr mapping', function()
   3120    exec_lua [[
   3121      vim.keymap.set('n', 'aa', function() return '<Insert>π<C-V><M-π>foo<lt><Esc>' end, {expr = true})
   3122    ]]
   3123 
   3124    feed('aa')
   3125 
   3126    eq({ 'π<M-π>foo<' }, api.nvim_buf_get_lines(0, 0, -1, false))
   3127  end)
   3128 
   3129  it('overwrite a mapping', function()
   3130    eq(
   3131      0,
   3132      exec_lua [[
   3133      GlobalCount = 0
   3134      vim.keymap.set('n', 'asdf', function() GlobalCount = GlobalCount + 1 end)
   3135      return GlobalCount
   3136    ]]
   3137    )
   3138 
   3139    feed('asdf\n')
   3140 
   3141    eq(1, exec_lua [[return GlobalCount]])
   3142 
   3143    exec_lua [[
   3144      vim.keymap.set('n', 'asdf', function() GlobalCount = GlobalCount - 1 end)
   3145    ]]
   3146 
   3147    feed('asdf\n')
   3148 
   3149    eq(0, exec_lua [[return GlobalCount]])
   3150  end)
   3151 
   3152  it('unmap', function()
   3153    eq(
   3154      0,
   3155      exec_lua [[
   3156      GlobalCount = 0
   3157      vim.keymap.set('n', 'asdf', function() GlobalCount = GlobalCount + 1 end)
   3158      return GlobalCount
   3159    ]]
   3160    )
   3161 
   3162    feed('asdf\n')
   3163 
   3164    eq(1, exec_lua [[return GlobalCount]])
   3165 
   3166    exec_lua [[
   3167      vim.keymap.del('n', 'asdf')
   3168    ]]
   3169 
   3170    feed('asdf\n')
   3171 
   3172    eq(1, exec_lua [[return GlobalCount]])
   3173    eq('\nNo mapping found', n.exec_capture('nmap asdf'))
   3174  end)
   3175 
   3176  it('buffer-local mappings', function()
   3177    eq(
   3178      0,
   3179      exec_lua [[
   3180      GlobalCount = 0
   3181      vim.keymap.set('n', 'asdf', function() GlobalCount = GlobalCount + 1 end, {buffer=true})
   3182      return GlobalCount
   3183    ]]
   3184    )
   3185 
   3186    feed('asdf\n')
   3187 
   3188    eq(1, exec_lua [[return GlobalCount]])
   3189 
   3190    exec_lua [[
   3191      vim.keymap.del('n', 'asdf', {buffer=true})
   3192    ]]
   3193 
   3194    feed('asdf\n')
   3195 
   3196    eq(1, exec_lua [[return GlobalCount]])
   3197    eq('\nNo mapping found', n.exec_capture('nmap asdf'))
   3198  end)
   3199 
   3200  it('does not mutate the opts parameter', function()
   3201    eq(
   3202      true,
   3203      exec_lua [[
   3204      opts = {buffer=true}
   3205      vim.keymap.set('n', 'asdf', function() end, opts)
   3206      return opts.buffer
   3207    ]]
   3208    )
   3209    eq(
   3210      true,
   3211      exec_lua [[
   3212      vim.keymap.del('n', 'asdf', opts)
   3213      return opts.buffer
   3214    ]]
   3215    )
   3216  end)
   3217 
   3218  it('<Plug> mappings', function()
   3219    eq(
   3220      0,
   3221      exec_lua [[
   3222      GlobalCount = 0
   3223      vim.keymap.set('n', '<plug>(asdf)', function() GlobalCount = GlobalCount + 1 end)
   3224      vim.keymap.set('n', 'ww', '<plug>(asdf)')
   3225      return GlobalCount
   3226    ]]
   3227    )
   3228 
   3229    feed('ww\n')
   3230 
   3231    eq(1, exec_lua [[return GlobalCount]])
   3232  end)
   3233 end)
   3234 
   3235 describe('Vimscript function exists()', function()
   3236  it('can check a lua function', function()
   3237    eq(
   3238      1,
   3239      exec_lua [[
   3240      _G.test = function() print("hello") end
   3241      return vim.fn.exists('*v:lua.test')
   3242    ]]
   3243    )
   3244 
   3245    eq(1, fn.exists('*v:lua.require("mpack").decode'))
   3246    eq(1, fn.exists("*v:lua.require('mpack').decode"))
   3247    eq(1, fn.exists('*v:lua.require"mpack".decode'))
   3248    eq(1, fn.exists("*v:lua.require'mpack'.decode"))
   3249    eq(1, fn.exists("*v:lua.require('vim.lsp').start"))
   3250    eq(1, fn.exists('*v:lua.require"vim.lsp".start'))
   3251    eq(1, fn.exists("*v:lua.require'vim.lsp'.start"))
   3252    eq(0, fn.exists("*v:lua.require'vim.lsp'.unknown"))
   3253    eq(0, fn.exists('*v:lua.?'))
   3254  end)
   3255 end)