123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411 |
- local t = require('test.testutil')
- local n = require('test.functional.testnvim')()
- local Screen = require('test.functional.ui.screen')
- local eq = t.eq
- local exec_lua = n.exec_lua
- local clear = n.clear
- local feed = n.feed
- local fn = n.fn
- local assert_log = t.assert_log
- local check_close = n.check_close
- local testlog = 'Xtest_lua_ui_event_log'
- describe('vim.ui_attach', function()
- local screen
- before_each(function()
- clear()
- exec_lua [[
- ns = vim.api.nvim_create_namespace 'testspace'
- events = {}
- function on_event(event, ...)
- events[#events+1] = {event, ...}
- return true
- end
- function get_events()
- local ret_events = events
- events = {}
- return ret_events
- end
- ]]
- screen = Screen.new(40, 5)
- end)
- local function expect_events(expected)
- local evs = exec_lua 'return get_events(...)'
- eq(expected, evs, vim.inspect(evs))
- end
- it('can receive popupmenu events', function()
- exec_lua [[ vim.ui_attach(ns, {ext_popupmenu=true}, on_event) ]]
- feed('ifo')
- screen:expect {
- grid = [[
- fo^ |
- {1:~ }|*3
- {5:-- INSERT --} |
- ]],
- }
- fn.complete(1, { 'food', 'foobar', 'foo' })
- screen:expect {
- grid = [[
- food^ |
- {1:~ }|*3
- {5:-- INSERT --} |
- ]],
- }
- expect_events {
- {
- 'popupmenu_show',
- { { 'food', '', '', '' }, { 'foobar', '', '', '' }, { 'foo', '', '', '' } },
- 0,
- 0,
- 0,
- 1,
- },
- }
- feed '<c-n>'
- screen:expect {
- grid = [[
- foobar^ |
- {1:~ }|*3
- {5:-- INSERT --} |
- ]],
- }
- expect_events {
- { 'popupmenu_select', 1 },
- }
- feed '<c-y>'
- screen:expect_unchanged()
- expect_events {
- { 'popupmenu_hide' },
- }
- -- vim.ui_detach() stops events, and reenables builtin pum immediately
- exec_lua [[
- vim.ui_detach(ns)
- vim.fn.complete(1, {'food', 'foobar', 'foo'})
- ]]
- screen:expect {
- grid = [[
- food^ |
- {12:food }{1: }|
- {4:foobar }{1: }|
- {4:foo }{1: }|
- {5:-- INSERT --} |
- ]],
- }
- expect_events {}
- end)
- it('does not crash on exit', function()
- fn.system({
- n.nvim_prog,
- '-u',
- 'NONE',
- '-i',
- 'NONE',
- '--cmd',
- [[ lua ns = vim.api.nvim_create_namespace 'testspace' ]],
- '--cmd',
- [[ lua vim.ui_attach(ns, {ext_popupmenu=true}, function() end) ]],
- '--cmd',
- 'quitall!',
- })
- eq(0, n.eval('v:shell_error'))
- end)
- it('can receive accurate message kinds even if they are history', function()
- exec_lua([[
- vim.cmd.echomsg("'message1'")
- print('message2')
- vim.ui_attach(ns, { ext_messages = true }, on_event)
- vim.cmd.echomsg("'message3'")
- ]])
- feed(':messages<cr>')
- feed('<cr>')
- local actual = exec_lua([[
- return vim.tbl_filter(function (event)
- return event[1] == "msg_history_show"
- end, events)
- ]])
- eq({
- {
- 'msg_history_show',
- {
- { 'echomsg', { { 0, 'message1', 0 } } },
- { 'lua_print', { { 0, 'message2', 0 } } },
- { 'echomsg', { { 0, 'message3', 0 } } },
- },
- },
- }, actual, vim.inspect(actual))
- end)
- it('ui_refresh() activates correct capabilities without remote UI', function()
- screen:detach()
- exec_lua('vim.ui_attach(ns, { ext_cmdline = true }, on_event)')
- eq(1, n.api.nvim_get_option_value('cmdheight', {}))
- exec_lua('vim.ui_detach(ns)')
- exec_lua('vim.ui_attach(ns, { ext_messages = true }, on_event)')
- n.api.nvim_set_option_value('cmdheight', 1, {})
- screen:attach()
- eq(1, n.api.nvim_get_option_value('cmdheight', {}))
- end)
- it("ui_refresh() sets 'cmdheight' for all open tabpages with ext_messages", function()
- exec_lua('vim.cmd.tabnew()')
- exec_lua('vim.ui_attach(ns, { ext_messages = true }, on_event)')
- exec_lua('vim.cmd.tabnext()')
- eq(0, n.api.nvim_get_option_value('cmdheight', {}))
- end)
- it('avoids recursive flushing and invalid memory access with :redraw', function()
- exec_lua([[
- _G.cmdline = 0
- vim.ui_attach(ns, { ext_messages = true }, function(ev)
- if ev == 'msg_show' then
- vim.schedule(function() vim.cmd.redraw() end)
- else
- vim.cmd.redraw()
- end
- _G.cmdline = _G.cmdline + (ev == 'cmdline_show' and 1 or 0)
- end
- )]])
- feed(':')
- n.assert_alive()
- eq(2, exec_lua('return _G.cmdline'))
- n.assert_alive()
- feed('version<CR><CR>v<Esc>')
- n.assert_alive()
- end)
- it("preserved 'incsearch/command' screen state after :redraw from ext_cmdline", function()
- exec_lua([[
- vim.cmd.norm('ifoobar')
- vim.cmd('1split cmdline')
- local buf = vim.api.nvim_get_current_buf()
- vim.cmd.wincmd('p')
- vim.ui_attach(ns, { ext_cmdline = true }, function(event, ...)
- if event == 'cmdline_show' then
- local content = select(1, ...)
- vim.api.nvim_buf_set_lines(buf, -2, -1, false, {content[1][2]})
- vim.cmd('redraw')
- end
- return true
- end)
- ]])
- -- Updates a cmdline window
- feed(':cmdline')
- screen:expect({
- grid = [[
- cmdline |
- {2:cmdline [+] }|
- fooba^r |
- {3:[No Name] [+] }|
- |
- ]],
- })
- -- Does not clear 'incsearch' highlighting
- feed('<Esc>/foo')
- screen:expect({
- grid = [[
- foo |
- {2:cmdline [+] }|
- {2:foo}ba^r |
- {3:[No Name] [+] }|
- |
- ]],
- })
- -- Shows new cmdline state during 'inccommand'
- feed('<Esc>:%s/bar/baz')
- screen:expect({
- grid = [[
- %s/bar/baz |
- {2:cmdline [+] }|
- foo{10:ba^z} |
- {3:[No Name] [+] }|
- |
- ]],
- })
- end)
- it('msg_show in fast context', function()
- exec_lua([[
- vim.ui_attach(ns, { ext_messages = true }, function(event, _, content)
- if event == "msg_show" then
- vim.api.nvim_get_runtime_file("foo", false)
- -- non-"fast-api" is not allowed in msg_show callback and should be scheduled
- local _, err = pcall(vim.api.nvim_buf_set_lines, 0, -2, -1, false, { content[1][2] })
- pcall(vim.api.nvim__redraw, { flush = true })
- vim.schedule(function()
- vim.api.nvim_buf_set_lines(0, -2, -1, false, { content[1][2], err })
- end)
- end
- end)
- ]])
- -- "fast-api" does not prevent aborting :function
- feed(':func Foo()<cr>bar<cr>endf<cr>:func Foo()<cr>')
- screen:expect({
- grid = [[
- ^E122: Function Foo already exists, add !|
- to replace it |
- E5560: nvim_buf_set_lines must not be ca|
- lled in a fast event context |
- {1:~ }|
- ]],
- messages = {
- {
- content = { { 'E122: Function Foo already exists, add ! to replace it', 9, 7 } },
- kind = 'emsg',
- },
- },
- })
- -- No fast context for prompt message kinds
- feed(':%s/Function/Replacement/c<cr>')
- screen:expect({
- grid = [[
- ^E122: {10:Function} Foo already exists, add !|
- to replace it |
- replace with Replacement (y/n/a/q/l/^E/^|
- Y)? |
- {1:~ }|
- ]],
- messages = {
- {
- content = { { 'replace with Replacement (y/n/a/q/l/^E/^Y)?', 6, 19 } },
- kind = 'confirm_sub',
- },
- },
- })
- feed('<esc>:call inputlist(["Select:", "One", "Two"])<cr>')
- screen:expect({
- grid = [[
- E122: {10:Function} Foo already exists, add !|
- to replace it |
- Type number and <Enter> or click with th|
- e mouse (q or empty cancels): |
- {1:^~ }|
- ]],
- messages = {
- {
- content = { { 'Select:\nOne\nTwo\n' } },
- kind = 'list_cmd',
- },
- {
- content = { { 'Type number and <Enter> or click with the mouse (q or empty cancels): ' } },
- kind = 'number_prompt',
- },
- },
- })
- end)
- end)
- describe('vim.ui_attach', function()
- local screen
- before_each(function()
- clear({ env = { NVIM_LOG_FILE = testlog } })
- screen = Screen.new(40, 5)
- end)
- after_each(function()
- check_close()
- os.remove(testlog)
- end)
- it('error in callback is logged', function()
- exec_lua([[
- local ns = vim.api.nvim_create_namespace('testspace')
- vim.ui_attach(ns, { ext_popupmenu = true }, function() error(42) end)
- ]])
- feed('ifoo<CR>foobar<CR>fo<C-X><C-N>')
- assert_log('Error executing UI event callback: Error executing lua: .*: 42', testlog, 100)
- end)
- it('detaches after excessive errors', function()
- screen:add_extra_attr_ids({ [100] = { bold = true, foreground = Screen.colors.SeaGreen } })
- exec_lua([[
- vim.ui_attach(vim.api.nvim_create_namespace(''), { ext_messages = true }, function()
- vim.api.nvim_buf_set_lines(0, -2, -1, false, { err[1] })
- end)
- ]])
- screen:expect({
- grid = [[
- ^ |
- {1:~ }|*4
- ]],
- })
- feed('ifoo')
- screen:expect({
- grid = [[
- foo^ |
- {1:~ }|*4
- ]],
- showmode = { { '-- INSERT --', 5, 12 } },
- })
- feed('<esc>:1mes clear<cr>:mes<cr>')
- screen:expect({
- grid = [[
- foo |
- {3: }|
- {9:Excessive errors in vim.ui_attach() call}|
- {9:back from ns: 1.} |
- {100:Press ENTER or type command to continue}^ |
- ]],
- })
- feed('<cr>')
- -- Also when scheduled
- exec_lua([[
- vim.ui_attach(vim.api.nvim_create_namespace(''), { ext_messages = true }, function()
- vim.schedule(function() vim.api.nvim_buf_set_lines(0, -2, -1, false, { err[1] }) end)
- end)
- ]])
- screen:expect({
- any = 'fo^o',
- messages = {
- {
- content = {
- {
- 'Error executing vim.schedule lua callback: [string "<nvim>"]:2: attempt to index global \'err\' (a nil value)\nstack traceback:\n\t[string "<nvim>"]:2: in function <[string "<nvim>"]:2>',
- 9,
- 7,
- },
- },
- kind = 'lua_error',
- },
- {
- content = {
- {
- 'Error executing vim.schedule lua callback: [string "<nvim>"]:2: attempt to index global \'err\' (a nil value)\nstack traceback:\n\t[string "<nvim>"]:2: in function <[string "<nvim>"]:2>',
- 9,
- 7,
- },
- },
- kind = 'lua_error',
- },
- {
- content = { { 'Press ENTER or type command to continue', 100, 19 } },
- kind = 'return_prompt',
- },
- },
- })
- feed('<esc>:1mes clear<cr>:mes<cr>')
- screen:expect({
- grid = [[
- foo |
- {3: }|
- {9:Excessive errors in vim.ui_attach() call}|
- {9:back from ns: 2.} |
- {100:Press ENTER or type command to continue}^ |
- ]],
- })
- end)
- end)
|