neovim

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

ctx_functions_spec.lua (13195B)


      1 local t = require('test.testutil')
      2 local n = require('test.functional.testnvim')()
      3 
      4 local call = n.call
      5 local clear = n.clear
      6 local command = n.command
      7 local eq = t.eq
      8 local eval = n.eval
      9 local feed = n.feed
     10 local map = vim.tbl_map
     11 local api = n.api
     12 local parse_context = n.parse_context
     13 local exec_capture = n.exec_capture
     14 local source = n.source
     15 local trim = vim.trim
     16 local write_file = t.write_file
     17 local pcall_err = t.pcall_err
     18 
     19 describe('context functions', function()
     20  local fname1 = 'Xtest-functional-eval-ctx1'
     21  local fname2 = 'Xtest-functional-eval-ctx2'
     22  local outofbounds = 'Vim:E475: Invalid value for argument index: out of bounds'
     23 
     24  before_each(function()
     25    clear()
     26    write_file(fname1, '1\n2\n3')
     27    write_file(fname2, 'a\nb\nc')
     28  end)
     29 
     30  after_each(function()
     31    os.remove(fname1)
     32    os.remove(fname2)
     33  end)
     34 
     35  describe('ctxpush/ctxpop', function()
     36    it('saves and restores registers properly', function()
     37      local regs = { '1', '2', '3', 'a' }
     38      local vals = { '1', '2', '3', 'hjkl' }
     39      feed('i1<cr>2<cr>3<c-[>ddddddqahjklq')
     40      eq(
     41        vals,
     42        map(function(r)
     43          return trim(call('getreg', r))
     44        end, regs)
     45      )
     46      call('ctxpush')
     47      call('ctxpush', { 'regs' })
     48 
     49      map(function(r)
     50        call('setreg', r, {})
     51      end, regs)
     52      eq(
     53        { '', '', '', '' },
     54        map(function(r)
     55          return trim(call('getreg', r))
     56        end, regs)
     57      )
     58 
     59      call('ctxpop')
     60      eq(
     61        vals,
     62        map(function(r)
     63          return trim(call('getreg', r))
     64        end, regs)
     65      )
     66 
     67      map(function(r)
     68        call('setreg', r, {})
     69      end, regs)
     70      eq(
     71        { '', '', '', '' },
     72        map(function(r)
     73          return trim(call('getreg', r))
     74        end, regs)
     75      )
     76 
     77      call('ctxpop')
     78      eq(
     79        vals,
     80        map(function(r)
     81          return trim(call('getreg', r))
     82        end, regs)
     83      )
     84    end)
     85 
     86    it('saves and restores jumplist properly', function()
     87      command('edit ' .. fname1)
     88      feed('G')
     89      feed('gg')
     90      command('edit ' .. fname2)
     91      local jumplist = call('getjumplist')
     92      call('ctxpush')
     93      call('ctxpush', { 'jumps' })
     94 
     95      command('clearjumps')
     96      eq({ {}, 0 }, call('getjumplist'))
     97 
     98      call('ctxpop')
     99      eq(jumplist, call('getjumplist'))
    100 
    101      command('clearjumps')
    102      eq({ {}, 0 }, call('getjumplist'))
    103 
    104      call('ctxpop')
    105      eq(jumplist, call('getjumplist'))
    106    end)
    107 
    108    it('saves and restores buffer list properly', function()
    109      command('edit ' .. fname1)
    110      command('edit ' .. fname2)
    111      command('edit TEST')
    112      local bufs = call('map', call('getbufinfo'), 'v:val.name')
    113      call('ctxpush')
    114      call('ctxpush', { 'bufs' })
    115 
    116      command('%bwipeout')
    117      eq({ '' }, call('map', call('getbufinfo'), 'v:val.name'))
    118 
    119      call('ctxpop')
    120      eq({ '', unpack(bufs) }, call('map', call('getbufinfo'), 'v:val.name'))
    121 
    122      command('%bwipeout')
    123      eq({ '' }, call('map', call('getbufinfo'), 'v:val.name'))
    124 
    125      call('ctxpop')
    126      eq({ '', unpack(bufs) }, call('map', call('getbufinfo'), 'v:val.name'))
    127    end)
    128 
    129    it('saves and restores global variables properly', function()
    130      api.nvim_set_var('one', 1)
    131      api.nvim_set_var('Two', 2)
    132      api.nvim_set_var('THREE', 3)
    133      eq({ 1, 2, 3 }, eval('[g:one, g:Two, g:THREE]'))
    134      call('ctxpush')
    135      call('ctxpush', { 'gvars' })
    136 
    137      api.nvim_del_var('one')
    138      api.nvim_del_var('Two')
    139      api.nvim_del_var('THREE')
    140      eq('Vim:E121: Undefined variable: g:one', pcall_err(eval, 'g:one'))
    141      eq('Vim:E121: Undefined variable: g:Two', pcall_err(eval, 'g:Two'))
    142      eq('Vim:E121: Undefined variable: g:THREE', pcall_err(eval, 'g:THREE'))
    143 
    144      call('ctxpop')
    145      eq({ 1, 2, 3 }, eval('[g:one, g:Two, g:THREE]'))
    146 
    147      api.nvim_del_var('one')
    148      api.nvim_del_var('Two')
    149      api.nvim_del_var('THREE')
    150      eq('Vim:E121: Undefined variable: g:one', pcall_err(eval, 'g:one'))
    151      eq('Vim:E121: Undefined variable: g:Two', pcall_err(eval, 'g:Two'))
    152      eq('Vim:E121: Undefined variable: g:THREE', pcall_err(eval, 'g:THREE'))
    153 
    154      call('ctxpop')
    155      eq({ 1, 2, 3 }, eval('[g:one, g:Two, g:THREE]'))
    156    end)
    157 
    158    it('saves and restores script functions properly', function()
    159      source([[
    160      function s:greet(name)
    161        echom 'Hello, '.a:name.'!'
    162      endfunction
    163 
    164      function s:greet_all(name, ...)
    165        echom 'Hello, '.a:name.'!'
    166        for more in a:000
    167          echom 'Hello, '.more.'!'
    168        endfor
    169      endfunction
    170 
    171      function Greet(name)
    172        call call('s:greet', [a:name])
    173      endfunction
    174 
    175      function GreetAll(name, ...)
    176        call call('s:greet_all', extend([a:name], a:000))
    177      endfunction
    178 
    179      function SaveSFuncs()
    180        call ctxpush(['sfuncs'])
    181      endfunction
    182 
    183      function DeleteSFuncs()
    184        delfunction s:greet
    185        delfunction s:greet_all
    186      endfunction
    187 
    188      function RestoreFuncs()
    189        call ctxpop()
    190      endfunction
    191 
    192      let g:sid = expand('<SID>')
    193      ]])
    194      local sid = api.nvim_get_var('sid')
    195 
    196      eq('Hello, World!', exec_capture([[call Greet('World')]]))
    197      eq(
    198        'Hello, World!' .. '\nHello, One!' .. '\nHello, Two!' .. '\nHello, Three!',
    199        exec_capture([[call GreetAll('World', 'One', 'Two', 'Three')]])
    200      )
    201 
    202      call('SaveSFuncs')
    203      call('DeleteSFuncs')
    204 
    205      eq(
    206        ('function Greet, line 1: Vim(call):E117: Unknown function: %sgreet'):format(sid),
    207        pcall_err(command, [[call Greet('World')]])
    208      )
    209      eq(
    210        ('function GreetAll, line 1: Vim(call):E117: Unknown function: %sgreet_all'):format(sid),
    211        pcall_err(command, [[call GreetAll('World', 'One', 'Two', 'Three')]])
    212      )
    213 
    214      call('RestoreFuncs')
    215 
    216      eq('Hello, World!', exec_capture([[call Greet('World')]]))
    217      eq(
    218        'Hello, World!' .. '\nHello, One!' .. '\nHello, Two!' .. '\nHello, Three!',
    219        exec_capture([[call GreetAll('World', 'One', 'Two', 'Three')]])
    220      )
    221    end)
    222 
    223    it('saves and restores functions properly', function()
    224      source([[
    225      function Greet(name)
    226        echom 'Hello, '.a:name.'!'
    227      endfunction
    228 
    229      function GreetAll(name, ...)
    230        echom 'Hello, '.a:name.'!'
    231        for more in a:000
    232          echom 'Hello, '.more.'!'
    233        endfor
    234      endfunction
    235      ]])
    236 
    237      eq('Hello, World!', exec_capture([[call Greet('World')]]))
    238      eq(
    239        'Hello, World!' .. '\nHello, One!' .. '\nHello, Two!' .. '\nHello, Three!',
    240        exec_capture([[call GreetAll('World', 'One', 'Two', 'Three')]])
    241      )
    242 
    243      call('ctxpush', { 'funcs' })
    244      command('delfunction Greet')
    245      command('delfunction GreetAll')
    246 
    247      eq('Vim:E117: Unknown function: Greet', pcall_err(call, 'Greet', 'World'))
    248      eq(
    249        'Vim:E117: Unknown function: GreetAll',
    250        pcall_err(call, 'GreetAll', 'World', 'One', 'Two', 'Three')
    251      )
    252 
    253      call('ctxpop')
    254 
    255      eq('Hello, World!', exec_capture([[call Greet('World')]]))
    256      eq(
    257        'Hello, World!' .. '\nHello, One!' .. '\nHello, Two!' .. '\nHello, Three!',
    258        exec_capture([[call GreetAll('World', 'One', 'Two', 'Three')]])
    259      )
    260    end)
    261 
    262    it('errors out when context stack is empty', function()
    263      local err = 'Vim:Context stack is empty'
    264      eq(err, pcall_err(call, 'ctxpop'))
    265      eq(err, pcall_err(call, 'ctxpop'))
    266      call('ctxpush')
    267      call('ctxpush')
    268      call('ctxpop')
    269      call('ctxpop')
    270      eq(err, pcall_err(call, 'ctxpop'))
    271    end)
    272  end)
    273 
    274  describe('ctxsize()', function()
    275    it('returns context stack size', function()
    276      eq(0, call('ctxsize'))
    277      call('ctxpush')
    278      eq(1, call('ctxsize'))
    279      call('ctxpush')
    280      eq(2, call('ctxsize'))
    281      call('ctxpush')
    282      eq(3, call('ctxsize'))
    283      call('ctxpop')
    284      eq(2, call('ctxsize'))
    285      call('ctxpop')
    286      eq(1, call('ctxsize'))
    287      call('ctxpop')
    288      eq(0, call('ctxsize'))
    289    end)
    290  end)
    291 
    292  describe('ctxget()', function()
    293    it('errors out when index is out of bounds', function()
    294      eq(outofbounds, pcall_err(call, 'ctxget'))
    295      call('ctxpush')
    296      eq(outofbounds, pcall_err(call, 'ctxget', 1))
    297      call('ctxpop')
    298      eq(outofbounds, pcall_err(call, 'ctxget', 0))
    299    end)
    300 
    301    it('returns context dict at index in context stack', function()
    302      feed('i1<cr>2<cr>3<c-[>ddddddqahjklq')
    303      command('edit! ' .. fname1)
    304      feed('G')
    305      feed('gg')
    306      command('edit ' .. fname2)
    307      api.nvim_set_var('one', 1)
    308      api.nvim_set_var('Two', 2)
    309      api.nvim_set_var('THREE', 3)
    310 
    311      local with_regs = {
    312        ['regs'] = {
    313          { ['rt'] = 1, ['rc'] = { '1' }, ['n'] = 49, ['ru'] = true },
    314          { ['rt'] = 1, ['rc'] = { '2' }, ['n'] = 50 },
    315          { ['rt'] = 1, ['rc'] = { '3' }, ['n'] = 51 },
    316          { ['rc'] = { 'hjkl' }, ['n'] = 97 },
    317        },
    318      }
    319 
    320      local with_jumps = {
    321        ['jumps'] = eval((([[
    322        filter(map(add(
    323        getjumplist()[0], { 'bufnr': bufnr('%'), 'lnum': getcurpos()[1] }),
    324        'filter(
    325        { "f": expand("#".v:val.bufnr.":p"), "l": v:val.lnum },
    326        { k, v -> k != "l" || v != 1 })'), '!empty(v:val.f)')
    327        ]]):gsub('\n', ''))),
    328      }
    329 
    330      local with_bufs = {
    331        ['bufs'] = eval([[
    332        filter(map(getbufinfo(), '{ "f": v:val.name }'), '!empty(v:val.f)')
    333        ]]),
    334      }
    335 
    336      local with_gvars = {
    337        ['gvars'] = { { 'one', 1 }, { 'Two', 2 }, { 'THREE', 3 } },
    338      }
    339 
    340      local with_all = {
    341        ['regs'] = with_regs['regs'],
    342        ['jumps'] = with_jumps['jumps'],
    343        ['bufs'] = with_bufs['bufs'],
    344        ['gvars'] = with_gvars['gvars'],
    345      }
    346 
    347      call('ctxpush')
    348      eq(with_all, parse_context(call('ctxget')))
    349      eq(with_all, parse_context(call('ctxget', 0)))
    350 
    351      call('ctxpush', { 'gvars' })
    352      eq(with_gvars, parse_context(call('ctxget')))
    353      eq(with_gvars, parse_context(call('ctxget', 0)))
    354      eq(with_all, parse_context(call('ctxget', 1)))
    355 
    356      call('ctxpush', { 'bufs' })
    357      eq(with_bufs, parse_context(call('ctxget')))
    358      eq(with_bufs, parse_context(call('ctxget', 0)))
    359      eq(with_gvars, parse_context(call('ctxget', 1)))
    360      eq(with_all, parse_context(call('ctxget', 2)))
    361 
    362      call('ctxpush', { 'jumps' })
    363      eq(with_jumps, parse_context(call('ctxget')))
    364      eq(with_jumps, parse_context(call('ctxget', 0)))
    365      eq(with_bufs, parse_context(call('ctxget', 1)))
    366      eq(with_gvars, parse_context(call('ctxget', 2)))
    367      eq(with_all, parse_context(call('ctxget', 3)))
    368 
    369      call('ctxpush', { 'regs' })
    370      eq(with_regs, parse_context(call('ctxget')))
    371      eq(with_regs, parse_context(call('ctxget', 0)))
    372      eq(with_jumps, parse_context(call('ctxget', 1)))
    373      eq(with_bufs, parse_context(call('ctxget', 2)))
    374      eq(with_gvars, parse_context(call('ctxget', 3)))
    375      eq(with_all, parse_context(call('ctxget', 4)))
    376 
    377      call('ctxpop')
    378      eq(with_jumps, parse_context(call('ctxget')))
    379      eq(with_jumps, parse_context(call('ctxget', 0)))
    380      eq(with_bufs, parse_context(call('ctxget', 1)))
    381      eq(with_gvars, parse_context(call('ctxget', 2)))
    382      eq(with_all, parse_context(call('ctxget', 3)))
    383 
    384      call('ctxpop')
    385      eq(with_bufs, parse_context(call('ctxget')))
    386      eq(with_bufs, parse_context(call('ctxget', 0)))
    387      eq(with_gvars, parse_context(call('ctxget', 1)))
    388      eq(with_all, parse_context(call('ctxget', 2)))
    389 
    390      call('ctxpop')
    391      eq(with_gvars, parse_context(call('ctxget')))
    392      eq(with_gvars, parse_context(call('ctxget', 0)))
    393      eq(with_all, parse_context(call('ctxget', 1)))
    394 
    395      call('ctxpop')
    396      eq(with_all, parse_context(call('ctxget')))
    397      eq(with_all, parse_context(call('ctxget', 0)))
    398    end)
    399  end)
    400 
    401  describe('ctxset()', function()
    402    it('errors out when index is out of bounds', function()
    403      eq(outofbounds, pcall_err(call, 'ctxset', { dummy = 1 }))
    404      call('ctxpush')
    405      eq(outofbounds, pcall_err(call, 'ctxset', { dummy = 1 }, 1))
    406      call('ctxpop')
    407      eq(outofbounds, pcall_err(call, 'ctxset', { dummy = 1 }, 0))
    408    end)
    409 
    410    it('errors when context dict is invalid', function()
    411      call('ctxpush')
    412      eq(
    413        'Vim:E474: Failed to convert list to msgpack string buffer',
    414        pcall_err(call, 'ctxset', { regs = { {} }, jumps = { {} } })
    415      )
    416    end)
    417 
    418    it('sets context dict at index in context stack', function()
    419      api.nvim_set_var('one', 1)
    420      api.nvim_set_var('Two', 2)
    421      api.nvim_set_var('THREE', 3)
    422      call('ctxpush')
    423      local ctx1 = call('ctxget')
    424      api.nvim_set_var('one', 'a')
    425      api.nvim_set_var('Two', 'b')
    426      api.nvim_set_var('THREE', 'c')
    427      call('ctxpush')
    428      call('ctxpush')
    429      local ctx2 = call('ctxget')
    430 
    431      eq({ 'a', 'b', 'c' }, eval('[g:one, g:Two, g:THREE]'))
    432      call('ctxset', ctx1)
    433      call('ctxset', ctx2, 2)
    434      call('ctxpop')
    435      eq({ 1, 2, 3 }, eval('[g:one, g:Two, g:THREE]'))
    436      call('ctxpop')
    437      eq({ 'a', 'b', 'c' }, eval('[g:one, g:Two, g:THREE]'))
    438      api.nvim_set_var('one', 1.5)
    439      eq({ 1.5, 'b', 'c' }, eval('[g:one, g:Two, g:THREE]'))
    440      call('ctxpop')
    441      eq({ 'a', 'b', 'c' }, eval('[g:one, g:Two, g:THREE]'))
    442    end)
    443  end)
    444 end)