diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2023-11-29 22:39:54 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2023-11-29 22:39:54 +0000 |
commit | 21cb7d04c387e4198ca8098a884c78b56ffcf4c2 (patch) | |
tree | 84fe5690df1551f0bb2bdfe1a13aacd29ebc1de7 /test/functional/terminal | |
parent | d9c904f85a23a496df4eb6be42aa43f007b22d50 (diff) | |
parent | 4a8bf24ac690004aedf5540fa440e788459e5e34 (diff) | |
download | rneovim-colorcolchar.tar.gz rneovim-colorcolchar.tar.bz2 rneovim-colorcolchar.zip |
Merge remote-tracking branch 'upstream/master' into colorcolcharcolorcolchar
Diffstat (limited to 'test/functional/terminal')
-rw-r--r-- | test/functional/terminal/api_spec.lua | 8 | ||||
-rw-r--r-- | test/functional/terminal/buffer_spec.lua | 158 | ||||
-rw-r--r-- | test/functional/terminal/channel_spec.lua | 96 | ||||
-rw-r--r-- | test/functional/terminal/cursor_spec.lua | 71 | ||||
-rw-r--r-- | test/functional/terminal/edit_spec.lua | 7 | ||||
-rw-r--r-- | test/functional/terminal/ex_terminal_spec.lua | 59 | ||||
-rw-r--r-- | test/functional/terminal/helpers.lua | 9 | ||||
-rw-r--r-- | test/functional/terminal/mouse_spec.lua | 78 | ||||
-rw-r--r-- | test/functional/terminal/scrollback_spec.lua | 75 | ||||
-rw-r--r-- | test/functional/terminal/tui_spec.lua | 923 | ||||
-rw-r--r-- | test/functional/terminal/window_spec.lua | 45 | ||||
-rw-r--r-- | test/functional/terminal/window_split_tab_spec.lua | 4 |
12 files changed, 1130 insertions, 403 deletions
diff --git a/test/functional/terminal/api_spec.lua b/test/functional/terminal/api_spec.lua index 724791343d..93641fc576 100644 --- a/test/functional/terminal/api_spec.lua +++ b/test/functional/terminal/api_spec.lua @@ -66,10 +66,10 @@ describe('api', function() screen:expect([[ [tui] insert-mode | - [socket 1] this is more t{4: }| - han 25 columns {4: }| - [socket 2] input{1: } {4: }| - {4:~ }| + [socket 1] this is more t | + han 25 columns | + [socket 2] input{1: } | + {4:~ } | {3:-- INSERT --} | {3:-- TERMINAL --} | ]]) diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua index 9c8b983ff7..6fcd029a5b 100644 --- a/test/functional/terminal/buffer_spec.lua +++ b/test/functional/terminal/buffer_spec.lua @@ -17,14 +17,14 @@ local sleep = helpers.sleep local funcs = helpers.funcs local is_os = helpers.is_os local skip = helpers.skip +local nvim_prog = helpers.nvim_prog describe(':terminal buffer', function() local screen before_each(function() clear() - feed_command('set modifiable swapfile undolevels=20') - poke_eventloop() + command('set modifiable swapfile undolevels=20') screen = thelpers.screen_setup() end) @@ -197,12 +197,11 @@ describe(':terminal buffer', function() it('handles loss of focus gracefully', function() -- Change the statusline to avoid printing the file name, which varies. - nvim('set_option', 'statusline', '==========') - feed_command('set laststatus=0') + nvim('set_option_value', 'statusline', '==========', {}) -- Save the buffer number of the terminal for later testing. local tbuf = eval('bufnr("%")') - local exitcmd = helpers.is_os('win') + local exitcmd = is_os('win') and "['cmd', '/c', 'exit']" or "['sh', '-c', 'exit']" source([[ @@ -231,8 +230,6 @@ describe(':terminal buffer', function() neq(tbuf, eval('bufnr("%")')) feed_command('quit!') -- Should exit the new window, not the terminal. eq(tbuf, eval('bufnr("%")')) - - feed_command('set laststatus=1') -- Restore laststatus to the default. end) it('term_close() use-after-free #4393', function() @@ -281,6 +278,7 @@ describe(':terminal buffer', function() end) it('requires bang (!) to close a running job #15402', function() + skip(is_os('win'), "Test freezes the CI and makes it time out") eq('Vim(wqall):E948: Job still running', exc_exec('wqall')) for _, cmd in ipairs({ 'bdelete', '%bdelete', 'bwipeout', 'bunload' }) do matches('^Vim%('..cmd:gsub('%%', '')..'%):E89: term://.*tty%-test.* will be killed %(add %! to override%)$', @@ -412,14 +410,6 @@ describe('on_lines does not emit out-of-bounds line indexes when', function() feed_command('bdelete!') eq('', exec_lua([[return _G.cb_error]])) end) - - it('runs TextChangedT event', function() - meths.set_var('called', 0) - command('autocmd TextChangedT * ++once let g:called = 1') - feed_command('terminal') - feed('iaa') - eq(1, meths.get_var('called')) - end) end) it('terminal truncates number of composing characters to 5', function() @@ -429,3 +419,141 @@ it('terminal truncates number of composing characters to 5', function() meths.chan_send(chan, 'a' .. composing:rep(8)) retry(nil, nil, function() eq('a' .. composing:rep(5), meths.get_current_line()) end) end) + +describe('terminal input', function() + before_each(function() + clear() + exec_lua([[ + _G.input_data = '' + vim.api.nvim_open_term(0, { on_input = function(_, _, _, data) + _G.input_data = _G.input_data .. data + end }) + ]]) + feed('i') + poke_eventloop() + end) + + it('<C-Space> is sent as NUL byte', function() + feed('aaa<C-Space>bbb') + eq('aaa\0bbb', exec_lua([[return _G.input_data]])) + end) + + it('unknown special keys are not sent', function() + feed('aaa<Help>bbb') + eq('aaabbb', exec_lua([[return _G.input_data]])) + end) +end) + +describe('terminal input', function() + it('sends various special keys with modifiers', function() + clear() + local screen = thelpers.screen_setup(0, + string.format([=[["%s", "-u", "NONE", "-i", "NONE", "--cmd", "startinsert"]]=], nvim_prog)) + screen:expect{grid=[[ + {1: } | + {4:~ }| + {4:~ }| + {4:~ }| + {5:[No Name] 0,1 All}| + {3:-- INSERT --} | + {3:-- TERMINAL --} | + ]]} + for _, key in ipairs({ + '<M-Tab>', '<M-CR>', '<M-Esc>', + '<BS>', '<S-Tab>', '<Insert>', '<Del>', '<PageUp>', '<PageDown>', + '<S-Up>', '<C-Up>', '<Up>', '<S-Down>', '<C-Down>', '<Down>', + '<S-Left>', '<C-Left>', '<Left>', '<S-Right>', '<C-Right>', '<Right>', + '<S-Home>', '<C-Home>', '<Home>', '<S-End>', '<C-End>', '<End>', + '<C-LeftMouse>', '<C-LeftRelease>', '<2-LeftMouse>', '<2-LeftRelease>', + '<S-RightMouse>', '<S-RightRelease>', '<2-RightMouse>', '<2-RightRelease>', + '<M-MiddleMouse>', '<M-MiddleRelease>', '<2-MiddleMouse>', '<2-MiddleRelease>', + '<S-ScrollWheelUp>', '<S-ScrollWheelDown>', '<ScrollWheelUp>', '<ScrollWheelDown>', + '<S-ScrollWheelLeft>', '<S-ScrollWheelRight>', '<ScrollWheelLeft>', '<ScrollWheelRight>', + }) do + feed('<CR><C-V>' .. key) + retry(nil, nil, function() eq(key, meths.get_current_line()) end) + end + end) +end) + +if is_os('win') then + describe(':terminal in Windows', function() + local screen + + before_each(function() + clear() + feed_command('set modifiable swapfile undolevels=20') + poke_eventloop() + local cmd = '["cmd.exe","/K","PROMPT=$g$s"]' + screen = thelpers.screen_setup(nil, cmd) + end) + + it('"put" operator sends data normally', function() + feed('<c-\\><c-n>G') + feed_command('let @a = ":: tty ready"') + feed_command('let @a = @a . "\\n:: appended " . @a . "\\n\\n"') + feed('"ap"ap') + screen:expect([[ + | + > :: tty ready | + > :: appended :: tty ready | + > :: tty ready | + > :: appended :: tty ready | + ^> {2: } | + :let @a = @a . "\n:: appended " . @a . "\n\n" | + ]]) + -- operator count is also taken into consideration + feed('3"ap') + screen:expect([[ + > :: appended :: tty ready | + > :: tty ready | + > :: appended :: tty ready | + > :: tty ready | + > :: appended :: tty ready | + ^> {2: } | + :let @a = @a . "\n:: appended " . @a . "\n\n" | + ]]) + end) + + it('":put" command sends data normally', function() + feed('<c-\\><c-n>G') + feed_command('let @a = ":: tty ready"') + feed_command('let @a = @a . "\\n:: appended " . @a . "\\n\\n"') + feed_command('put a') + screen:expect([[ + | + > :: tty ready | + > :: appended :: tty ready | + > {2: } | + | + ^ | + :put a | + ]]) + -- line argument is only used to move the cursor + feed_command('6put a') + screen:expect([[ + | + > :: tty ready | + > :: appended :: tty ready | + > :: tty ready | + > :: appended :: tty ready | + ^> {2: } | + :6put a | + ]]) + end) + end) +end + +describe('termopen()', function() + before_each(clear) + + it('disallowed when textlocked and in cmdwin buffer', function() + command("autocmd TextYankPost <buffer> ++once call termopen('foo')") + matches("Vim%(call%):E565: Not allowed to change text or change window$", + pcall_err(command, "normal! yy")) + + feed("q:") + eq("Vim:E11: Invalid in command-line window; <CR> executes, CTRL-C quits", + pcall_err(funcs.termopen, "bar")) + end) +end) diff --git a/test/functional/terminal/channel_spec.lua b/test/functional/terminal/channel_spec.lua index 2ca7cdb0a2..8510df5347 100644 --- a/test/functional/terminal/channel_spec.lua +++ b/test/functional/terminal/channel_spec.lua @@ -8,6 +8,10 @@ local pcall_err = helpers.pcall_err local feed = helpers.feed local poke_eventloop = helpers.poke_eventloop local is_os = helpers.is_os +local meths = helpers.meths +local async_meths = helpers.async_meths +local testprg = helpers.testprg +local assert_alive = helpers.assert_alive describe('terminal channel is closed and later released if', function() local screen @@ -24,6 +28,7 @@ describe('terminal channel is closed and later released if', function() -- channel hasn't been released yet eq("Vim(call):Can't send data to closed stream", pcall_err(command, [[bdelete! | call chansend(id, 'test')]])) + feed('<Ignore>') -- add input to separate two RPC requests -- channel has been released after one main loop iteration eq(chans - 1, eval('len(nvim_list_chans())')) end) @@ -40,6 +45,7 @@ describe('terminal channel is closed and later released if', function() feed('i<CR>') -- need to first process input poke_eventloop() + feed('<Ignore>') -- add input to separate two RPC requests -- channel has been released after another main loop iteration eq(chans - 1, eval('len(nvim_list_chans())')) end) @@ -55,6 +61,7 @@ describe('terminal channel is closed and later released if', function() -- channel still hasn't been released yet eq("Vim(call):Can't send data to closed stream", pcall_err(command, [[bdelete | call chansend(id, 'test')]])) + feed('<Ignore>') -- add input to separate two RPC requests -- channel has been released after one main loop iteration eq(chans - 1, eval('len(nvim_list_chans())')) end) @@ -72,6 +79,7 @@ describe('terminal channel is closed and later released if', function() feed('i<CR>') -- need to first process input poke_eventloop() + feed('<Ignore>') -- add input to separate two RPC requests -- channel has been released after another main loop iteration eq(chans - 1, eval('len(nvim_list_chans())')) end) @@ -89,21 +97,105 @@ describe('terminal channel is closed and later released if', function() -- channel still hasn't been released yet eq("Vim(call):Can't send data to closed stream", pcall_err(command, [[bdelete | call chansend(id, 'test')]])) + feed('<Ignore>') -- add input to separate two RPC requests -- channel has been released after one main loop iteration eq(chans - 1, eval('len(nvim_list_chans())')) end) end) it('chansend sends lines to terminal channel in proper order', function() - clear() + clear({args = {'--cmd', 'set laststatus=2'}}) local screen = Screen.new(100, 20) screen:attach() local shells = is_os('win') and {'cmd.exe', 'pwsh.exe -nop', 'powershell.exe -nop'} or {'sh'} for _, sh in ipairs(shells) do - command([[bdelete! | let id = termopen(']] .. sh .. [[')]]) + command([[let id = termopen(']] .. sh .. [[')]]) command([[call chansend(id, ['echo "hello"', 'echo "world"', ''])]]) screen:expect{ any=[[echo "hello".*echo "world"]] } + command('bdelete!') + screen:expect{ + any='%[No Name%]' + } end end) + +describe('no crash when TermOpen autocommand', function() + local screen + + before_each(function() + clear() + meths.set_option_value('shell', testprg('shell-test'), {}) + command('set shellcmdflag=EXE shellredir= shellpipe= shellquote= shellxquote=') + screen = Screen.new(60, 4) + screen:set_default_attr_ids({ + [0] = {bold = true, foreground = Screen.colors.Blue}; + }) + screen:attach() + end) + + it('processes job exit event when using termopen()', function() + command([[autocmd TermOpen * call input('')]]) + async_meths.command('terminal foobar') + screen:expect{grid=[[ + | + {0:~ }| + {0:~ }| + ^ | + ]]} + feed('<CR>') + screen:expect{grid=[[ + ^ready $ foobar | + | + [Process exited 0] | + | + ]]} + feed('i<CR>') + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }| + | + ]]} + assert_alive() + end) + + it('wipes buffer and processes events when using termopen()', function() + command([[autocmd TermOpen * bwipe! | call input('')]]) + async_meths.command('terminal foobar') + screen:expect{grid=[[ + | + {0:~ }| + {0:~ }| + ^ | + ]]} + feed('<CR>') + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }| + | + ]]} + assert_alive() + end) + + it('wipes buffer and processes events when using nvim_open_term()', function() + command([[autocmd TermOpen * bwipe! | call input('')]]) + async_meths.open_term(0, {}) + screen:expect{grid=[[ + | + {0:~ }| + {0:~ }| + ^ | + ]]} + feed('<CR>') + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }| + | + ]]} + assert_alive() + end) +end) diff --git a/test/functional/terminal/cursor_spec.lua b/test/functional/terminal/cursor_spec.lua index 98ac03211a..8285bcc26e 100644 --- a/test/functional/terminal/cursor_spec.lua +++ b/test/functional/terminal/cursor_spec.lua @@ -6,7 +6,7 @@ local testprg, command = helpers.testprg, helpers.command local nvim_prog = helpers.nvim_prog local eq, eval = helpers.eq, helpers.eval local matches = helpers.matches -local feed_command = helpers.feed_command +local poke_eventloop = helpers.poke_eventloop local hide_cursor = thelpers.hide_cursor local show_cursor = thelpers.show_cursor local is_os = helpers.is_os @@ -153,7 +153,8 @@ describe('cursor with customized highlighting', function() }) screen:attach({rgb=false}) command('call termopen(["'..testprg('tty-test')..'"])') - feed_command('startinsert') + feed('i') + poke_eventloop() end) it('overrides the default highlighting', function() @@ -187,13 +188,16 @@ describe('buffer cursor position is correct in terminal without number column', ..[[", "-u", "NONE", "-i", "NONE", "-E", "--cmd", "let @r = ']]..str..[['", ]] -- <Left> and <Right> don't always work ..[["--cmd", "cnoremap <C-X> <Left>", "--cmd", "cnoremap <C-O> <Right>"]]..']', 70) + -- Also check for real cursor position, as it is used for stuff like input methods + screen._handle_busy_start = function() end + screen._handle_busy_stop = function() end screen:expect([[ | | | | Entering Ex mode. Type "visual" to go to Normal mode. | - :{1: } | + :{1:^ } | {3:-- TERMINAL --} | ]]) end @@ -213,7 +217,7 @@ describe('buffer cursor position is correct in terminal without number column', | | Entering Ex mode. Type "visual" to go to Normal mode. | - :aaaaaaaa{1: } | + :aaaaaaaa{1:^ } | {3:-- TERMINAL --} | ]]) eq({6, 9}, eval('nvim_win_get_cursor(0)')) @@ -238,7 +242,7 @@ describe('buffer cursor position is correct in terminal without number column', | | Entering Ex mode. Type "visual" to go to Normal mode. | - :aaaaaa{1:a}a | + :aaaaaa{1:^a}a | {3:-- TERMINAL --} | ]]) eq({6, 7}, eval('nvim_win_get_cursor(0)')) @@ -263,7 +267,7 @@ describe('buffer cursor position is correct in terminal without number column', | | Entering Ex mode. Type "visual" to go to Normal mode. | - :a{1:a}aaaaaa | + :a{1:^a}aaaaaa | {3:-- TERMINAL --} | ]]) eq({6, 2}, eval('nvim_win_get_cursor(0)')) @@ -294,7 +298,7 @@ describe('buffer cursor position is correct in terminal without number column', | | Entering Ex mode. Type "visual" to go to Normal mode. | - :µµµµµµµµ{1: } | + :µµµµµµµµ{1:^ } | {3:-- TERMINAL --} | ]]) eq({6, 17}, eval('nvim_win_get_cursor(0)')) @@ -319,7 +323,7 @@ describe('buffer cursor position is correct in terminal without number column', | | Entering Ex mode. Type "visual" to go to Normal mode. | - :µµµµµµ{1:µ}µ | + :µµµµµµ{1:^µ}µ | {3:-- TERMINAL --} | ]]) eq({6, 13}, eval('nvim_win_get_cursor(0)')) @@ -344,7 +348,7 @@ describe('buffer cursor position is correct in terminal without number column', | | Entering Ex mode. Type "visual" to go to Normal mode. | - :µ{1:µ}µµµµµµ | + :µ{1:^µ}µµµµµµ | {3:-- TERMINAL --} | ]]) eq({6, 3}, eval('nvim_win_get_cursor(0)')) @@ -377,7 +381,7 @@ describe('buffer cursor position is correct in terminal without number column', | | Entering Ex mode. Type "visual" to go to Normal mode. | - :µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳{1: } | + :µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳{1:^ } | {3:-- TERMINAL --} | ]]) eq({6, 33}, eval('nvim_win_get_cursor(0)')) @@ -402,7 +406,7 @@ describe('buffer cursor position is correct in terminal without number column', | | Entering Ex mode. Type "visual" to go to Normal mode. | - :µ̳µ̳µ̳µ̳µ̳µ̳{1:µ̳}µ̳ | + :µ̳µ̳µ̳µ̳µ̳µ̳{1:^µ̳}µ̳ | {3:-- TERMINAL --} | ]]) eq({6, 25}, eval('nvim_win_get_cursor(0)')) @@ -427,7 +431,7 @@ describe('buffer cursor position is correct in terminal without number column', | | Entering Ex mode. Type "visual" to go to Normal mode. | - :µ̳{1:µ̳}µ̳µ̳µ̳µ̳µ̳µ̳ | + :µ̳{1:^µ̳}µ̳µ̳µ̳µ̳µ̳µ̳ | {3:-- TERMINAL --} | ]]) eq({6, 5}, eval('nvim_win_get_cursor(0)')) @@ -446,7 +450,7 @@ describe('buffer cursor position is correct in terminal without number column', end) describe('in a line with double-cell multibyte characters and no trailing spaces,', function() - skip(is_os('win'), "Encoding problem?") + if skip(is_os('win'), "Encoding problem?") then return end before_each(function() setup_ex_register('哦哦哦哦哦哦哦哦') @@ -460,7 +464,7 @@ describe('buffer cursor position is correct in terminal without number column', | | Entering Ex mode. Type "visual" to go to Normal mode. | - :哦哦哦哦哦哦哦哦{1: } | + :哦哦哦哦哦哦哦哦{1:^ } | {3:-- TERMINAL --} | ]]) eq({6, 25}, eval('nvim_win_get_cursor(0)')) @@ -485,7 +489,7 @@ describe('buffer cursor position is correct in terminal without number column', | | Entering Ex mode. Type "visual" to go to Normal mode. | - :哦哦哦哦哦哦{1:哦}哦 | + :哦哦哦哦哦哦{1:^哦}哦 | {3:-- TERMINAL --} | ]]) eq({6, 19}, eval('nvim_win_get_cursor(0)')) @@ -510,7 +514,7 @@ describe('buffer cursor position is correct in terminal without number column', | | Entering Ex mode. Type "visual" to go to Normal mode. | - :哦{1:哦}哦哦哦哦哦哦 | + :哦{1:^哦}哦哦哦哦哦哦 | {3:-- TERMINAL --} | ]]) eq({6, 4}, eval('nvim_win_get_cursor(0)')) @@ -537,7 +541,7 @@ describe('buffer cursor position is correct in terminal without number column', | | Entering Ex mode. Type "visual" to go to Normal mode. | - :aaaaaaaa {1: } | + :aaaaaaaa {1:^ } | {3:-- TERMINAL --} | ]]) matches('^:aaaaaaaa [ ]*$', eval('nvim_get_current_line()')) @@ -564,13 +568,16 @@ describe('buffer cursor position is correct in terminal with number column', fun ..[[", "-u", "NONE", "-i", "NONE", "-E", "--cmd", "let @r = ']]..str..[['", ]] -- <Left> and <Right> don't always work ..[["--cmd", "cnoremap <C-X> <Left>", "--cmd", "cnoremap <C-O> <Right>"]]..']', 70) + -- Also check for real cursor position, as it is used for stuff like input methods + screen._handle_busy_start = function() end + screen._handle_busy_stop = function() end screen:expect([[ {7: 1 } | {7: 2 } | {7: 3 } | {7: 4 } | {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | - {7: 6 }:{1: } | + {7: 6 }:{1:^ } | {3:-- TERMINAL --} | ]]) end @@ -593,7 +600,7 @@ describe('buffer cursor position is correct in terminal with number column', fun {7: 3 } | {7: 4 } | {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | - {7: 6 }:aaaaaaaa{1: } | + {7: 6 }:aaaaaaaa{1:^ } | {3:-- TERMINAL --} | ]]) eq({6, 9}, eval('nvim_win_get_cursor(0)')) @@ -618,7 +625,7 @@ describe('buffer cursor position is correct in terminal with number column', fun {7: 3 } | {7: 4 } | {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | - {7: 6 }:aaaaaa{1:a}a | + {7: 6 }:aaaaaa{1:^a}a | {3:-- TERMINAL --} | ]]) eq({6, 7}, eval('nvim_win_get_cursor(0)')) @@ -643,7 +650,7 @@ describe('buffer cursor position is correct in terminal with number column', fun {7: 3 } | {7: 4 } | {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | - {7: 6 }:a{1:a}aaaaaa | + {7: 6 }:a{1:^a}aaaaaa | {3:-- TERMINAL --} | ]]) eq({6, 2}, eval('nvim_win_get_cursor(0)')) @@ -674,7 +681,7 @@ describe('buffer cursor position is correct in terminal with number column', fun {7: 3 } | {7: 4 } | {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | - {7: 6 }:µµµµµµµµ{1: } | + {7: 6 }:µµµµµµµµ{1:^ } | {3:-- TERMINAL --} | ]]) eq({6, 17}, eval('nvim_win_get_cursor(0)')) @@ -699,7 +706,7 @@ describe('buffer cursor position is correct in terminal with number column', fun {7: 3 } | {7: 4 } | {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | - {7: 6 }:µµµµµµ{1:µ}µ | + {7: 6 }:µµµµµµ{1:^µ}µ | {3:-- TERMINAL --} | ]]) eq({6, 13}, eval('nvim_win_get_cursor(0)')) @@ -724,7 +731,7 @@ describe('buffer cursor position is correct in terminal with number column', fun {7: 3 } | {7: 4 } | {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | - {7: 6 }:µ{1:µ}µµµµµµ | + {7: 6 }:µ{1:^µ}µµµµµµ | {3:-- TERMINAL --} | ]]) eq({6, 3}, eval('nvim_win_get_cursor(0)')) @@ -757,7 +764,7 @@ describe('buffer cursor position is correct in terminal with number column', fun {7: 3 } | {7: 4 } | {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | - {7: 6 }:µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳{1: } | + {7: 6 }:µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳{1:^ } | {3:-- TERMINAL --} | ]]) eq({6, 33}, eval('nvim_win_get_cursor(0)')) @@ -782,7 +789,7 @@ describe('buffer cursor position is correct in terminal with number column', fun {7: 3 } | {7: 4 } | {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | - {7: 6 }:µ̳µ̳µ̳µ̳µ̳µ̳{1:µ̳}µ̳ | + {7: 6 }:µ̳µ̳µ̳µ̳µ̳µ̳{1:^µ̳}µ̳ | {3:-- TERMINAL --} | ]]) eq({6, 25}, eval('nvim_win_get_cursor(0)')) @@ -807,7 +814,7 @@ describe('buffer cursor position is correct in terminal with number column', fun {7: 3 } | {7: 4 } | {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | - {7: 6 }:µ̳{1:µ̳}µ̳µ̳µ̳µ̳µ̳µ̳ | + {7: 6 }:µ̳{1:^µ̳}µ̳µ̳µ̳µ̳µ̳µ̳ | {3:-- TERMINAL --} | ]]) eq({6, 5}, eval('nvim_win_get_cursor(0)')) @@ -826,7 +833,7 @@ describe('buffer cursor position is correct in terminal with number column', fun end) describe('in a line with double-cell multibyte characters and no trailing spaces,', function() - skip(is_os('win'), "Encoding problem?") + if skip(is_os('win'), "Encoding problem?") then return end before_each(function() setup_ex_register('哦哦哦哦哦哦哦哦') @@ -840,7 +847,7 @@ describe('buffer cursor position is correct in terminal with number column', fun {7: 3 } | {7: 4 } | {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | - {7: 6 }:哦哦哦哦哦哦哦哦{1: } | + {7: 6 }:哦哦哦哦哦哦哦哦{1:^ } | {3:-- TERMINAL --} | ]]) eq({6, 25}, eval('nvim_win_get_cursor(0)')) @@ -865,7 +872,7 @@ describe('buffer cursor position is correct in terminal with number column', fun {7: 3 } | {7: 4 } | {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | - {7: 6 }:哦哦哦哦哦哦{1:哦}哦 | + {7: 6 }:哦哦哦哦哦哦{1:^哦}哦 | {3:-- TERMINAL --} | ]]) eq({6, 19}, eval('nvim_win_get_cursor(0)')) @@ -890,7 +897,7 @@ describe('buffer cursor position is correct in terminal with number column', fun {7: 3 } | {7: 4 } | {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | - {7: 6 }:哦{1:哦}哦哦哦哦哦哦 | + {7: 6 }:哦{1:^哦}哦哦哦哦哦哦 | {3:-- TERMINAL --} | ]]) eq({6, 4}, eval('nvim_win_get_cursor(0)')) @@ -917,7 +924,7 @@ describe('buffer cursor position is correct in terminal with number column', fun {7: 3 } | {7: 4 } | {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | - {7: 6 }:aaaaaaaa {1: } | + {7: 6 }:aaaaaaaa {1:^ } | {3:-- TERMINAL --} | ]]) matches('^:aaaaaaaa [ ]*$', eval('nvim_get_current_line()')) diff --git a/test/functional/terminal/edit_spec.lua b/test/functional/terminal/edit_spec.lua index 80287bb3d0..29361bc0fa 100644 --- a/test/functional/terminal/edit_spec.lua +++ b/test/functional/terminal/edit_spec.lua @@ -21,8 +21,8 @@ describe(':edit term://*', function() before_each(function() clear() - meths.set_option('shell', testprg('shell-test')) - meths.set_option('shellcmdflag', 'EXE') + meths.set_option_value('shell', testprg('shell-test'), {}) + meths.set_option_value('shellcmdflag', 'EXE', {}) end) it('runs TermOpen event', function() @@ -36,11 +36,10 @@ describe(':edit term://*', function() end) it("runs TermOpen early enough to set buffer-local 'scrollback'", function() - if helpers.skip(helpers.is_os('win')) then return end local columns, lines = 20, 4 local scr = get_screen(columns, lines) local rep = 97 - meths.set_option('shellcmdflag', 'REP ' .. rep) + meths.set_option_value('shellcmdflag', 'REP ' .. rep, {}) command('set shellxquote=') -- win: avoid extra quotes local sb = 10 command('autocmd TermOpen * :setlocal scrollback='..tostring(sb) diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua index 6b7e93a864..f628e261a2 100644 --- a/test/functional/terminal/ex_terminal_spec.lua +++ b/test/functional/terminal/ex_terminal_spec.lua @@ -99,7 +99,7 @@ describe(':terminal', function() end) it('nvim_get_mode() in :terminal', function() - command(':terminal') + command('terminal') eq({ blocking=false, mode='nt' }, nvim('get_mode')) feed('i') eq({ blocking=false, mode='t' }, nvim('get_mode')) @@ -108,21 +108,37 @@ describe(':terminal', function() end) it(':stopinsert RPC request exits terminal-mode #7807', function() - command(':terminal') + command('terminal') feed('i[tui] insert-mode') eq({ blocking=false, mode='t' }, nvim('get_mode')) command('stopinsert') + feed('<Ignore>') -- Add input to separate two RPC requests eq({ blocking=false, mode='nt' }, nvim('get_mode')) end) it(':stopinsert in normal mode doesn\'t break insert mode #9889', function() - command(':terminal') + command('terminal') eq({ blocking=false, mode='nt' }, nvim('get_mode')) - command(':stopinsert') + command('stopinsert') + feed('<Ignore>') -- Add input to separate two RPC requests eq({ blocking=false, mode='nt' }, nvim('get_mode')) feed('a') eq({ blocking=false, mode='t' }, nvim('get_mode')) end) + + it('switching to terminal buffer in Insert mode goes to Terminal mode #7164', function() + command('terminal') + command('vnew') + feed('i') + command('let g:events = []') + command('autocmd InsertLeave * let g:events += ["InsertLeave"]') + command('autocmd TermEnter * let g:events += ["TermEnter"]') + command('inoremap <F2> <Cmd>wincmd p<CR>') + eq({ blocking=false, mode='i' }, nvim('get_mode')) + feed('<F2>') + eq({ blocking=false, mode='t' }, nvim('get_mode')) + eq({'InsertLeave', 'TermEnter'}, eval('g:events')) + end) end) describe(':terminal (with fake shell)', function() @@ -133,31 +149,37 @@ describe(':terminal (with fake shell)', function() screen = Screen.new(50, 4) screen:attach({rgb=false}) -- shell-test.c is a fake shell that prints its arguments and exits. - nvim('set_option', 'shell', testprg('shell-test')) - nvim('set_option', 'shellcmdflag', 'EXE') + nvim('set_option_value', 'shell', testprg('shell-test'), {}) + nvim('set_option_value', 'shellcmdflag', 'EXE', {}) + nvim('set_option_value', 'shellxquote', '', {}) end) -- Invokes `:terminal {cmd}` using a fake shell (shell-test.c) which prints - -- the {cmd} and exits immediately . + -- the {cmd} and exits immediately. + -- When no argument is given and the exit code is zero, the terminal buffer + -- closes automatically. local function terminal_with_fake_shell(cmd) feed_command("terminal "..(cmd and cmd or "")) end it('with no argument, acts like termopen()', function() skip(is_os('win')) - terminal_with_fake_shell() + -- Use the EXIT subcommand to end the process with a non-zero exit code to + -- prevent the buffer from closing automatically + nvim('set_option_value', 'shellcmdflag', 'EXIT', {}) + terminal_with_fake_shell(1) retry(nil, 4 * screen.timeout, function() screen:expect([[ - ^ready $ | - [Process exited 0] | + ^ | + [Process exited 1] | | - :terminal | + :terminal 1 | ]]) end) end) it("with no argument, and 'shell' is set to empty string", function() - nvim('set_option', 'shell', '') + nvim('set_option_value', 'shell', '', {}) terminal_with_fake_shell() screen:expect([[ ^ | @@ -169,7 +191,7 @@ describe(':terminal (with fake shell)', function() it("with no argument, but 'shell' has arguments, acts like termopen()", function() skip(is_os('win')) - nvim('set_option', 'shell', testprg('shell-test')..' -t jeff') + nvim('set_option_value', 'shell', testprg('shell-test')..' -t jeff', {}) terminal_with_fake_shell() screen:expect([[ ^jeff $ | @@ -193,7 +215,7 @@ describe(':terminal (with fake shell)', function() it("executes a given command through the shell, when 'shell' has arguments", function() skip(is_os('win')) - nvim('set_option', 'shell', testprg('shell-test')..' -t jeff') + nvim('set_option_value', 'shell', testprg('shell-test')..' -t jeff', {}) command('set shellxquote=') -- win: avoid extra quotes terminal_with_fake_shell('echo hi') screen:expect([[ @@ -243,12 +265,13 @@ describe(':terminal (with fake shell)', function() it('works with :find', function() skip(is_os('win')) - terminal_with_fake_shell() + nvim('set_option_value', 'shellcmdflag', 'EXIT', {}) + terminal_with_fake_shell(1) screen:expect([[ - ^ready $ | - [Process exited 0] | + ^ | + [Process exited 1] | | - :terminal | + :terminal 1 | ]]) eq('term://', string.match(eval('bufname("%")'), "^term://")) feed([[<C-\><C-N>]]) diff --git a/test/functional/terminal/helpers.lua b/test/functional/terminal/helpers.lua index 7247361649..62d3dd67a3 100644 --- a/test/functional/terminal/helpers.lua +++ b/test/functional/terminal/helpers.lua @@ -5,7 +5,7 @@ local helpers = require('test.functional.helpers')(nil) local Screen = require('test.functional.ui.screen') local testprg = helpers.testprg local exec_lua = helpers.exec_lua -local feed_command, nvim = helpers.feed_command, helpers.nvim +local nvim = helpers.nvim local function feed_data(data) if type(data) == 'table' then @@ -82,15 +82,16 @@ local function screen_setup(extra_rows, command, cols, opts) screen:attach(opts or {rgb=false}) - feed_command('enew | call termopen('..command..')') + nvim('command', 'enew | call termopen('..command..')') nvim('input', '<CR>') local vim_errmsg = nvim('eval', 'v:errmsg') if vim_errmsg and "" ~= vim_errmsg then error(vim_errmsg) end - feed_command('setlocal scrollback=10') - feed_command('startinsert') + nvim('command', 'setlocal scrollback=10') + nvim('command', 'startinsert') + nvim('input', '<Ignore>') -- Add input to separate two RPC requests -- tty-test puts the terminal into raw mode and echoes input. Tests work by -- feeding termcodes to control the display and asserting by screen:expect. diff --git a/test/functional/terminal/mouse_spec.lua b/test/functional/terminal/mouse_spec.lua index 50c8f5e7df..3291a38e03 100644 --- a/test/functional/terminal/mouse_spec.lua +++ b/test/functional/terminal/mouse_spec.lua @@ -11,7 +11,7 @@ describe(':terminal mouse', function() before_each(function() clear() - nvim('set_option', 'statusline', '==========') + nvim('set_option_value', 'statusline', '==========', {}) command('highlight StatusLine cterm=NONE') command('highlight StatusLineNC cterm=NONE') command('highlight VertSplit cterm=NONE') @@ -67,9 +67,23 @@ describe(':terminal mouse', function() eq('nt', eval('mode(1)')) end) - it('does not leave terminal mode on left-release', function() - skip(is_os('win')) - feed('<LeftRelease>') + it('will not exit focus on left-release', function() + eq('t', eval('mode(1)')) + feed('<LeftRelease><0,0>') + eq('t', eval('mode(1)')) + command('setlocal number') + eq('t', eval('mode(1)')) + feed('<LeftRelease><0,0>') + eq('t', eval('mode(1)')) + end) + + it('will not exit focus on mouse movement', function() + eq('t', eval('mode(1)')) + feed('<MouseMove><0,0>') + eq('t', eval('mode(1)')) + command('setlocal number') + eq('t', eval('mode(1)')) + feed('<MouseMove><0,0>') eq('t', eval('mode(1)')) end) @@ -232,7 +246,6 @@ describe(':terminal mouse', function() end) describe('with a split window and other buffer', function() - skip(is_os('win')) before_each(function() feed('<c-\\><c-n>:vsp<cr>') screen:expect([[ @@ -289,7 +302,7 @@ describe(':terminal mouse', function() ]]) end) - it("won't lose focus if another window is scrolled", function() + it("scrolling another window keeps focus and respects 'mousescroll'", function() feed('<ScrollWheelUp><4,0><ScrollWheelUp><4,0>') screen:expect([[ {7: 21 }line │line30 | @@ -310,22 +323,8 @@ describe(':terminal mouse', function() ========== ========== | {3:-- TERMINAL --} | ]]) - end) - - it("scrolling another window respects 'mousescroll'", function() - command('set mousescroll=ver:1') - feed('<ScrollWheelUp><4,0>') - screen:expect([[ - {7: 26 }line │line30 | - {7: 27 }line │rows: 5, cols: 25 | - {7: 28 }line │rows: 5, cols: 24 | - {7: 29 }line │mouse enabled | - {7: 30 }line │{1: } | - ========== ========== | - {3:-- TERMINAL --} | - ]]) command('set mousescroll=ver:10') - feed('<ScrollWheelUp><4,0>') + feed('<ScrollWheelUp><0,0>') screen:expect([[ {7: 16 }line │line30 | {7: 17 }line │rows: 5, cols: 25 | @@ -336,8 +335,41 @@ describe(':terminal mouse', function() {3:-- TERMINAL --} | ]]) command('set mousescroll=ver:0') - feed('<ScrollWheelUp><4,0>') + feed('<ScrollWheelUp><0,0>') screen:expect_unchanged() + feed([[<C-\><C-N><C-W>w]]) + command('setlocal nowrap') + feed('0<C-V>gg3ly$4p<C-W>wi') + screen:expect([[ + {7: 1 }linelinelinelineline │line30 | + {7: 2 }linelinelinelineline │rows: 5, cols: 25 | + {7: 3 }linelinelinelineline │rows: 5, cols: 24 | + {7: 4 }linelinelinelineline │mouse enabled | + {7: 5 }linelinelinelineline │{1: } | + ========== ========== | + {3:-- TERMINAL --} | + ]]) + feed('<ScrollWheelRight><4,0>') + screen:expect([[ + {7: 1 }nelinelineline │line30 | + {7: 2 }nelinelineline │rows: 5, cols: 25 | + {7: 3 }nelinelineline │rows: 5, cols: 24 | + {7: 4 }nelinelineline │mouse enabled | + {7: 5 }nelinelineline │{1: } | + ========== ========== | + {3:-- TERMINAL --} | + ]]) + command('set mousescroll=hor:4') + feed('<ScrollWheelLeft><4,0>') + screen:expect([[ + {7: 1 }nelinelinelineline │line30 | + {7: 2 }nelinelinelineline │rows: 5, cols: 25 | + {7: 3 }nelinelinelineline │rows: 5, cols: 24 | + {7: 4 }nelinelinelineline │mouse enabled | + {7: 5 }nelinelinelineline │{1: } | + ========== ========== | + {3:-- TERMINAL --} | + ]]) end) it('will lose focus if another window is clicked', function() @@ -354,7 +386,7 @@ describe(':terminal mouse', function() end) it('handles terminal size when switching buffers', function() - nvim('set_option', 'hidden', true) + nvim('set_option_value', 'hidden', true, {}) feed('<c-\\><c-n><c-w><c-w>') screen:expect([[ {7: 27 }line │line30 | diff --git a/test/functional/terminal/scrollback_spec.lua b/test/functional/terminal/scrollback_spec.lua index a4899c8219..1e278e4cff 100644 --- a/test/functional/terminal/scrollback_spec.lua +++ b/test/functional/terminal/scrollback_spec.lua @@ -2,13 +2,12 @@ local Screen = require('test.functional.ui.screen') local helpers = require('test.functional.helpers')(after_each) local thelpers = require('test.functional.terminal.helpers') local clear, eq, curbuf = helpers.clear, helpers.eq, helpers.curbuf -local feed, testprg, feed_command = helpers.feed, helpers.testprg, helpers.feed_command +local feed, testprg = helpers.feed, helpers.testprg local eval = helpers.eval local command = helpers.command -local matches = helpers.matches local poke_eventloop = helpers.poke_eventloop local retry = helpers.retry -local curbufmeths = helpers.curbufmeths +local meths = helpers.meths local nvim = helpers.nvim local feed_data = thelpers.feed_data local pcall_err = helpers.pcall_err @@ -140,7 +139,6 @@ describe(':terminal scrollback', function() describe('and height decreased by 1', function() - if skip(is_os('win')) then return end local function will_hide_top_line() feed([[<C-\><C-N>]]) screen:try_resize(screen._width - 2, screen._height - 1) @@ -347,12 +345,10 @@ end) describe(':terminal prints more lines than the screen height and exits', function() it('will push extra lines to scrollback', function() - skip(is_os('win')) clear() local screen = Screen.new(30, 7) screen:attach({rgb=false}) - feed_command(("call termopen(['%s', '10']) | startinsert"):format(testprg('tty-test'))) - poke_eventloop() + command(("call termopen(['%s', '10']) | startinsert"):format(testprg('tty-test'))) screen:expect([[ line6 | line7 | @@ -382,9 +378,7 @@ describe("'scrollback' option", function() end) local function set_fake_shell() - -- shell-test.c is a fake shell that prints its arguments and exits. - nvim('set_option', 'shell', testprg('shell-test')) - nvim('set_option', 'shellcmdflag', 'EXE') + nvim('set_option_value', 'shell', string.format('"%s" INTERACT', testprg('shell-test')), {}) end local function expect_lines(expected, epsilon) @@ -403,7 +397,7 @@ describe("'scrollback' option", function() screen = thelpers.screen_setup(nil, "['sh']", 30) end - curbufmeths.set_option('scrollback', 0) + meths.set_option_value('scrollback', 0, {}) feed_data(('%s REP 31 line%s'):format(testprg('shell-test'), is_os('win') and '\r' or '\n')) screen:expect{any='30: line '} retry(nil, nil, function() expect_lines(7) end) @@ -419,7 +413,7 @@ describe("'scrollback' option", function() screen = thelpers.screen_setup(nil, "['sh']", 30) end - curbufmeths.set_option('scrollback', 200) + meths.set_option_value('scrollback', 200, {}) -- Wait for prompt. screen:expect{any='%$'} @@ -428,10 +422,10 @@ describe("'scrollback' option", function() screen:expect{any='30: line '} retry(nil, nil, function() expect_lines(33, 2) end) - curbufmeths.set_option('scrollback', 10) + meths.set_option_value('scrollback', 10, {}) poke_eventloop() retry(nil, nil, function() expect_lines(16) end) - curbufmeths.set_option('scrollback', 10000) + meths.set_option_value('scrollback', 10000, {}) retry(nil, nil, function() expect_lines(16) end) -- Terminal job data is received asynchronously, may happen before the -- 'scrollback' option is synchronized with the internal sb_buffer. @@ -462,8 +456,8 @@ describe("'scrollback' option", function() expect_lines(58) -- Verify off-screen state - matches((is_os('win') and '^36: line[ ]*$' or '^35: line[ ]*$'), eval("getline(line('w0') - 1)")) - matches((is_os('win') and '^27: line[ ]*$' or '^26: line[ ]*$'), eval("getline(line('w0') - 10)")) + eq((is_os('win') and '36: line' or '35: line'), eval("getline(line('w0') - 1)->trim(' ', 2)")) + eq((is_os('win') and '27: line' or '26: line'), eval("getline(line('w0') - 10)->trim(' ', 2)")) end) it('deletes extra lines immediately', function() @@ -486,18 +480,18 @@ describe("'scrollback' option", function() ]]) local term_height = 6 -- Actual terminal screen height, not the scrollback -- Initial - local scrollback = curbufmeths.get_option('scrollback') + local scrollback = meths.get_option_value('scrollback', {}) eq(scrollback + term_height, eval('line("$")')) -- Reduction scrollback = scrollback - 2 - curbufmeths.set_option('scrollback', scrollback) + meths.set_option_value('scrollback', scrollback, {}) eq(scrollback + term_height, eval('line("$")')) end) it('defaults to 10000 in :terminal buffers', function() set_fake_shell() command('terminal') - eq(10000, curbufmeths.get_option('scrollback')) + eq(10000, meths.get_option_value('scrollback', {})) end) it('error if set to invalid value', function() @@ -509,7 +503,7 @@ describe("'scrollback' option", function() it('defaults to -1 on normal buffers', function() command('new') - eq(-1, curbufmeths.get_option('scrollback')) + eq(-1, meths.get_option_value('scrollback', {})) end) it(':setlocal in a :terminal buffer', function() @@ -518,45 +512,45 @@ describe("'scrollback' option", function() -- _Global_ scrollback=-1 defaults :terminal to 10_000. command('setglobal scrollback=-1') command('terminal') - eq(10000, curbufmeths.get_option('scrollback')) + eq(10000, meths.get_option_value('scrollback', {})) -- _Local_ scrollback=-1 in :terminal forces the _maximum_. command('setlocal scrollback=-1') retry(nil, nil, function() -- Fixup happens on refresh, not immediately. - eq(100000, curbufmeths.get_option('scrollback')) + eq(100000, meths.get_option_value('scrollback', {})) end) -- _Local_ scrollback=-1 during TermOpen forces the maximum. #9605 command('setglobal scrollback=-1') command('autocmd TermOpen * setlocal scrollback=-1') command('terminal') - eq(100000, curbufmeths.get_option('scrollback')) + eq(100000, meths.get_option_value('scrollback', {})) end) it(':setlocal in a normal buffer', function() command('new') -- :setlocal to -1. command('setlocal scrollback=-1') - eq(-1, curbufmeths.get_option('scrollback')) + eq(-1, meths.get_option_value('scrollback', {})) -- :setlocal to anything except -1. Currently, this just has no effect. command('setlocal scrollback=42') - eq(42, curbufmeths.get_option('scrollback')) + eq(42, meths.get_option_value('scrollback', {})) end) it(':set updates local value and global default', function() set_fake_shell() command('set scrollback=42') -- set global value - eq(42, curbufmeths.get_option('scrollback')) + eq(42, meths.get_option_value('scrollback', {})) command('terminal') - eq(42, curbufmeths.get_option('scrollback')) -- inherits global default + eq(42, meths.get_option_value('scrollback', {})) -- inherits global default command('setlocal scrollback=99') - eq(99, curbufmeths.get_option('scrollback')) + eq(99, meths.get_option_value('scrollback', {})) command('set scrollback<') -- reset to global default - eq(42, curbufmeths.get_option('scrollback')) + eq(42, meths.get_option_value('scrollback', {})) command('setglobal scrollback=734') -- new global default - eq(42, curbufmeths.get_option('scrollback')) -- local value did not change + eq(42, meths.get_option_value('scrollback', {})) -- local value did not change command('terminal') - eq(734, curbufmeths.get_option('scrollback')) + eq(734, meths.get_option_value('scrollback', {})) end) end) @@ -577,12 +571,12 @@ describe("pending scrollback line handling", function() it("does not crash after setting 'number' #14891", function() exec_lua [[ - local a = vim.api - local buf = a.nvim_create_buf(true, true) - local chan = a.nvim_open_term(buf, {}) - a.nvim_win_set_option(0, "number", true) - a.nvim_chan_send(chan, ("a\n"):rep(11) .. "a") - a.nvim_win_set_buf(0, buf) + local api = vim.api + local buf = api.nvim_create_buf(true, true) + local chan = api.nvim_open_term(buf, {}) + vim.wo.number = true + api.nvim_chan_send(chan, ("a\n"):rep(11) .. "a") + api.nvim_win_set_buf(0, buf) ]] screen:expect [[ {1: 1 }^a | @@ -609,12 +603,11 @@ describe("pending scrollback line handling", function() it("does not crash after nvim_buf_call #14891", function() skip(is_os('win')) exec_lua [[ - local a = vim.api - local bufnr = a.nvim_create_buf(false, true) - a.nvim_buf_call(bufnr, function() + local bufnr = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_call(bufnr, function() vim.fn.termopen({"echo", ("hi\n"):rep(11)}) end) - a.nvim_win_set_buf(0, bufnr) + vim.api.nvim_win_set_buf(0, bufnr) vim.cmd("startinsert") ]] screen:expect [[ diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index b28728057f..b17eed00f9 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -8,12 +8,12 @@ local helpers = require('test.functional.helpers')(after_each) local thelpers = require('test.functional.terminal.helpers') local Screen = require('test.functional.ui.screen') local eq = helpers.eq -local feed_command = helpers.feed_command local feed_data = thelpers.feed_data local clear = helpers.clear local command = helpers.command local dedent = helpers.dedent local exec = helpers.exec +local exec_lua = helpers.exec_lua local testprg = helpers.testprg local retry = helpers.retry local nvim_prog = helpers.nvim_prog @@ -27,10 +27,9 @@ local is_os = helpers.is_os local new_pipename = helpers.new_pipename local spawn_argv = helpers.spawn_argv local set_session = helpers.set_session -local feed = helpers.feed -local eval = helpers.eval +local write_file = helpers.write_file -if helpers.skip(helpers.is_os('win')) then return end +if helpers.skip(is_os('win')) then return end describe('TUI', function() local screen @@ -68,13 +67,13 @@ describe('TUI', function() local function expect_child_buf_lines(expected) assert(type({}) == type(expected)) retry(nil, nil, function() - local _, buflines = child_session:request( - 'nvim_buf_get_lines', 0, 0, -1, false) + local _, buflines = child_session:request('nvim_buf_get_lines', 0, 0, -1, false) eq(expected, buflines) end) end it('rapid resize #7572 #7628', function() + helpers.skip(helpers.is_asan(), 'Test extra unstable with ASAN. See #23762') -- Need buffer rows to provoke the behavior. feed_data(":edit test/functional/fixtures/bigfile.txt\n") screen:expect([[ @@ -108,14 +107,14 @@ describe('TUI', function() end) it('accepts resize while pager is active', function() - child_session:request("nvim_command", [[ - set more - func! ManyErr() - for i in range(10) - echoerr "FAIL ".i - endfor - endfunc - ]]) + child_session:request('nvim_exec2', [[ + set more + func! ManyErr() + for i in range(20) + echoerr "FAIL ".i + endfor + endfunc + ]], {}) feed_data(':call ManyErr()\r') screen:expect{grid=[[ {8:Error detected while processing function ManyErr:} | @@ -127,7 +126,35 @@ describe('TUI', function() {3:-- TERMINAL --} | ]]} - feed_data('d') + screen:try_resize(50,10) + screen:expect{grid=[[ + :call ManyErr() | + {8:Error detected while processing function ManyErr:} | + {11:line 2:} | + {8:FAIL 0} | + {8:FAIL 1} | + {8:FAIL 2} | + | + | + {10:-- More --}{1: } | + {3:-- TERMINAL --} | + ]]} + + feed_data('j') + screen:expect{grid=[[ + {8:Error detected while processing function ManyErr:} | + {11:line 2:} | + {8:FAIL 0} | + {8:FAIL 1} | + {8:FAIL 2} | + {8:FAIL 3} | + {8:FAIL 4} | + {8:FAIL 5} | + {10:-- More --}{1: } | + {3:-- TERMINAL --} | + ]]} + + screen:try_resize(50,7) screen:expect{grid=[[ {8:FAIL 1} | {8:FAIL 2} | @@ -211,7 +238,7 @@ describe('TUI', function() it('interprets leading <Esc> byte as ALT modifier in normal-mode', function() local keys = 'dfghjkl' for c in keys:gmatch('.') do - feed_command('nnoremap <a-'..c..'> ialt-'..c..'<cr><esc>') + feed_data(':nnoremap <a-'..c..'> ialt-'..c..'<cr><esc>\r') feed_data('\027'..c) end screen:expect([[ @@ -251,9 +278,11 @@ describe('TUI', function() end) it('interprets <Esc>[27u as <Esc>', function() - feed_command('nnoremap <M-;> <Nop>') - feed_command('nnoremap <Esc> AESC<Esc>') - feed_command('nnoremap ; Asemicolon<Esc>') + child_session:request('nvim_exec2', [[ + nnoremap <M-;> <Nop> + nnoremap <Esc> AESC<Esc> + nnoremap ; Asemicolon<Esc> + ]], {}) feed_data('\027[27u;') screen:expect([[ ESCsemicolo{1:n} | @@ -290,22 +319,22 @@ describe('TUI', function() local attrs = screen:get_default_attr_ids() attrs[11] = {foreground = 81} screen:expect([[ - {11:^G^V^M}{1: } | - {4:~ }| - {4:~ }| - {4:~ }| - {5:[No Name] [+] }| - {3:-- INSERT --} | - {3:-- TERMINAL --} | + {11:^G^V^M}{1: } | + {4:~ }| + {4:~ }| + {4:~ }| + {5:[No Name] [+] }| + {3:-- INSERT --} | + {3:-- TERMINAL --} | ]], attrs) end) - it('accepts mouse wheel events #19992', function() - child_session:request('nvim_command', [[ + local function test_mouse_wheel(esc) + child_session:request('nvim_exec2', [[ set number nostartofline nowrap mousescroll=hor:1,ver:1 call setline(1, repeat([join(range(10), '----')], 10)) vsplit - ]]) + ]], {}) screen:expect([[ {11: 1 }{1:0}----1----2----3----4│{11: 1 }0----1----2----3----| {11: 2 }0----1----2----3----4│{11: 2 }0----1----2----3----| @@ -316,7 +345,11 @@ describe('TUI', function() {3:-- TERMINAL --} | ]]) -- <ScrollWheelDown> in active window - feed_data('\027[<65;8;1M') + if esc then + feed_data('\027[<65;8;1M') + else + meths.input_mouse('wheel', 'down', '', 0, 0, 7) + end screen:expect([[ {11: 2 }{1:0}----1----2----3----4│{11: 1 }0----1----2----3----| {11: 3 }0----1----2----3----4│{11: 2 }0----1----2----3----| @@ -327,7 +360,11 @@ describe('TUI', function() {3:-- TERMINAL --} | ]]) -- <ScrollWheelDown> in inactive window - feed_data('\027[<65;48;1M') + if esc then + feed_data('\027[<65;48;1M') + else + meths.input_mouse('wheel', 'down', '', 0, 0, 47) + end screen:expect([[ {11: 2 }{1:0}----1----2----3----4│{11: 2 }0----1----2----3----| {11: 3 }0----1----2----3----4│{11: 3 }0----1----2----3----| @@ -338,7 +375,11 @@ describe('TUI', function() {3:-- TERMINAL --} | ]]) -- <ScrollWheelRight> in active window - feed_data('\027[<67;8;1M') + if esc then + feed_data('\027[<67;8;1M') + else + meths.input_mouse('wheel', 'right', '', 0, 0, 7) + end screen:expect([[ {11: 2 }{1:-}---1----2----3----4-│{11: 2 }0----1----2----3----| {11: 3 }----1----2----3----4-│{11: 3 }0----1----2----3----| @@ -349,7 +390,11 @@ describe('TUI', function() {3:-- TERMINAL --} | ]]) -- <ScrollWheelRight> in inactive window - feed_data('\027[<67;48;1M') + if esc then + feed_data('\027[<67;48;1M') + else + meths.input_mouse('wheel', 'right', '', 0, 0, 47) + end screen:expect([[ {11: 2 }{1:-}---1----2----3----4-│{11: 2 }----1----2----3----4| {11: 3 }----1----2----3----4-│{11: 3 }----1----2----3----4| @@ -360,7 +405,11 @@ describe('TUI', function() {3:-- TERMINAL --} | ]]) -- <S-ScrollWheelDown> in active window - feed_data('\027[<69;8;1M') + if esc then + feed_data('\027[<69;8;1M') + else + meths.input_mouse('wheel', 'down', 'S', 0, 0, 7) + end screen:expect([[ {11: 5 }{1:-}---1----2----3----4-│{11: 2 }----1----2----3----4| {11: 6 }----1----2----3----4-│{11: 3 }----1----2----3----4| @@ -371,7 +420,11 @@ describe('TUI', function() {3:-- TERMINAL --} | ]]) -- <S-ScrollWheelDown> in inactive window - feed_data('\027[<69;48;1M') + if esc then + feed_data('\027[<69;48;1M') + else + meths.input_mouse('wheel', 'down', 'S', 0, 0, 47) + end screen:expect([[ {11: 5 }{1:-}---1----2----3----4-│{11: 5 }----1----2----3----4| {11: 6 }----1----2----3----4-│{11: 6 }----1----2----3----4| @@ -382,7 +435,11 @@ describe('TUI', function() {3:-- TERMINAL --} | ]]) -- <S-ScrollWheelRight> in active window - feed_data('\027[<71;8;1M') + if esc then + feed_data('\027[<71;8;1M') + else + meths.input_mouse('wheel', 'right', 'S', 0, 0, 7) + end screen:expect([[ {11: 5 }{1:-}---6----7----8----9 │{11: 5 }----1----2----3----4| {11: 6 }----6----7----8----9 │{11: 6 }----1----2----3----4| @@ -393,7 +450,11 @@ describe('TUI', function() {3:-- TERMINAL --} | ]]) -- <S-ScrollWheelRight> in inactive window - feed_data('\027[<71;48;1M') + if esc then + feed_data('\027[<71;48;1M') + else + meths.input_mouse('wheel', 'right', 'S', 0, 0, 47) + end screen:expect([[ {11: 5 }{1:-}---6----7----8----9 │{11: 5 }5----6----7----8----| {11: 6 }----6----7----8----9 │{11: 6 }5----6----7----8----| @@ -404,7 +465,11 @@ describe('TUI', function() {3:-- TERMINAL --} | ]]) -- <ScrollWheelUp> in active window - feed_data('\027[<64;8;1M') + if esc then + feed_data('\027[<64;8;1M') + else + meths.input_mouse('wheel', 'up', '', 0, 0, 7) + end screen:expect([[ {11: 4 }----6----7----8----9 │{11: 5 }5----6----7----8----| {11: 5 }{1:-}---6----7----8----9 │{11: 6 }5----6----7----8----| @@ -415,7 +480,11 @@ describe('TUI', function() {3:-- TERMINAL --} | ]]) -- <ScrollWheelUp> in inactive window - feed_data('\027[<64;48;1M') + if esc then + feed_data('\027[<64;48;1M') + else + meths.input_mouse('wheel', 'up', '', 0, 0, 47) + end screen:expect([[ {11: 4 }----6----7----8----9 │{11: 4 }5----6----7----8----| {11: 5 }{1:-}---6----7----8----9 │{11: 5 }5----6----7----8----| @@ -426,7 +495,11 @@ describe('TUI', function() {3:-- TERMINAL --} | ]]) -- <ScrollWheelLeft> in active window - feed_data('\027[<66;8;1M') + if esc then + feed_data('\027[<66;8;1M') + else + meths.input_mouse('wheel', 'left', '', 0, 0, 7) + end screen:expect([[ {11: 4 }5----6----7----8----9│{11: 4 }5----6----7----8----| {11: 5 }5{1:-}---6----7----8----9│{11: 5 }5----6----7----8----| @@ -437,7 +510,11 @@ describe('TUI', function() {3:-- TERMINAL --} | ]]) -- <ScrollWheelLeft> in inactive window - feed_data('\027[<66;48;1M') + if esc then + feed_data('\027[<66;48;1M') + else + meths.input_mouse('wheel', 'left', '', 0, 0, 47) + end screen:expect([[ {11: 4 }5----6----7----8----9│{11: 4 }-5----6----7----8---| {11: 5 }5{1:-}---6----7----8----9│{11: 5 }-5----6----7----8---| @@ -448,7 +525,11 @@ describe('TUI', function() {3:-- TERMINAL --} | ]]) -- <S-ScrollWheelUp> in active window - feed_data('\027[<68;8;1M') + if esc then + feed_data('\027[<68;8;1M') + else + meths.input_mouse('wheel', 'up', 'S', 0, 0, 7) + end screen:expect([[ {11: 1 }5----6----7----8----9│{11: 4 }-5----6----7----8---| {11: 2 }5----6----7----8----9│{11: 5 }-5----6----7----8---| @@ -459,7 +540,11 @@ describe('TUI', function() {3:-- TERMINAL --} | ]]) -- <S-ScrollWheelUp> in inactive window - feed_data('\027[<68;48;1M') + if esc then + feed_data('\027[<68;48;1M') + else + meths.input_mouse('wheel', 'up', 'S', 0, 0, 47) + end screen:expect([[ {11: 1 }5----6----7----8----9│{11: 1 }-5----6----7----8---| {11: 2 }5----6----7----8----9│{11: 2 }-5----6----7----8---| @@ -470,7 +555,11 @@ describe('TUI', function() {3:-- TERMINAL --} | ]]) -- <S-ScrollWheelLeft> in active window - feed_data('\027[<70;8;1M') + if esc then + feed_data('\027[<70;8;1M') + else + meths.input_mouse('wheel', 'left', 'S', 0, 0, 7) + end screen:expect([[ {11: 1 }0----1----2----3----4│{11: 1 }-5----6----7----8---| {11: 2 }0----1----2----3----4│{11: 2 }-5----6----7----8---| @@ -481,7 +570,11 @@ describe('TUI', function() {3:-- TERMINAL --} | ]]) -- <S-ScrollWheelLeft> in inactive window - feed_data('\027[<70;48;1M') + if esc then + feed_data('\027[<70;48;1M') + else + meths.input_mouse('wheel', 'left', 'S', 0, 0, 47) + end screen:expect([[ {11: 1 }0----1----2----3----4│{11: 1 }0----1----2----3----| {11: 2 }0----1----2----3----4│{11: 2 }0----1----2----3----| @@ -491,6 +584,136 @@ describe('TUI', function() | {3:-- TERMINAL --} | ]]) + end + + describe('accepts mouse wheel events', function() + it('(mouse events sent to host)', function() + test_mouse_wheel(false) + end) + + it('(escape sequences sent to child)', function() + test_mouse_wheel(true) + end) + end) + + local function test_mouse_popup(esc) + child_session:request('nvim_exec2', [[ + call setline(1, 'popup menu test') + set mouse=a mousemodel=popup + + aunmenu PopUp + menu PopUp.foo :let g:menustr = 'foo'<CR> + menu PopUp.bar :let g:menustr = 'bar'<CR> + menu PopUp.baz :let g:menustr = 'baz'<CR> + highlight Pmenu ctermbg=NONE ctermfg=NONE cterm=underline,reverse + highlight PmenuSel ctermbg=NONE ctermfg=NONE cterm=underline,reverse,bold + ]], {}) + if esc then + feed_data('\027[<2;5;1M') + else + meths.input_mouse('right', 'press', '', 0, 0, 4) + end + screen:expect([[ + {1:p}opup menu test | + {4:~ }{13: foo }{4: }| + {4:~ }{13: bar }{4: }| + {4:~ }{13: baz }{4: }| + {5:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + if esc then + feed_data('\027[<2;5;1m') + else + meths.input_mouse('right', 'release', '', 0, 0, 4) + end + screen:expect_unchanged() + if esc then + feed_data('\027[<35;7;4M') + else + meths.input_mouse('move', '', '', 0, 3, 6) + end + screen:expect([[ + {1:p}opup menu test | + {4:~ }{13: foo }{4: }| + {4:~ }{13: bar }{4: }| + {4:~ }{14: baz }{4: }| + {5:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + if esc then + feed_data('\027[<0;7;3M') + else + meths.input_mouse('left', 'press', '', 0, 2, 6) + end + screen:expect([[ + {1:p}opup menu test | + {4:~ }| + {4:~ }| + {4:~ }| + {5:[No Name] [+] }| + :let g:menustr = 'bar' | + {3:-- TERMINAL --} | + ]]) + if esc then + feed_data('\027[<0;7;3m') + else + meths.input_mouse('left', 'release', '', 0, 2, 6) + end + screen:expect_unchanged() + if esc then + feed_data('\027[<2;45;3M') + else + meths.input_mouse('right', 'press', '', 0, 2, 44) + end + screen:expect([[ + {1:p}opup menu test | + {4:~ }| + {4:~ }| + {4:~ }{13: foo }{4: }| + {5:[No Name] [+] }{13: bar }{5: }| + :let g:menustr = 'bar' {13: baz } | + {3:-- TERMINAL --} | + ]]) + if esc then + feed_data('\027[<34;48;6M') + else + meths.input_mouse('right', 'drag', '', 0, 5, 47) + end + screen:expect([[ + {1:p}opup menu test | + {4:~ }| + {4:~ }| + {4:~ }{13: foo }{4: }| + {5:[No Name] [+] }{13: bar }{5: }| + :let g:menustr = 'bar' {14: baz } | + {3:-- TERMINAL --} | + ]]) + if esc then + feed_data('\027[<2;48;6m') + else + meths.input_mouse('right', 'release', '', 0, 5, 47) + end + screen:expect([[ + {1:p}opup menu test | + {4:~ }| + {4:~ }| + {4:~ }| + {5:[No Name] [+] }| + :let g:menustr = 'baz' | + {3:-- TERMINAL --} | + ]]) + end + + describe('mouse events work with right-click menu', function() + it('(mouse events sent to host)', function() + test_mouse_popup(false) + end) + + it('(escape sequences sent to child)', function() + test_mouse_popup(true) + end) end) it('accepts keypad keys from kitty keyboard protocol #19180', function() @@ -631,11 +854,11 @@ describe('TUI', function() | {3:-- TERMINAL --} | ]]) - child_session:request('nvim_command', [[ + child_session:request('nvim_exec2', [[ tab split tabnew highlight Tabline ctermbg=NONE ctermfg=NONE cterm=underline - ]]) + ]], {}) screen:expect([[ {12: + [No Name] + [No Name] }{3: [No Name] }{1: }{12:X}| {1: } | @@ -667,52 +890,26 @@ describe('TUI', function() ]]) end) - it('mouse events work with right-click menu', function() - child_session:request('nvim_command', [[ - call setline(1, 'popup menu test') - set mouse=a mousemodel=popup - - aunmenu PopUp - menu PopUp.foo :let g:menustr = 'foo'<CR> - menu PopUp.bar :let g:menustr = 'bar'<CR> - menu PopUp.baz :let g:menustr = 'baz'<CR> - highlight Pmenu ctermbg=NONE ctermfg=NONE cterm=underline,reverse - highlight PmenuSel ctermbg=NONE ctermfg=NONE cterm=underline,reverse,bold - ]]) - meths.input_mouse('right', 'press', '', 0, 0, 4) - screen:expect([[ - {1:p}opup menu test | - {4:~ }{13: foo }{4: }| - {4:~ }{13: bar }{4: }| - {4:~ }{13: baz }{4: }| - {5:[No Name] [+] }| - | - {3:-- TERMINAL --} | - ]]) - meths.input_mouse('right', 'release', '', 0, 0, 4) - screen:expect_unchanged() - meths.input_mouse('move', '', '', 0, 3, 6) - screen:expect([[ - {1:p}opup menu test | - {4:~ }{13: foo }{4: }| - {4:~ }{13: bar }{4: }| - {4:~ }{14: baz }{4: }| - {5:[No Name] [+] }| - | - {3:-- TERMINAL --} | - ]]) - meths.input_mouse('left', 'press', '', 0, 2, 6) + it('supports Super and Meta modifiers', function() + feed_data('i') + feed_data('\022\027[106;9u') -- Super + j + feed_data('\022\027[107;33u') -- Meta + k + feed_data('\022\027[13;41u') -- Super + Meta + Enter + feed_data('\022\027[127;48u') -- Shift + Alt + Ctrl + Super + Meta + Backspace + feed_data('\n') + feed_data('\022\027[57376;9u') -- Super + F13 + feed_data('\022\027[57377;33u') -- Meta + F14 + feed_data('\022\027[57378;41u') -- Super + Meta + F15 + feed_data('\022\027[57379;48u') -- Shift + Alt + Ctrl + Super + Meta + F16 screen:expect([[ - {1:p}opup menu test | - {4:~ }| + <D-j><T-k><T-D-CR><M-T-C-S-D-BS> | + <D-F13><T-F14><T-D-F15><M-T-C-S-D-F16>{1: } | {4:~ }| {4:~ }| {5:[No Name] [+] }| - :let g:menustr = 'bar' | + {3:-- INSERT --} | {3:-- TERMINAL --} | ]]) - meths.input_mouse('left', 'release', '', 0, 2, 6) - screen:expect_unchanged() end) it('paste: Insert mode', function() @@ -752,8 +949,7 @@ describe('TUI', function() ]]) -- Dot-repeat/redo. feed_data('2.') - expect_child_buf_lines( - {'"pasted from terminapasted from terminalpasted from terminall"'}) + expect_child_buf_lines({'"pasted from terminapasted from terminalpasted from terminall"'}) screen:expect([[ "pasted from terminapasted from terminalpasted fro| m termina{1:l}l" | @@ -806,13 +1002,13 @@ describe('TUI', function() 'this is line 2', 'line 3 is here', '', - } + } -- Redo. feed_data('\18') -- <C-r> expect_child_buf_lines{ 'thisjust paste it™3 is here', '', - } + } end) it('paste: terminal mode', function() @@ -874,17 +1070,15 @@ describe('TUI', function() screen:expect{grid=expected_grid1, attr_ids=expected_attr} -- Dot-repeat/redo. feed_data('.') - screen:expect{ - grid=[[ - ESC:{11:^[} / CR: | - xline 1 | - ESC:{11:^[} / CR: | - {1:x} | - {5:[No Name] [+] 5,1 Bot}| - | - {3:-- TERMINAL --} | - ]], - attr_ids=expected_attr} + screen:expect{grid=[[ + ESC:{11:^[} / CR: | + xline 1 | + ESC:{11:^[} / CR: | + {1:x} | + {5:[No Name] [+] 5,1 Bot}| + | + {3:-- TERMINAL --} | + ]], attr_ids=expected_attr} -- Undo. feed_data('u') expect_child_buf_lines(expected_crlf) @@ -1171,8 +1365,7 @@ describe('TUI', function() {5:[No Name] [+] }| {3:-- INSERT --} | {3:-- TERMINAL --} | - ]], - attr_ids={ + ]], attr_ids={ [1] = {reverse = true}, [2] = {background = tonumber('0x00000b')}, [3] = {bold = true}, @@ -1351,7 +1544,7 @@ describe('TUI', function() it('forwards :term palette colors with termguicolors', function() if is_ci('github') then - pending("tty-test complains about not owning the terminal -- actions/runner#241") + pending("tty-test complains about not owning the terminal -- actions/runner#241") end screen:set_rgb_cterm(true) screen:set_default_attr_ids({ @@ -1397,24 +1590,41 @@ describe('TUI', function() ]]} end) - it('is included in nvim_list_uis()', function() - feed_data(':echo map(nvim_list_uis(), {k,v -> sort(items(filter(v, {k,v -> k[:3] !=# "ext_" })))})\r') - screen:expect([=[ - | - {4:~ }| - {5: }| - [[['chan', 1], ['height', 6], ['override', v:false| - ], ['rgb', v:false], ['width', 50]]] | - {10:Press ENTER or type command to continue}{1: } | - {3:-- TERMINAL --} | - ]=]) + it('in nvim_list_uis()', function() + -- $TERM in :terminal. + local exp_term = is_os('bsd') and 'builtin_xterm' or 'xterm-256color' + local expected = { + { + chan = 1, + ext_cmdline = false, + ext_hlstate = false, + ext_linegrid = true, + ext_messages = false, + ext_multigrid = false, + ext_popupmenu = false, + ext_tabline = false, + ext_termcolors = true, + ext_wildmenu = false, + height = 6, + override = false, + rgb = false, + stdin_tty = true, + stdout_tty = true, + term_background = '', + term_colors = 256, + term_name = exp_term, + width = 50 + }, + } + local _, rv = child_session:request('nvim_list_uis') + eq(expected, rv) end) it('allows grid to assume wider ambiguous-width characters than host terminal #19686', function() child_session:request('nvim_buf_set_lines', 0, 0, -1, true, { ('℃'):rep(60), ('℃'):rep(60) }) - child_session:request('nvim_win_set_option', 0, 'cursorline', true) - child_session:request('nvim_win_set_option', 0, 'list', true) - child_session:request('nvim_win_set_option', 0, 'listchars', 'eol:$') + child_session:request('nvim_set_option_value', 'cursorline', true, {}) + child_session:request('nvim_set_option_value', 'list', true, {}) + child_session:request('nvim_set_option_value', 'listchars', 'eol:$', {win=0}) feed_data('gg') local singlewidth_screen = [[ {13:℃}{12:℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃}| @@ -1425,21 +1635,21 @@ describe('TUI', function() | {3:-- TERMINAL --} | ]] - -- When grid assumes "℃" to be double-width but host terminal assumes it to be single-width, the - -- second cell of "℃" is a space and the attributes of the "℃" are applied to it. + -- When grid assumes "℃" to be double-width but host terminal assumes it to be single-width, + -- the second cell of "℃" is a space and the attributes of the "℃" are applied to it. local doublewidth_screen = [[ {13:℃}{12: ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ }| {12:℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ }| {12:℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ }{15:$}{12: }| - ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ >{4:@@@}| + ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ {4:@@@@}| {5:[No Name] [+] }| | {3:-- TERMINAL --} | ]] screen:expect(singlewidth_screen) - child_session:request('nvim_set_option', 'ambiwidth', 'double') + child_session:request('nvim_set_option_value', 'ambiwidth', 'double', {}) screen:expect(doublewidth_screen) - child_session:request('nvim_set_option', 'ambiwidth', 'single') + child_session:request('nvim_set_option_value', 'ambiwidth', 'single', {}) screen:expect(singlewidth_screen) child_session:request('nvim_call_function', 'setcellwidths', {{{0x2103, 0x2103, 2}}}) screen:expect(doublewidth_screen) @@ -1448,22 +1658,19 @@ describe('TUI', function() end) it('draws correctly when cursor_address overflows #21643', function() - helpers.skip(helpers.is_os('mac'), 'FIXME: crashes/errors on macOS') - screen:try_resize(77, 834) + helpers.skip(is_os('mac'), 'FIXME: crashes/errors on macOS') + screen:try_resize(77, 855) retry(nil, nil, function() - eq({true, 831}, {child_session:request('nvim_win_get_height', 0)}) + eq({true, 852}, {child_session:request('nvim_win_get_height', 0)}) end) -- Use full screen message so that redrawing afterwards is more deterministic. child_session:notify('nvim_command', 'intro') screen:expect({any = 'Nvim'}) -- Going to top-left corner needs 3 bytes. -- Setting underline attribute needs 9 bytes. - -- With screen width 77, 63857 characters need 829 full screen lines. - -- Drawing each full screen line needs 77 + 2 = 79 bytes (2 bytes for CR LF). - -- The incomplete screen line needs 24 + 3 = 27 bytes. - -- The whole line needs 3 + 9 + 79 * 829 + 27 = 65530 bytes. + -- The whole line needs 3 + 9 + 65515 + 3 = 65530 bytes. -- The cursor_address that comes after will overflow the 65535-byte buffer. - local line = ('a'):rep(63857) .. '℃' + local line = ('a'):rep(65515) .. '℃' child_session:notify('nvim_exec_lua', [[ vim.api.nvim_buf_set_lines(0, 0, -1, true, {...}) vim.o.cursorline = true @@ -1472,8 +1679,8 @@ describe('TUI', function() feed_data('\n') screen:expect( '{13:a}{12:' .. ('a'):rep(76) .. '}|\n' - .. ('{12:' .. ('a'):rep(77) .. '}|\n'):rep(828) - .. '{12:' .. ('a'):rep(24) .. '℃' .. (' '):rep(52) .. '}|\n' .. dedent([[ + .. ('{12:' .. ('a'):rep(77) .. '}|\n'):rep(849) + .. '{12:' .. ('a'):rep(65) .. '℃' .. (' '):rep(11) .. '}|\n' .. dedent([[ b | {5:[No Name] [+] }| | @@ -1506,13 +1713,107 @@ describe('TUI', function() {3:-- TERMINAL --} | ]]} end) + + it('no assert failure on deadly signal #21896', function() + exec_lua([[vim.uv.kill(vim.fn.jobpid(vim.bo.channel), 'sigterm')]]) + screen:expect{grid=[[ + Vim: Caught deadly signal 'SIGTERM' | + | + | + [Process exited 1]{1: } | + | + | + {3:-- TERMINAL --} | + ]]} + end) + + it('no stack-use-after-scope with cursor color #22432', function() + screen:set_option('rgb', true) + command('set termguicolors') + child_session:request('nvim_exec2', [[ + set tgc + hi Cursor guifg=Red guibg=Green + set guicursor=n:block-Cursor/lCursor + ]], {}) + screen:set_default_attr_ids({ + [1] = {reverse = true}, + [2] = {bold = true, foreground = Screen.colors.Blue}, + [3] = {foreground = Screen.colors.Blue}, + [4] = {reverse = true, bold = true}, + [5] = {bold = true}, + }) + screen:expect([[ + {1: } | + {2:~}{3: }| + {2:~}{3: }| + {2:~}{3: }| + {4:[No Name] }| + | + {5:-- TERMINAL --} | + ]]) + feed_data('i') + screen:expect([[ + {1: } | + {2:~}{3: }| + {2:~}{3: }| + {2:~}{3: }| + {4:[No Name] }| + {5:-- INSERT --} | + {5:-- TERMINAL --} | + ]]) + end) + + it('redraws on SIGWINCH even if terminal size is unchanged #23411', function() + child_session:request('nvim_echo', {{'foo'}}, false, {}) + screen:expect([[ + {1: } | + {4:~ }| + {4:~ }| + {4:~ }| + {5:[No Name] }| + foo | + {3:-- TERMINAL --} | + ]]) + exec_lua([[vim.uv.kill(vim.fn.jobpid(vim.bo.channel), 'sigwinch')]]) + screen:expect([[ + {1: } | + {4:~ }| + {4:~ }| + {4:~ }| + {5:[No Name] }| + | + {3:-- TERMINAL --} | + ]]) + end) + + it('supports hiding cursor', function() + child_session:request('nvim_command', + "let g:id = jobstart([v:progpath, '--clean', '--headless'])") + feed_data(':call jobwait([g:id])\n') + screen:expect([[ + | + {4:~ }| + {4:~ }| + {4:~ }| + {5:[No Name] }| + :call jobwait([g:id]) | + {3:-- TERMINAL --} | + ]]) + feed_data('\003') + screen:expect([[ + {1: } | + {4:~ }| + {4:~ }| + {4:~ }| + {5:[No Name] }| + Type :qa and press <Enter> to exit Nvim | + {3:-- TERMINAL --} | + ]]) + end) end) describe('TUI', function() before_each(clear) - after_each(function() - os.remove('testF') - end) it('resize at startup #17285 #15044 #11330', function() local screen = Screen.new(50, 10) @@ -1538,12 +1839,51 @@ describe('TUI', function() {2:~ }│{4:~ }| {2:~ }│{5:[No Name] 0,0-1 All}| {2:~ }│ | - {5:new }{MATCH:<.*[/\]nvim }| + {5:new }{1:{MATCH:<.*[/\]nvim }}| | ]]) end) + it('argv[0] can be overridden #23953', function() + if not exec_lua('return pcall(require, "ffi")') then + pending('missing LuaJIT FFI') + end + local script_file = 'Xargv0.lua' + write_file(script_file, [=[ + local ffi = require('ffi') + ffi.cdef([[int execl(const char *, const char *, ...);]]) + ffi.C.execl(vim.v.progpath, 'Xargv0nvim', '--clean') + ]=]) + finally(function() + os.remove(script_file) + end) + local screen = thelpers.screen_setup(0, string.format([=[["%s", "--clean", "-l", "%s"]]=], + nvim_prog, script_file)) + screen:expect{grid=[[ + {1: } | + {4:~ }| + {4:~ }| + {4:~ }| + {5:[No Name] 0,0-1 All}| + | + {3:-- TERMINAL --} | + ]]} + feed_data(':put =v:argv + [v:progname]\n') + screen:expect{grid=[[ + Xargv0nvim | + --embed | + --clean | + {1:X}argv0nvim | + {5:[No Name] [+] 5,1 Bot}| + 4 more lines | + {3:-- TERMINAL --} | + ]]} + end) + it('with non-tty (pipe) stdout/stderr', function() + finally(function() + os.remove('testF') + end) local screen = thelpers.screen_setup(0, '"'..nvim_prog ..' -u NONE -i NONE --cmd \'set noswapfile noshowcmd noruler\' --cmd \'normal iabc\' > /dev/null 2>&1 && cat testF && rm testF"') feed_data(':w testF\n:q\n') @@ -1582,6 +1922,30 @@ describe('TUI', function() {3:-- TERMINAL --} | ]]) end) + + it('draws line with many trailing spaces correctly #24955', function() + local screen = thelpers.screen_setup(0, '["'..nvim_prog..[[", "-u", "NONE", "-i", "NONE"]] + ..[[, "--cmd", "call setline(1, ['1st line' .. repeat(' ', 153), '2nd line'])"]]..']', 80) + screen:expect{grid=[[ + {1:1}st line | + | + | + 2nd line | + {5:[No Name] [+] 1,1 All}| + | + {3:-- TERMINAL --} | + ]]} + feed_data('$') + screen:expect{grid=[[ + 1st line | + | + {1: } | + 2nd line | + {5:[No Name] [+] 1,161 All}| + | + {3:-- TERMINAL --} | + ]]} + end) end) describe('TUI UIEnter/UILeave', function() @@ -1620,12 +1984,16 @@ end) describe('TUI FocusGained/FocusLost', function() local screen + local child_session before_each(function() clear() - screen = thelpers.screen_setup(0, '["'..nvim_prog - ..'", "-u", "NONE", "-i", "NONE", "--cmd", "set noswapfile noshowcmd noruler"]') - screen:expect{grid=[[ + local child_server = new_pipename() + screen = thelpers.screen_setup(0, + string.format( + [=[["%s", "--listen", "%s", "-u", "NONE", "-i", "NONE", "--cmd", "set noswapfile noshowcmd noruler"]]=], + nvim_prog, child_server)) + screen:expect([[ {1: } | {4:~ }| {4:~ }| @@ -1633,22 +2001,16 @@ describe('TUI FocusGained/FocusLost', function() {5:[No Name] }| | {3:-- TERMINAL --} | - ]]} - feed_data(":autocmd FocusGained * echo 'gained'\n") - feed_data(":autocmd FocusLost * echo 'lost'\n") + ]]) + child_session = helpers.connect(child_server) + child_session:request('nvim_exec2', [[ + autocmd FocusGained * echo 'gained' + autocmd FocusLost * echo 'lost' + ]], {}) feed_data("\034\016") -- CTRL-\ CTRL-N end) it('in normal-mode', function() - screen:expect{grid=[[ - {1: } | - {4:~ }| - {4:~ }| - {4:~ }| - {5:[No Name] }| - :autocmd FocusLost * echo 'lost' | - {3:-- TERMINAL --} | - ]]} retry(2, 3 * screen.timeout, function() feed_data('\027[I') screen:expect([[ @@ -1675,7 +2037,7 @@ describe('TUI FocusGained/FocusLost', function() end) it('in insert-mode', function() - feed_command('set noshowmode') + feed_data(':set noshowmode\r') feed_data('i') screen:expect{grid=[[ {1: } | @@ -1740,18 +2102,11 @@ describe('TUI FocusGained/FocusLost', function() -- Set up autocmds that modify the buffer, instead of just calling :echo. -- This is how we can test handling of focus gained/lost during cmdline-mode. -- See commit: 5cc87d4dabd02167117be7a978b5c8faaa975419. - feed_data(":autocmd!\n") - feed_data(":autocmd FocusLost * call append(line('$'), 'lost')\n") - feed_data(":autocmd FocusGained * call append(line('$'), 'gained')\n") - screen:expect{grid=[[ - {1: } | - {4:~ }| - {4:~ }| - {4:~ }| - {5:[No Name] }| - | - {3:-- TERMINAL --} | - ]]} + child_session:request('nvim_exec2', [[ + autocmd! + autocmd FocusLost * call append(line('$'), 'lost') + autocmd FocusGained * call append(line('$'), 'gained') + ]], {}) retry(2, 3 * screen.timeout, function() -- Enter cmdline-mode. feed_data(':') @@ -1768,26 +2123,26 @@ describe('TUI FocusGained/FocusLost', function() end) it('in terminal-mode', function() - feed_data(':set shell='..testprg('shell-test')..'\n') + feed_data(':set shell='..testprg('shell-test')..' shellcmdflag=EXE\n') feed_data(':set noshowmode laststatus=0\n') - feed_data(':terminal\n') + feed_data(':terminal zia\n') -- Wait for terminal to be ready. screen:expect{grid=[[ - {1:r}eady $ | - [Process exited 0] | + {1:r}eady $ zia | | + [Process exited 0] | | | - :terminal | + :terminal zia | {3:-- TERMINAL --} | ]]} feed_data('\027[I') screen:expect{grid=[[ - {1:r}eady $ | - [Process exited 0] | + {1:r}eady $ zia | | + [Process exited 0] | | | gained | @@ -1796,9 +2151,9 @@ describe('TUI FocusGained/FocusLost', function() feed_data('\027[O') screen:expect([[ - {1:r}eady $ | - [Process exited 0] | + {1:r}eady $ zia | | + [Process exited 0] | | | lost | @@ -1839,7 +2194,7 @@ describe("TUI 't_Co' (terminal colors)", function() local screen local function assert_term_colors(term, colorterm, maxcolors) - helpers.clear({env={TERM=term}, args={}}) + clear({env={TERM=term}, args={}}) -- This is ugly because :term/termopen() forces TERM=xterm-256color. -- TODO: Revisit this after jobstart/termopen accept `env` dict. screen = thelpers.screen_setup(0, string.format( @@ -2204,45 +2559,44 @@ describe("TUI", function() end) +-- See test/unit/tui_spec.lua for unit tests. describe('TUI bg color', function() local screen - local function setup() - -- Only single integration test. - -- See test/unit/tui_spec.lua for unit tests. + local function setup_bg_test() clear() screen = thelpers.screen_setup(0, '["'..nvim_prog ..'", "-u", "NONE", "-i", "NONE", "--cmd", "set noswapfile", ' ..'"-c", "autocmd OptionSet background echo \\"did OptionSet, yay!\\""]') end - before_each(setup) + before_each(setup_bg_test) it('triggers OptionSet event on unsplit terminal-response', function() screen:expect([[ - {1: } | - {4:~ }| - {4:~ }| - {4:~ }| - {5:[No Name] 0,0-1 All}| - | - {3:-- TERMINAL --} | + {1: } | + {4:~ }| + {4:~ }| + {4:~ }| + {5:[No Name] 0,0-1 All}| + | + {3:-- TERMINAL --} | ]]) - feed_data('\027]11;rgb:ffff/ffff/ffff\007') + feed_data('\027]11;rgb:ffff/ffff/ffff\027\\') screen:expect{any='did OptionSet, yay!'} feed_data(':echo "new_bg=".&background\n') screen:expect{any='new_bg=light'} - setup() + setup_bg_test() screen:expect([[ - {1: } | - {4:~ }| - {4:~ }| - {4:~ }| - {5:[No Name] 0,0-1 All}| - | - {3:-- TERMINAL --} | + {1: } | + {4:~ }| + {4:~ }| + {4:~ }| + {5:[No Name] 0,0-1 All}| + | + {3:-- TERMINAL --} | ]]) feed_data('\027]11;rgba:ffff/ffff/ffff/8000\027\\') screen:expect{any='did OptionSet, yay!'} @@ -2253,13 +2607,13 @@ describe('TUI bg color', function() it('triggers OptionSet event with split terminal-response', function() screen:expect([[ - {1: } | - {4:~ }| - {4:~ }| - {4:~ }| - {5:[No Name] 0,0-1 All}| - | - {3:-- TERMINAL --} | + {1: } | + {4:~ }| + {4:~ }| + {4:~ }| + {5:[No Name] 0,0-1 All}| + | + {3:-- TERMINAL --} | ]]) -- Send a background response with the OSC command part split. feed_data('\027]11;rgb') @@ -2269,19 +2623,19 @@ describe('TUI bg color', function() feed_data(':echo "new_bg=".&background\n') screen:expect{any='new_bg=light'} - setup() + setup_bg_test() screen:expect([[ - {1: } | - {4:~ }| - {4:~ }| - {4:~ }| - {5:[No Name] 0,0-1 All}| - | - {3:-- TERMINAL --} | + {1: } | + {4:~ }| + {4:~ }| + {4:~ }| + {5:[No Name] 0,0-1 All}| + | + {3:-- TERMINAL --} | ]]) -- Send a background response with the Pt portion split. feed_data('\027]11;rgba:ffff/fff') - feed_data('f/ffff/8000\007') + feed_data('f/ffff/8000\027\\') screen:expect{any='did OptionSet, yay!'} feed_data(':echo "new_bg=".&background\n') @@ -2290,13 +2644,13 @@ describe('TUI bg color', function() it('not triggers OptionSet event with invalid terminal-response', function() screen:expect([[ - {1: } | - {4:~ }| - {4:~ }| - {4:~ }| - {5:[No Name] 0,0-1 All}| - | - {3:-- TERMINAL --} | + {1: } | + {4:~ }| + {4:~ }| + {4:~ }| + {5:[No Name] 0,0-1 All}| + | + {3:-- TERMINAL --} | ]]) feed_data('\027]11;rgb:ffff/ffff/ffff/8000\027\\') screen:expect_unchanged() @@ -2304,17 +2658,17 @@ describe('TUI bg color', function() feed_data(':echo "new_bg=".&background\n') screen:expect{any='new_bg=dark'} - setup() + setup_bg_test() screen:expect([[ - {1: } | - {4:~ }| - {4:~ }| - {4:~ }| - {5:[No Name] 0,0-1 All}| - | - {3:-- TERMINAL --} | + {1: } | + {4:~ }| + {4:~ }| + {4:~ }| + {5:[No Name] 0,0-1 All}| + | + {3:-- TERMINAL --} | ]]) - feed_data('\027]11;rgba:ffff/foo/ffff/8000\007') + feed_data('\027]11;rgba:ffff/foo/ffff/8000\027\\') screen:expect_unchanged() feed_data(':echo "new_bg=".&background\n') @@ -2359,18 +2713,31 @@ describe("TUI as a client", function() set_session(client_super) local screen_client = thelpers.screen_setup(0, - string.format([=[["%s", "-u", "NONE", "-i", "NONE", "--server", "%s", "--remote-ui"]]=], + string.format([=[["%s", "--server", "%s", "--remote-ui"]]=], nvim_prog, server_pipe)) - screen_client:expect{grid=[[ - Hello, Worl{1:d} | - {4:~ }| - {4:~ }| - {4:~ }| - {5:[No Name] [+] }| - | - {3:-- TERMINAL --} | - ]]} + screen_client:expect{grid=[[ + Hello, Worl{1:d} | + {4:~ }| + {4:~ }| + {4:~ }| + {5:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]} + + -- grid smaller than containing terminal window is cleared properly + feed_data(":call setline(1,['a'->repeat(&columns)]->repeat(&lines))\n") + feed_data("0:set lines=3\n") + screen_server:expect{grid=[[ + {1:a}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {5:[No Name] [+] }| + | + | + | + | + {3:-- TERMINAL --} | + ]]} feed_data(":q!\n") @@ -2379,19 +2746,19 @@ describe("TUI as a client", function() end) it("connects to remote instance (--headless)", function() - local server = helpers.spawn_argv(false) -- equivalent to clear() + local server = spawn_argv(false) -- equivalent to clear() local client_super = spawn_argv(true) set_session(server) - local server_pipe = eval'v:servername' - feed'iHalloj!<esc>' + local server_pipe = meths.get_vvar('servername') + server:request('nvim_input', 'iHalloj!<Esc>') set_session(client_super) - local screen = thelpers.screen_setup(0, - string.format([=[["%s", "-u", "NONE", "-i", "NONE", "--server", "%s", "--remote-ui"]]=], + local screen_client = thelpers.screen_setup(0, + string.format([=[["%s", "--server", "%s", "--remote-ui"]]=], nvim_prog, server_pipe)) - screen:expect{grid=[[ + screen_client:expect{grid=[[ Halloj{1:!} | {4:~ }| {4:~ }| @@ -2401,15 +2768,32 @@ describe("TUI as a client", function() {3:-- TERMINAL --} | ]]} + -- No heap-use-after-free when receiving UI events after deadly signal #22184 + server:request('nvim_input', ('a'):rep(1000)) + exec_lua([[vim.uv.kill(vim.fn.jobpid(vim.bo.channel), 'sigterm')]]) + screen_client:expect{grid=[[ + Vim: Caught deadly signal 'SIGTERM' | + | + | + [Process exited 1]{1: } | + | + | + {3:-- TERMINAL --} | + ]]} + + eq(0, meths.get_vvar('shell_error')) + -- exits on input eof #22244 + funcs.system({nvim_prog, '--server', server_pipe, '--remote-ui'}) + eq(1, meths.get_vvar('shell_error')) + client_super:close() server:close() end) - it("throws error when no server exists", function() clear() local screen = thelpers.screen_setup(0, - string.format([=[["%s", "-u", "NONE", "-i", "NONE", "--server", "127.0.0.1:2436546", "--remote-ui"]]=], + string.format([=[["%s", "--server", "127.0.0.1:2436546", "--remote-ui"]]=], nvim_prog), 60) screen:expect([[ @@ -2423,7 +2807,7 @@ describe("TUI as a client", function() ]]) end) - it("exits when server quits", function() + local function test_remote_tui_quit(status) local server_super = spawn_argv(false) -- equivalent to clear() local client_super = spawn_argv(true) @@ -2432,6 +2816,15 @@ describe("TUI as a client", function() local screen_server = thelpers.screen_setup(0, string.format([=[["%s", "--listen", "%s", "-u", "NONE", "-i", "NONE", "--cmd", "%s laststatus=2 background=dark"]]=], nvim_prog, server_pipe, nvim_set)) + screen_server:expect{grid=[[ + {1: } | + {4:~ }| + {4:~ }| + {4:~ }| + {5:[No Name] }| + | + {3:-- TERMINAL --} | + ]]} feed_data("iHello, World") screen_server:expect{grid=[[ @@ -2456,7 +2849,7 @@ describe("TUI as a client", function() set_session(client_super) local screen_client = thelpers.screen_setup(0, - string.format([=[["%s", "-u", "NONE", "-i", "NONE", "--server", "%s", "--remote-ui"]]=], + string.format([=[["%s", "--server", "%s", "--remote-ui"]]=], nvim_prog, server_pipe)) screen_client:expect{grid=[[ @@ -2471,13 +2864,39 @@ describe("TUI as a client", function() -- quitting the server set_session(server_super) - feed_data(":q!\n") - screen_server:expect({any="Process exited 0"}) - + feed_data(status and ':' .. status .. 'cquit!\n' or ":quit!\n") + status = status and status or 0 + screen_server:expect{grid=[[ + | + [Process exited ]] .. status .. [[]{1: }{MATCH:%s+}| + | + | + | + | + {3:-- TERMINAL --} | + ]]} -- assert that client has exited - screen_client:expect({any="Process exited 0"}) + screen_client:expect{grid=[[ + | + [Process exited ]] .. status .. [[]{1: }{MATCH:%s+}| + | + | + | + | + {3:-- TERMINAL --} | + ]]} server_super:close() client_super:close() + end + + describe("exits when server quits", function() + it("with :quit", function() + test_remote_tui_quit() + end) + + it("with :cquit", function() + test_remote_tui_quit(42) + end) end) end) diff --git a/test/functional/terminal/window_spec.lua b/test/functional/terminal/window_spec.lua index 80e9d78400..39fc2c2562 100644 --- a/test/functional/terminal/window_spec.lua +++ b/test/functional/terminal/window_spec.lua @@ -44,7 +44,7 @@ describe(':terminal window', function() {7:6 } | {3:-- TERMINAL --} | ]]) - feed_data({'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'}) + feed_data('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') screen:expect([[ {7:1 }tty ready | {7:2 }rows: 6, cols: 48 | @@ -55,8 +55,6 @@ describe(':terminal window', function() {3:-- TERMINAL --} | ]]) - skip(is_os('win'), 'win: :terminal resize is unreliable #7007') - -- numberwidth=9 feed([[<C-\><C-N>]]) feed([[:set numberwidth=9 number<CR>i]]) @@ -64,17 +62,17 @@ describe(':terminal window', function() {7: 1 }tty ready | {7: 2 }rows: 6, cols: 48 | {7: 3 }abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNO| - {7: 4 }WXYZrows: 6, cols: 41 | + {7: 4 }PQRSTUVWXYZrows: 6, cols: 41 | {7: 5 }{1: } | {7: 6 } | {3:-- TERMINAL --} | ]]) - feed_data({' abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'}) + feed_data(' abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') screen:expect([[ {7: 1 }tty ready | {7: 2 }rows: 6, cols: 48 | {7: 3 }abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNO| - {7: 4 }WXYZrows: 6, cols: 41 | + {7: 4 }PQRSTUVWXYZrows: 6, cols: 41 | {7: 5 } abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN| {7: 6 }OPQRSTUVWXYZ{1: } | {3:-- TERMINAL --} | @@ -82,6 +80,41 @@ describe(':terminal window', function() end) end) + describe("with 'statuscolumn'", function() + it('wraps text', function() + command([[set number statuscolumn=++%l\ \ ]]) + screen:expect([[ + {7:++1 }tty ready | + {7:++2 }rows: 6, cols: 45 | + {7:++3 }{1: } | + {7:++4 } | + {7:++5 } | + {7:++6 } | + {3:-- TERMINAL --} | + ]]) + feed_data('\n\n\n\n\nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') + screen:expect([[ + {7:++4 } | + {7:++5 } | + {7:++6 } | + {7:++7 } | + {7:++8 }abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRS| + {7:++9 }TUVWXYZ{1: } | + {3:-- TERMINAL --} | + ]]) + feed_data('\nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') + screen:expect([[ + {7:++7 } | + {7:++8 }abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR| + {7:++9 }STUVWXYZ | + {7:++10 }abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR| + {7:++11 }STUVWXYZrows: 6, cols: 44 | + {7:++12 }{1: } | + {3:-- TERMINAL --} | + ]]) + end) + end) + describe("with 'colorcolumn'", function() before_each(function() feed([[<C-\><C-N>]]) diff --git a/test/functional/terminal/window_split_tab_spec.lua b/test/functional/terminal/window_split_tab_spec.lua index 1d77e1e92e..da14531fa2 100644 --- a/test/functional/terminal/window_split_tab_spec.lua +++ b/test/functional/terminal/window_split_tab_spec.lua @@ -19,7 +19,7 @@ describe(':terminal', function() clear() -- set the statusline to a constant value because of variables like pid -- and current directory and to improve visibility of splits - meths.set_option('statusline', '==========') + meths.set_option_value('statusline', '==========', {}) command('highlight StatusLine cterm=NONE') command('highlight StatusLineNC cterm=NONE') command('highlight VertSplit cterm=NONE') @@ -71,7 +71,7 @@ describe(':terminal', function() end) it('does not change size if updated when not visible in any window #19665', function() - local channel = meths.buf_get_option(0, 'channel') + local channel = meths.get_option_value('channel', {}) command('enew') sleep(100) meths.chan_send(channel, 'foo') |