123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645 |
- local t = require('test.testutil')
- local n = require('test.functional.testnvim')()
- local Screen = require('test.functional.ui.screen')
- local tt = require('test.functional.testterm')
- local clear, eq = n.clear, t.eq
- local feed, testprg = n.feed, n.testprg
- local eval = n.eval
- local command = n.command
- local poke_eventloop = n.poke_eventloop
- local retry = t.retry
- local api = n.api
- local feed_data = tt.feed_data
- local pcall_err = t.pcall_err
- local exec_lua = n.exec_lua
- local assert_alive = n.assert_alive
- local skip = t.skip
- local is_os = t.is_os
- describe(':terminal scrollback', function()
- local screen
- before_each(function()
- clear()
- screen = tt.setup_screen(nil, nil, 30)
- end)
- describe('when the limit is exceeded', function()
- before_each(function()
- local lines = {}
- for i = 1, 30 do
- table.insert(lines, 'line' .. tostring(i))
- end
- table.insert(lines, '')
- feed_data(lines)
- screen:expect([[
- line26 |
- line27 |
- line28 |
- line29 |
- line30 |
- ^ |
- {3:-- TERMINAL --} |
- ]])
- end)
- it('will delete extra lines at the top', function()
- feed('<c-\\><c-n>gg')
- screen:expect([[
- ^line16 |
- line17 |
- line18 |
- line19 |
- line20 |
- line21 |
- |
- ]])
- end)
- end)
- describe('with cursor at last row', function()
- before_each(function()
- feed_data({ 'line1', 'line2', 'line3', 'line4', '' })
- screen:expect([[
- tty ready |
- line1 |
- line2 |
- line3 |
- line4 |
- ^ |
- {3:-- TERMINAL --} |
- ]])
- end)
- describe('and 1 line is printed', function()
- before_each(function()
- feed_data({ 'line5', '' })
- end)
- it('will hide the top line', function()
- screen:expect([[
- line1 |
- line2 |
- line3 |
- line4 |
- line5 |
- ^ |
- {3:-- TERMINAL --} |
- ]])
- eq(7, api.nvim_buf_line_count(0))
- end)
- describe('and then 3 more lines are printed', function()
- before_each(function()
- feed_data({ 'line6', 'line7', 'line8' })
- end)
- it('will hide the top 4 lines', function()
- screen:expect([[
- line3 |
- line4 |
- line5 |
- line6 |
- line7 |
- line8^ |
- {3:-- TERMINAL --} |
- ]])
- feed('<c-\\><c-n>6k')
- screen:expect([[
- ^line2 |
- line3 |
- line4 |
- line5 |
- line6 |
- line7 |
- |
- ]])
- feed('gg')
- screen:expect([[
- ^tty ready |
- line1 |
- line2 |
- line3 |
- line4 |
- line5 |
- |
- ]])
- feed('G')
- screen:expect([[
- line3 |
- line4 |
- line5 |
- line6 |
- line7 |
- ^line8 |
- |
- ]])
- end)
- end)
- end)
- describe('and height decreased by 1', function()
- local function will_hide_top_line()
- feed([[<C-\><C-N>]])
- screen:try_resize(screen._width - 2, screen._height - 1)
- screen:expect([[
- line2 |
- line3 |
- line4 |
- rows: 5, cols: 28 |
- ^ |
- |
- ]])
- end
- it('will hide top line', will_hide_top_line)
- describe('and then decreased by 2', function()
- before_each(function()
- will_hide_top_line()
- screen:try_resize(screen._width - 2, screen._height - 2)
- end)
- it('will hide the top 3 lines', function()
- screen:expect([[
- rows: 5, cols: 28 |
- rows: 3, cols: 26 |
- ^ |
- |
- ]])
- eq(8, api.nvim_buf_line_count(0))
- feed([[3k]])
- screen:expect([[
- ^line4 |
- rows: 5, cols: 28 |
- rows: 3, cols: 26 |
- |
- ]])
- end)
- end)
- end)
- end)
- describe('with empty lines after the cursor', function()
- -- XXX: Can't test this reliably on Windows unless the cursor is _moved_
- -- by the resize. http://docs.libuv.org/en/v1.x/signal.html
- -- See also: https://github.com/rprichard/winpty/issues/110
- if skip(is_os('win')) then
- return
- end
- describe('and the height is decreased by 2', function()
- before_each(function()
- screen:try_resize(screen._width, screen._height - 2)
- end)
- local function will_delete_last_two_lines()
- screen:expect([[
- tty ready |
- rows: 4, cols: 30 |
- ^ |
- |
- {3:-- TERMINAL --} |
- ]])
- eq(4, api.nvim_buf_line_count(0))
- end
- it('will delete the last two empty lines', will_delete_last_two_lines)
- describe('and then decreased by 1', function()
- before_each(function()
- will_delete_last_two_lines()
- screen:try_resize(screen._width, screen._height - 1)
- end)
- it('will delete the last line and hide the first', function()
- screen:expect([[
- rows: 4, cols: 30 |
- rows: 3, cols: 30 |
- ^ |
- {3:-- TERMINAL --} |
- ]])
- eq(4, api.nvim_buf_line_count(0))
- feed('<c-\\><c-n>gg')
- screen:expect([[
- ^tty ready |
- rows: 4, cols: 30 |
- rows: 3, cols: 30 |
- |
- ]])
- feed('a')
- screen:expect([[
- rows: 4, cols: 30 |
- rows: 3, cols: 30 |
- ^ |
- {3:-- TERMINAL --} |
- ]])
- end)
- end)
- end)
- end)
- describe('with 4 lines hidden in the scrollback', function()
- before_each(function()
- feed_data({ 'line1', 'line2', 'line3', 'line4', '' })
- screen:expect([[
- tty ready |
- line1 |
- line2 |
- line3 |
- line4 |
- ^ |
- {3:-- TERMINAL --} |
- ]])
- screen:try_resize(screen._width, screen._height - 3)
- screen:expect([[
- line4 |
- rows: 3, cols: 30 |
- ^ |
- {3:-- TERMINAL --} |
- ]])
- eq(7, api.nvim_buf_line_count(0))
- end)
- describe('and the height is increased by 1', function()
- -- XXX: Can't test this reliably on Windows unless the cursor is _moved_
- -- by the resize. http://docs.libuv.org/en/v1.x/signal.html
- -- See also: https://github.com/rprichard/winpty/issues/110
- if skip(is_os('win')) then
- return
- end
- local function pop_then_push()
- screen:try_resize(screen._width, screen._height + 1)
- screen:expect([[
- line4 |
- rows: 3, cols: 30 |
- rows: 4, cols: 30 |
- ^ |
- {3:-- TERMINAL --} |
- ]])
- end
- it('will pop 1 line and then push it back', pop_then_push)
- describe('and then by 3', function()
- before_each(function()
- pop_then_push()
- eq(8, api.nvim_buf_line_count(0))
- screen:try_resize(screen._width, screen._height + 3)
- end)
- local function pop3_then_push1()
- screen:expect([[
- line2 |
- line3 |
- line4 |
- rows: 3, cols: 30 |
- rows: 4, cols: 30 |
- rows: 7, cols: 30 |
- ^ |
- {3:-- TERMINAL --} |
- ]])
- eq(9, api.nvim_buf_line_count(0))
- feed('<c-\\><c-n>gg')
- screen:expect([[
- ^tty ready |
- line1 |
- line2 |
- line3 |
- line4 |
- rows: 3, cols: 30 |
- rows: 4, cols: 30 |
- |
- ]])
- end
- it('will pop 3 lines and then push one back', pop3_then_push1)
- describe('and then by 4', function()
- before_each(function()
- pop3_then_push1()
- feed('Gi')
- screen:try_resize(screen._width, screen._height + 4)
- end)
- it('will show all lines and leave a blank one at the end', function()
- screen:expect([[
- tty ready |
- line1 |
- line2 |
- line3 |
- line4 |
- rows: 3, cols: 30 |
- rows: 4, cols: 30 |
- rows: 7, cols: 30 |
- rows: 11, cols: 30 |
- ^ |
- |
- {3:-- TERMINAL --} |
- ]])
- -- since there's an empty line after the cursor, the buffer line
- -- count equals the terminal screen height
- eq(11, api.nvim_buf_line_count(0))
- end)
- end)
- end)
- end)
- end)
- end)
- describe(':terminal prints more lines than the screen height and exits', function()
- it('will push extra lines to scrollback', function()
- clear()
- local screen = Screen.new(30, 7, { rgb = false })
- command(
- ("call jobstart(['%s', '10'], {'term':v:true}) | startinsert"):format(testprg('tty-test'))
- )
- screen:expect([[
- line6 |
- line7 |
- line8 |
- line9 |
- |
- [Process exited 0]^ |
- {5:-- TERMINAL --} |
- ]])
- feed('<cr>')
- -- closes the buffer correctly after pressing a key
- screen:expect {
- grid = [[
- ^ |
- {1:~ }|*5
- |
- ]],
- attr_ids = { [1] = { foreground = 12 } },
- }
- end)
- end)
- describe("'scrollback' option", function()
- before_each(function()
- clear()
- end)
- local function set_fake_shell()
- api.nvim_set_option_value('shell', string.format('"%s" INTERACT', testprg('shell-test')), {})
- end
- local function expect_lines(expected, epsilon)
- local ep = epsilon and epsilon or 0
- local actual = eval("line('$')")
- if expected > actual + ep and expected < actual - ep then
- error('expected (+/- ' .. ep .. '): ' .. expected .. ', actual: ' .. tostring(actual))
- end
- end
- it('set to 0 behaves as 1', function()
- local screen
- if is_os('win') then
- screen = tt.setup_screen(nil, { 'cmd.exe' }, 30)
- else
- screen = tt.setup_screen(nil, { 'sh' }, 30)
- end
- api.nvim_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)
- end)
- it('deletes lines (only) if necessary', function()
- local screen
- if is_os('win') then
- command([[let $PROMPT='$$']])
- screen = tt.setup_screen(nil, { 'cmd.exe' }, 30)
- else
- command('let $PS1 = "$"')
- screen = tt.setup_screen(nil, { 'sh' }, 30)
- end
- api.nvim_set_option_value('scrollback', 200, {})
- -- Wait for prompt.
- screen:expect { any = '%$' }
- 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(33, 2)
- end)
- api.nvim_set_option_value('scrollback', 10, {})
- poke_eventloop()
- retry(nil, nil, function()
- expect_lines(16)
- end)
- api.nvim_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.
- command('sleep 100m')
- feed_data(('%s REP 41 line%s'):format(testprg('shell-test'), is_os('win') and '\r' or '\n'))
- if is_os('win') then
- screen:expect {
- grid = [[
- 37: line |
- 38: line |
- 39: line |
- 40: line |
- |
- $^ |
- {3:-- TERMINAL --} |
- ]],
- }
- else
- screen:expect {
- grid = [[
- 36: line |
- 37: line |
- 38: line |
- 39: line |
- 40: line |
- {MATCH:.*}|
- {3:-- TERMINAL --} |
- ]],
- }
- end
- expect_lines(58)
- -- Verify off-screen state
- 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()
- -- Scrollback is 10 on setup_screen
- local screen = tt.setup_screen(nil, nil, 30)
- local lines = {}
- for i = 1, 30 do
- table.insert(lines, 'line' .. tostring(i))
- end
- table.insert(lines, '')
- feed_data(lines)
- screen:expect([[
- line26 |
- line27 |
- line28 |
- line29 |
- line30 |
- ^ |
- {3:-- TERMINAL --} |
- ]])
- local term_height = 6 -- Actual terminal screen height, not the scrollback
- -- Initial
- local scrollback = api.nvim_get_option_value('scrollback', {})
- eq(scrollback + term_height, eval('line("$")'))
- -- Reduction
- scrollback = scrollback - 2
- api.nvim_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, api.nvim_get_option_value('scrollback', {}))
- end)
- it('error if set to invalid value', function()
- eq('Vim(set):E474: Invalid argument: scrollback=-2', pcall_err(command, 'set scrollback=-2'))
- eq(
- 'Vim(set):E474: Invalid argument: scrollback=100001',
- pcall_err(command, 'set scrollback=100001')
- )
- end)
- it('defaults to -1 on normal buffers', function()
- command('new')
- eq(-1, api.nvim_get_option_value('scrollback', {}))
- end)
- it(':setlocal in a :terminal buffer', function()
- set_fake_shell()
- -- _Global_ scrollback=-1 defaults :terminal to 10_000.
- command('setglobal scrollback=-1')
- command('terminal')
- eq(10000, api.nvim_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, api.nvim_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, api.nvim_get_option_value('scrollback', {}))
- end)
- it(':setlocal in a normal buffer', function()
- command('new')
- -- :setlocal to -1.
- command('setlocal scrollback=-1')
- eq(-1, api.nvim_get_option_value('scrollback', {}))
- -- :setlocal to anything except -1. Currently, this just has no effect.
- command('setlocal scrollback=42')
- eq(42, api.nvim_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, api.nvim_get_option_value('scrollback', {}))
- command('terminal')
- eq(42, api.nvim_get_option_value('scrollback', {})) -- inherits global default
- command('setlocal scrollback=99')
- eq(99, api.nvim_get_option_value('scrollback', {}))
- command('set scrollback<') -- reset to global default
- eq(42, api.nvim_get_option_value('scrollback', {}))
- command('setglobal scrollback=734') -- new global default
- eq(42, api.nvim_get_option_value('scrollback', {})) -- local value did not change
- command('terminal')
- eq(734, api.nvim_get_option_value('scrollback', {}))
- end)
- end)
- describe('pending scrollback line handling', function()
- local screen
- before_each(function()
- clear()
- screen = Screen.new(30, 7)
- screen:set_default_attr_ids {
- [1] = { foreground = Screen.colors.Brown },
- [2] = { reverse = true },
- [3] = { bold = true },
- }
- end)
- it("does not crash after setting 'number' #14891", function()
- exec_lua [[
- 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 |
- {1: 2 }a |
- {1: 3 }a |
- {1: 4 }a |
- {1: 5 }a |
- {1: 6 }a |
- |
- ]]
- feed('G')
- screen:expect [[
- {1: 7 }a |
- {1: 8 }a |
- {1: 9 }a |
- {1: 10 }a |
- {1: 11 }a |
- {1: 12 }^a |
- |
- ]]
- assert_alive()
- end)
- it('does not crash after nvim_buf_call #14891', function()
- exec_lua(
- [[
- local bufnr = vim.api.nvim_create_buf(false, true)
- local args = ...
- vim.api.nvim_buf_call(bufnr, function()
- vim.fn.jobstart(args, { term = true })
- end)
- vim.api.nvim_win_set_buf(0, bufnr)
- vim.cmd('startinsert')
- ]],
- is_os('win') and { 'cmd.exe', '/c', 'for /L %I in (1,1,12) do @echo hi' }
- or { 'printf', ('hi\n'):rep(12) }
- )
- screen:expect [[
- hi |*4
- |
- [Process exited 0]^ |
- {3:-- TERMINAL --} |
- ]]
- assert_alive()
- end)
- end)
|