neovim

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

source_spec.lua (9444B)


      1 local t = require('test.testutil')
      2 local n = require('test.functional.testnvim')()
      3 
      4 local command = n.command
      5 local insert = n.insert
      6 local eq = t.eq
      7 local clear = n.clear
      8 local api = n.api
      9 local fn = n.fn
     10 local feed = n.feed
     11 local feed_command = n.feed_command
     12 local write_file = t.write_file
     13 local tmpname = t.tmpname
     14 local exec = n.exec
     15 local exc_exec = n.exc_exec
     16 local exec_lua = n.exec_lua
     17 local eval = n.eval
     18 local exec_capture = n.exec_capture
     19 local neq = t.neq
     20 local matches = t.matches
     21 local mkdir = t.mkdir
     22 local rmdir = n.rmdir
     23 local is_os = t.is_os
     24 
     25 describe(':source', function()
     26  before_each(function()
     27    clear()
     28  end)
     29 
     30  it('sourcing a file that is deleted and recreated is consistent vim-patch:8.1.0151', function()
     31    local test_file = 'Xfile.vim'
     32    local other_file = 'Xfoobar'
     33    local script = [[
     34      func Func()
     35      endfunc
     36    ]]
     37    write_file(test_file, script)
     38    command('source ' .. test_file)
     39    os.remove(test_file)
     40    write_file(test_file, script)
     41    command('source ' .. test_file)
     42    os.remove(test_file)
     43    write_file(other_file, '')
     44    write_file(test_file, script)
     45    command('source ' .. test_file)
     46    os.remove(other_file)
     47    os.remove(test_file)
     48  end)
     49 
     50  it("changing 'shellslash' changes the result of expand()", function()
     51    t.skip(not is_os('win'), "N/A: 'shellslash' only works on Windows")
     52 
     53    api.nvim_set_option_value('shellslash', false, {})
     54    mkdir('Xshellslash')
     55 
     56    write_file(
     57      [[Xshellslash/Xstack.vim]],
     58      [[
     59      let g:stack1 = expand('<stack>')
     60      set shellslash
     61      let g:stack2 = expand('<stack>')
     62      set noshellslash
     63      let g:stack3 = expand('<stack>')
     64    ]]
     65    )
     66 
     67    for _ = 1, 2 do
     68      command([[source Xshellslash/Xstack.vim]])
     69      matches([[Xshellslash\Xstack%.vim]], api.nvim_get_var('stack1'))
     70      matches([[Xshellslash/Xstack%.vim]], api.nvim_get_var('stack2'))
     71      matches([[Xshellslash\Xstack%.vim]], api.nvim_get_var('stack3'))
     72    end
     73 
     74    write_file(
     75      [[Xshellslash/Xstack.lua]],
     76      [[
     77      vim.g.stack1 = vim.fn.expand('<stack>')
     78      vim.o.shellslash = true
     79      vim.g.stack2 = vim.fn.expand('<stack>')
     80      vim.o.shellslash = false
     81      vim.g.stack3 = vim.fn.expand('<stack>')
     82    ]]
     83    )
     84 
     85    for _ = 1, 2 do
     86      command([[source Xshellslash/Xstack.lua]])
     87      matches([[Xshellslash\Xstack%.lua]], api.nvim_get_var('stack1'))
     88      matches([[Xshellslash/Xstack%.lua]], api.nvim_get_var('stack2'))
     89      matches([[Xshellslash\Xstack%.lua]], api.nvim_get_var('stack3'))
     90    end
     91 
     92    rmdir('Xshellslash')
     93  end)
     94 
     95  it('current buffer', function()
     96    insert([[
     97      let a = 2
     98      let b = #{
     99        \ k: "v"
    100       "\ (o_o)
    101        \ }
    102      let c = expand("<SID>")
    103      let s:s = 0zbeef.cafe
    104      let d = s:s]])
    105 
    106    command('source')
    107    eq('2', exec_capture('echo a'))
    108    eq("{'k': 'v'}", exec_capture('echo b'))
    109    eq('<SNR>1_', exec_capture('echo c'))
    110    eq('0zBEEFCAFE', exec_capture('echo d'))
    111 
    112    exec('set cpoptions+=C')
    113    eq("Vim(let):E723: Missing end of Dictionary '}': ", exc_exec('source'))
    114  end)
    115 
    116  it('selection in current buffer', function()
    117    insert([[
    118      let a = 2
    119      let a = 3
    120      let a = 4
    121      let b = #{
    122       "\ (>_<)
    123        \ K: "V"
    124        \ }
    125      function! s:C() abort
    126        return expand("<SID>") .. "C()"
    127      endfunction
    128      let D = {-> s:C()}]])
    129 
    130    -- Source the 2nd line only
    131    feed('ggjV')
    132    feed_command(':source')
    133    eq('3', exec_capture('echo a'))
    134 
    135    -- Source last line only
    136    feed_command(':$source')
    137    eq('Vim(echo):E117: Unknown function: s:C', exc_exec('echo D()'))
    138 
    139    -- Source from 2nd line to end of file
    140    feed('ggjVG')
    141    feed_command(':source')
    142    eq('4', exec_capture('echo a'))
    143    eq("{'K': 'V'}", exec_capture('echo b'))
    144    eq('<SNR>1_C()', exec_capture('echo D()'))
    145 
    146    -- Source last line after the lines that define s:C() have been sourced
    147    feed_command(':$source')
    148    eq('<SNR>1_C()', exec_capture('echo D()'))
    149 
    150    exec('set cpoptions+=C')
    151    eq("Vim(let):E723: Missing end of Dictionary '}': ", exc_exec("'<,'>source"))
    152  end)
    153 
    154  it('does not break if current buffer is modified while sourced', function()
    155    insert [[
    156      bwipeout!
    157      let a = 123
    158    ]]
    159    command('source')
    160    eq('123', exec_capture('echo a'))
    161  end)
    162 
    163  it('multiline heredoc command', function()
    164    insert([[
    165      lua << EOF
    166      y = 4
    167      EOF]])
    168 
    169    command('source')
    170    eq('4', exec_capture('echo luaeval("y")'))
    171  end)
    172 
    173  --- @param verbose boolean
    174  local function test_source_lua_file(verbose)
    175    local test_file = 'Xtest.lua'
    176    write_file(
    177      test_file,
    178      [[
    179      vim.g.sourced_lua = 1
    180      vim.g.sfile_value = vim.fn.expand('<sfile>')
    181      vim.g.stack_value = vim.fn.expand('<stack>')
    182      vim.g.script_value = vim.fn.expand('<script>')
    183      vim.g.script_id = tonumber(vim.fn.expand('<SID>'):match('<SNR>(%d+)_'))
    184      vim.o.mouse = 'nv'
    185    ]]
    186    )
    187 
    188    command('set shellslash')
    189    command(('%ssource %s'):format(verbose and 'verbose ' or '', test_file))
    190    eq(1, eval('g:sourced_lua'))
    191    matches([[/Xtest%.lua$]], api.nvim_get_var('sfile_value'))
    192    matches([[/Xtest%.lua$]], api.nvim_get_var('stack_value'))
    193    matches([[/Xtest%.lua$]], api.nvim_get_var('script_value'))
    194 
    195    local expected_sid = fn.getscriptinfo({ name = test_file })[1].sid
    196    local sid = api.nvim_get_var('script_id')
    197    eq(expected_sid, sid)
    198    eq(sid, api.nvim_get_option_info2('mouse', {}).last_set_sid)
    199 
    200    os.remove(test_file)
    201  end
    202 
    203  it('can source lua files', function()
    204    test_source_lua_file(false)
    205  end)
    206 
    207  it('with :verbose modifier can source lua files', function()
    208    test_source_lua_file(true)
    209  end)
    210 
    211  describe('can source current buffer', function()
    212    local function test_source_lua_curbuf()
    213      it('selected region', function()
    214        insert([[
    215          vim.g.b = 5
    216          vim.g.b = 6
    217          vim.g.b = 7
    218          a = [=[
    219           "\ a
    220            \ b]=]
    221        ]])
    222        feed('dd')
    223 
    224        feed('ggjV')
    225        feed_command(':source')
    226        eq(6, eval('g:b'))
    227 
    228        feed('GVkk')
    229        feed_command(':source')
    230        eq(' "\\ a\n  \\ b', exec_lua('return _G.a'))
    231      end)
    232 
    233      it('whole buffer', function()
    234        insert([[
    235          vim.g.c = 10
    236          vim.g.c = 11
    237          vim.g.c = 12
    238          a = [=[
    239            \ 1
    240           "\ 2]=]
    241          vim.g.sfile_value = vim.fn.expand('<sfile>')
    242          vim.g.stack_value = vim.fn.expand('<stack>')
    243          vim.g.script_value = vim.fn.expand('<script>')
    244        ]])
    245        feed('dd')
    246 
    247        feed_command(':source')
    248        local filepath = fn.expand('%:p')
    249        if filepath == '' then
    250          filepath = ':source buffer=1'
    251        end
    252        eq(12, eval('g:c'))
    253        eq('  \\ 1\n "\\ 2', exec_lua('return _G.a'))
    254        eq(filepath, api.nvim_get_var('sfile_value'))
    255        eq(filepath, api.nvim_get_var('stack_value'))
    256        eq(filepath, api.nvim_get_var('script_value'))
    257      end)
    258    end
    259 
    260    describe('with ft=lua', function()
    261      before_each(function()
    262        command('setlocal ft=lua')
    263      end)
    264      test_source_lua_curbuf()
    265    end)
    266 
    267    describe('with .lua extension', function()
    268      before_each(function()
    269        command('edit ' .. tmpname() .. '.lua')
    270      end)
    271      test_source_lua_curbuf()
    272    end)
    273  end)
    274 
    275  it("doesn't throw E484 for lua parsing/runtime errors", function()
    276    local test_file = 'Xtest.lua'
    277 
    278    -- Does throw E484 for unreadable files
    279    local ok, result = pcall(exec_capture, ':source ' .. test_file .. 'noexisting')
    280    eq(false, ok)
    281    neq(nil, result:find('E484'))
    282 
    283    -- Doesn't throw for parsing error
    284    write_file(test_file, 'vim.g.c = ')
    285    ok, result = pcall(exec_capture, ':source ' .. test_file)
    286    eq(false, ok)
    287    eq(nil, result:find('E484'))
    288    os.remove(test_file)
    289 
    290    -- Doesn't throw for runtime error
    291    write_file(test_file, "error('Cause error anyway :D')")
    292    ok, result = pcall(exec_capture, ':source ' .. test_file)
    293    eq(false, ok)
    294    eq(nil, result:find('E484'))
    295    os.remove(test_file)
    296  end)
    297 
    298  it('sources Lua/Vimscript codeblocks based on treesitter injection', function()
    299    insert([[
    300      *test.txt*  Test help file
    301 
    302      Lua example: >lua
    303        vim.g.test_lua = 42
    304      <
    305 
    306      Vim example: >vim
    307        let g:test_vim = 99
    308      <]])
    309    command('setlocal filetype=help')
    310 
    311    -- Source Lua codeblock (line 4 contains the Lua code)
    312    command(':4source')
    313    eq(42, eval('g:test_lua'))
    314 
    315    -- Source Vimscript codeblock (line 8 contains the Vim code)
    316    command(':8source')
    317    eq(99, eval('g:test_vim'))
    318 
    319    -- Test fallback without treesitter
    320    command('enew')
    321    insert([[let g:test_no_ts = 123]])
    322    command('setlocal filetype=')
    323    command('source')
    324    eq(123, eval('g:test_no_ts'))
    325  end)
    326 end)
    327 
    328 it('$HOME is not shortened in filepath in v:stacktrace from sourced file', function()
    329  local sep = n.get_pathsep()
    330  local xhome = table.concat({ vim.uv.cwd(), 'Xhome' }, sep)
    331  mkdir(xhome)
    332  clear({ env = { HOME = xhome } })
    333  finally(function()
    334    rmdir(xhome)
    335  end)
    336  local filepath = table.concat({ xhome, 'Xstacktrace.vim' }, sep)
    337  local script = [[
    338    func Xfunc()
    339      throw 'Exception from Xfunc'
    340    endfunc
    341  ]]
    342  write_file(filepath, script)
    343  exec('source ' .. filepath)
    344  exec([[
    345    try
    346      call Xfunc()
    347    catch
    348      let g:stacktrace = v:stacktrace
    349    endtry
    350  ]])
    351  eq(filepath, n.eval('g:stacktrace[-1].filepath'))
    352 end)