neovim

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

embed_spec.lua (11305B)


      1 local t = require('test.testutil')
      2 local n = require('test.functional.testnvim')()
      3 local Screen = require('test.functional.ui.screen')
      4 local uv = vim.uv
      5 
      6 local api = n.api
      7 local feed = n.feed
      8 local eq = t.eq
      9 local neq = t.neq
     10 local clear = n.clear
     11 local ok = t.ok
     12 local fn = n.fn
     13 local nvim_prog = n.nvim_prog
     14 local retry = t.retry
     15 local write_file = t.write_file
     16 local assert_log = t.assert_log
     17 local check_close = n.check_close
     18 local is_os = t.is_os
     19 
     20 local testlog = 'Xtest-embed-log'
     21 
     22 local function test_embed(ext_linegrid)
     23  local screen
     24  local function startup(...)
     25    clear { args_rm = { '--headless' }, args = { ... } }
     26 
     27    -- attach immediately after startup, for early UI
     28    screen = Screen.new(60, 8, { ext_linegrid = ext_linegrid })
     29    screen:add_extra_attr_ids {
     30      [100] = { foreground = Screen.colors.NvimDarkCyan },
     31      [101] = { foreground = Screen.colors.NvimDarkRed },
     32      [102] = {
     33        background = Screen.colors.NvimLightGrey4,
     34        foreground = Screen.colors.NvimDarkGrey2,
     35      },
     36    }
     37  end
     38 
     39  it('can display errors', function()
     40    startup('--cmd', 'echoerr invalid+')
     41    screen:expect([[
     42                                                                  |*4
     43      {3:                                                            }|
     44      {9:Error in pre-vimrc command line:}                            |
     45      {9:E121: Undefined variable: invalid}                           |
     46      {6:Press ENTER or type command to continue}^                     |
     47    ]])
     48 
     49    feed('<cr>')
     50    screen:expect([[
     51      ^                                                            |
     52      {1:~                                                           }|*6
     53                                                                  |
     54    ]])
     55  end)
     56 
     57  it("doesn't erase output when setting color scheme", function()
     58    if t.is_os('openbsd') then
     59      pending('FIXME #10804')
     60    end
     61    startup('--cmd', 'echoerr "foo"', '--cmd', 'color default', '--cmd', 'echoerr "bar"')
     62    screen:expect([[
     63                                                                  |*3
     64      {102:                                                            }|
     65      {9:Error in pre-vimrc command line:}                            |
     66      {9:foo}                                                         |
     67      {101:bar}                                                         |
     68      {100:Press ENTER or type command to continue}^                     |
     69    ]])
     70  end)
     71 
     72  it("doesn't erase output when setting Normal colors", function()
     73    startup('--cmd', 'echoerr "foo"', '--cmd', 'hi Normal guibg=Green', '--cmd', 'echoerr "bar"')
     74    screen:expect {
     75      grid = [[
     76                                                                  |*3
     77      {3:                                                            }|
     78      {9:Error in pre-vimrc command line:}                            |
     79      {9:foo}                                                         |
     80      {9:bar}                                                         |
     81      {6:Press ENTER or type command to continue}^                     |
     82    ]],
     83      condition = function()
     84        eq(Screen.colors.Green, screen.default_colors.rgb_bg)
     85      end,
     86    }
     87  end)
     88 end
     89 
     90 describe('--embed UI on startup (ext_linegrid=true)', function()
     91  test_embed(true)
     92 end)
     93 describe('--embed UI on startup (ext_linegrid=false)', function()
     94  test_embed(false)
     95 end)
     96 
     97 describe('--embed UI', function()
     98  after_each(function()
     99    check_close()
    100    os.remove(testlog)
    101  end)
    102 
    103  it('can pass stdin', function()
    104    local pipe = assert(uv.pipe())
    105 
    106    local writer = assert(uv.new_pipe(false))
    107    writer:open(pipe.write)
    108 
    109    clear { args_rm = { '--headless' }, io_extra = pipe.read, env = { NVIM_LOG_FILE = testlog } }
    110 
    111    -- attach immediately after startup, for early UI
    112    -- rpc_async: Avoid hanging. #24888
    113    local screen = Screen.new(40, 8, { stdin_fd = 3 }, false)
    114    screen.rpc_async = true -- Avoid hanging. #24888
    115    screen:attach()
    116 
    117    writer:write 'hello nvim\nfrom external input\n'
    118    writer:shutdown(function()
    119      writer:close()
    120    end)
    121 
    122    screen:expect [[
    123      ^hello nvim                              |
    124      from external input                     |
    125      {1:~                                       }|*5
    126                                              |
    127    ]]
    128 
    129    -- stdin (rpc input) still works
    130    feed 'o'
    131    screen:expect [[
    132      hello nvim                              |
    133      ^                                        |
    134      from external input                     |
    135      {1:~                                       }|*4
    136      {5:-- INSERT --}                            |
    137    ]]
    138 
    139    if not is_os('win') then
    140      assert_log('Failed to get flags on descriptor 3: Bad file descriptor', testlog, 100)
    141    end
    142  end)
    143 
    144  it('can pass stdin to -q - #17523', function()
    145    write_file(
    146      'Xbadfile.c',
    147      [[
    148      /* some file with an error */
    149      main() {
    150        functionCall(arg; arg, arg);
    151        return 666
    152      }
    153      ]]
    154    )
    155    finally(function()
    156      os.remove('Xbadfile.c')
    157    end)
    158 
    159    local pipe = assert(uv.pipe())
    160 
    161    local writer = assert(uv.new_pipe(false))
    162    writer:open(pipe.write)
    163 
    164    clear { args_rm = { '--headless' }, args = { '-q', '-' }, io_extra = pipe.read }
    165 
    166    -- attach immediately after startup, for early UI
    167    local screen = Screen.new(60, 8, { stdin_fd = 3 }, false)
    168    screen.rpc_async = true -- Avoid hanging. #24888
    169    screen:attach()
    170 
    171    writer:write [[Xbadfile.c:4:12: error: expected ';' before '}' token]]
    172    writer:shutdown(function()
    173      writer:close()
    174    end)
    175 
    176    screen:expect [[
    177      /* some file with an error */                               |
    178      main() {                                                    |
    179        functionCall(arg; arg, arg);                              |
    180        return 66^6                                                |
    181      }                                                           |
    182      {1:~                                                           }|*2
    183      (1 of 1): error: expected ';' before '}' token              |
    184    ]]
    185 
    186    -- stdin (rpc input) still works
    187    feed 'A'
    188    screen:expect [[
    189      /* some file with an error */                               |
    190      main() {                                                    |
    191        functionCall(arg; arg, arg);                              |
    192        return 666^                                                |
    193      }                                                           |
    194      {1:~                                                           }|*2
    195      {5:-- INSERT --}                                                |
    196    ]]
    197 
    198    eq('-', api.nvim_get_option_value('errorfile', {}))
    199  end)
    200 
    201  it('only sets background colors once even if overridden', function()
    202    local screen, current, seen
    203    local function handle_default_colors_set(_, _, rgb_bg, _, _, _)
    204      seen[rgb_bg] = true
    205      current = rgb_bg
    206    end
    207    local function startup(...)
    208      seen = {}
    209      current = nil
    210      clear { args_rm = { '--headless' }, args = { ... } }
    211 
    212      -- attach immediately after startup, for early UI
    213      screen = Screen.new(40, 8)
    214      screen._handle_default_colors_set = handle_default_colors_set
    215    end
    216 
    217    startup()
    218    screen:expect {
    219      condition = function()
    220        eq(16777215, current)
    221      end,
    222    }
    223    eq({ [16777215] = true }, seen)
    224 
    225    -- NB: by accident how functional/testutil.lua currently handles the default color scheme, the
    226    -- above is sufficient to test the behavior. But in case that workaround is removed, we need
    227    -- a test with an explicit override like below, so do it to remain safe.
    228    startup('--cmd', 'hi NORMAL guibg=#FF00FF')
    229    screen:expect {
    230      condition = function()
    231        eq(16711935, current)
    232      end,
    233    }
    234    eq({ [16711935] = true }, seen) -- we only saw the last one, despite 16777215 was set internally earlier
    235  end)
    236 
    237  it('updates cwd of attached UI #21771', function()
    238    clear { args_rm = { '--headless' } }
    239    api.nvim_set_current_dir(t.paths.test_source_path)
    240 
    241    local screen = Screen.new(40, 8)
    242 
    243    screen:expect {
    244      condition = function()
    245        eq(t.paths.test_source_path, screen.pwd)
    246      end,
    247    }
    248 
    249    -- Change global cwd
    250    n.command(string.format('cd %s/src/nvim', t.paths.test_source_path))
    251 
    252    screen:expect {
    253      condition = function()
    254        eq(string.format('%s/src/nvim', t.paths.test_source_path), screen.pwd)
    255      end,
    256    }
    257 
    258    -- Split the window and change the cwd in the split
    259    n.command('new')
    260    n.command(string.format('lcd %s/test', t.paths.test_source_path))
    261 
    262    screen:expect {
    263      condition = function()
    264        eq(string.format('%s/test', t.paths.test_source_path), screen.pwd)
    265      end,
    266    }
    267 
    268    -- Move to the original window
    269    n.command('wincmd p')
    270 
    271    screen:expect {
    272      condition = function()
    273        eq(string.format('%s/src/nvim', t.paths.test_source_path), screen.pwd)
    274      end,
    275    }
    276 
    277    -- Change global cwd again
    278    n.command(string.format('cd %s', t.paths.test_source_path))
    279 
    280    screen:expect {
    281      condition = function()
    282        eq(t.paths.test_source_path, screen.pwd)
    283      end,
    284    }
    285  end)
    286 
    287  it('closing stdio with another remote UI does not leak memory #36392', function()
    288    clear({ args_rm = { '--headless' } })
    289    Screen.new()
    290    eq(1, #api.nvim_list_uis())
    291    local server = api.nvim_get_vvar('servername')
    292    local other_session = n.connect(server)
    293    Screen.new(nil, nil, nil, other_session)
    294    eq(2, #api.nvim_list_uis())
    295    check_close()
    296    other_session:close()
    297  end)
    298 end)
    299 
    300 describe('--embed --listen UI', function()
    301  it('waits for connection on listening address', function()
    302    clear()
    303    local child_server = assert(n.new_pipename())
    304    fn.jobstart({
    305      nvim_prog,
    306      '--embed',
    307      '--listen',
    308      child_server,
    309      '--clean',
    310      '--cmd',
    311      'colorscheme vim',
    312    })
    313    retry(nil, nil, function()
    314      neq(nil, uv.fs_stat(child_server))
    315    end)
    316 
    317    local child_session = n.connect(child_server)
    318 
    319    local info_ok, api_info = child_session:request('nvim_get_api_info')
    320    ok(info_ok)
    321    eq(2, #api_info)
    322    ok(api_info[1] > 2, 'channel_id > 2', api_info[1])
    323 
    324    child_session:request(
    325      'nvim_exec2',
    326      [[
    327      let g:evs = []
    328      autocmd UIEnter * call add(g:evs, $"UIEnter:{v:event.chan}")
    329      autocmd VimEnter * call add(g:evs, "VimEnter")
    330    ]],
    331      {}
    332    )
    333 
    334    -- VimEnter and UIEnter shouldn't be triggered until after attach
    335    local var_ok, var = child_session:request('nvim_get_var', 'evs')
    336    ok(var_ok)
    337    eq({}, var)
    338 
    339    local child_screen = Screen.new(40, 6, nil, child_session)
    340    child_screen:expect {
    341      grid = [[
    342      ^                                        |
    343      {1:~                                       }|*3
    344      {2:[No Name]             0,0-1          All}|
    345                                              |
    346    ]],
    347      attr_ids = {
    348        [1] = { foreground = Screen.colors.Blue, bold = true },
    349        [2] = { reverse = true, bold = true },
    350      },
    351    }
    352 
    353    -- VimEnter and UIEnter should now be triggered
    354    var_ok, var = child_session:request('nvim_get_var', 'evs')
    355    ok(var_ok)
    356    eq({ 'VimEnter', ('UIEnter:%d'):format(api_info[1]) }, var)
    357  end)
    358 end)