mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			530 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			530 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
| 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, nvim_dir, feed_command = helpers.feed, helpers.nvim_dir, helpers.feed_command
 | |
| local iswin = helpers.iswin
 | |
| local eval = helpers.eval
 | |
| local command = helpers.command
 | |
| local poke_eventloop = helpers.poke_eventloop
 | |
| local retry = helpers.retry
 | |
| local curbufmeths = helpers.curbufmeths
 | |
| local nvim = helpers.nvim
 | |
| local feed_data = thelpers.feed_data
 | |
| local pcall_err = helpers.pcall_err
 | |
| 
 | |
| describe(':terminal scrollback', function()
 | |
|   local screen
 | |
| 
 | |
|   before_each(function()
 | |
|     clear()
 | |
|     screen = thelpers.screen_setup(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                        |
 | |
|         {1: }                             |
 | |
|         {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                         |
 | |
|         {1: }                             |
 | |
|         {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                         |
 | |
|           {1: }                             |
 | |
|           {3:-- TERMINAL --}                |
 | |
|         ]])
 | |
|         eq(7, curbuf('line_count'))
 | |
|       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{1: }                        |
 | |
|             {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{2: }                        |
 | |
|                                           |
 | |
|           ]])
 | |
|         end)
 | |
|       end)
 | |
|     end)
 | |
| 
 | |
| 
 | |
|     describe('and height decreased by 1', function()
 | |
|       if helpers.pending_win32(pending) then return end
 | |
|       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           |
 | |
|           {2:^ }                           |
 | |
|                                       |
 | |
|         ]])
 | |
|       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         |
 | |
|             {2:^ }                         |
 | |
|                                       |
 | |
|           ]])
 | |
|           eq(8, curbuf('line_count'))
 | |
|           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 helpers.pending_win32(pending) 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             |
 | |
|           {1: }                             |
 | |
|                                         |
 | |
|           {3:-- TERMINAL --}                |
 | |
|         ]])
 | |
|         eq(4, curbuf('line_count'))
 | |
|       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             |
 | |
|             {1: }                             |
 | |
|             {3:-- TERMINAL --}                |
 | |
|           ]])
 | |
|           eq(4, curbuf('line_count'))
 | |
|           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             |
 | |
|             {1: }                             |
 | |
|             {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                         |
 | |
|         {1: }                             |
 | |
|         {3:-- TERMINAL --}                |
 | |
|       ]])
 | |
|       screen:try_resize(screen._width, screen._height - 3)
 | |
|       screen:expect([[
 | |
|         line4                         |
 | |
|         rows: 3, cols: 30             |
 | |
|         {1: }                             |
 | |
|         {3:-- TERMINAL --}                |
 | |
|       ]])
 | |
|       eq(7, curbuf('line_count'))
 | |
|     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 helpers.pending_win32(pending) 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             |
 | |
|           {1: }                             |
 | |
|           {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, curbuf('line_count'))
 | |
|           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             |
 | |
|             {1: }                             |
 | |
|             {3:-- TERMINAL --}                |
 | |
|           ]])
 | |
|           eq(9, curbuf('line_count'))
 | |
|           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            |
 | |
|               {1: }                             |
 | |
|                                             |
 | |
|               {3:-- TERMINAL --}                |
 | |
|             ]])
 | |
|             -- since there's an empty line after the cursor, the buffer line
 | |
|             -- count equals the terminal screen height
 | |
|             eq(11, curbuf('line_count'))
 | |
|           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)
 | |
|     screen:attach({rgb=false})
 | |
|     feed_command('call termopen(["'..nvim_dir..'/tty-test", "10"]) | startinsert')
 | |
|     poke_eventloop()
 | |
|     screen:expect([[
 | |
|       line6                         |
 | |
|       line7                         |
 | |
|       line8                         |
 | |
|       line9                         |
 | |
|                                     |
 | |
|       [Process exited 0]            |
 | |
|       -- TERMINAL --                |
 | |
|     ]])
 | |
|     feed('<cr>')
 | |
|     -- closes the buffer correctly after pressing a key
 | |
|     screen:expect([[
 | |
|       ^                              |
 | |
|       ~                             |
 | |
|       ~                             |
 | |
|       ~                             |
 | |
|       ~                             |
 | |
|       ~                             |
 | |
|                                     |
 | |
|     ]])
 | |
|   end)
 | |
| end)
 | |
| 
 | |
| describe("'scrollback' option", function()
 | |
|   before_each(function()
 | |
|     clear()
 | |
|   end)
 | |
| 
 | |
|   local function set_fake_shell()
 | |
|     -- shell-test.c is a fake shell that prints its arguments and exits.
 | |
|     nvim('set_option', 'shell', nvim_dir..'/shell-test')
 | |
|     nvim('set_option', 'shellcmdflag', 'EXE')
 | |
|   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 iswin() then
 | |
|       screen = thelpers.screen_setup(nil, "['cmd.exe']", 30)
 | |
|     else
 | |
|       screen = thelpers.screen_setup(nil, "['sh']", 30)
 | |
|     end
 | |
| 
 | |
|     curbufmeths.set_option('scrollback', 0)
 | |
|     feed_data(nvim_dir..'/shell-test REP 31 line'..(iswin() 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 iswin() then
 | |
|       command([[let $PROMPT='$$']])
 | |
|       screen = thelpers.screen_setup(nil, "['cmd.exe']", 30)
 | |
|     else
 | |
|       command('let $PS1 = "$"')
 | |
|       screen = thelpers.screen_setup(nil, "['sh']", 30)
 | |
|     end
 | |
| 
 | |
|     curbufmeths.set_option('scrollback', 200)
 | |
| 
 | |
|     -- Wait for prompt.
 | |
|     screen:expect{any='%$'}
 | |
| 
 | |
|     feed_data(nvim_dir.."/shell-test REP 31 line"..(iswin() and '\r' or '\n'))
 | |
|     screen:expect{any='30: line                      '}
 | |
| 
 | |
|     retry(nil, nil, function() expect_lines(33, 2) end)
 | |
|     curbufmeths.set_option('scrollback', 10)
 | |
|     poke_eventloop()
 | |
|     retry(nil, nil, function() expect_lines(16) end)
 | |
|     curbufmeths.set_option('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(nvim_dir.."/shell-test REP 41 line"..(iswin() and '\r' or '\n'))
 | |
|     if iswin() then
 | |
|       screen:expect{grid=[[
 | |
|         37: line                      |
 | |
|         38: line                      |
 | |
|         39: line                      |
 | |
|         40: line                      |
 | |
|                                       |
 | |
|         ${1: }                            |
 | |
|         {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((iswin() and '36: line' or '35: line'), eval("getline(line('w0') - 1)"))
 | |
|     eq((iswin() and '27: line' or '26: line'), eval("getline(line('w0') - 10)"))
 | |
|   end)
 | |
| 
 | |
|   it('defaults to 10000 in :terminal buffers', function()
 | |
|     set_fake_shell()
 | |
|     command('terminal')
 | |
|     eq(10000, curbufmeths.get_option('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, curbufmeths.get_option('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, curbufmeths.get_option('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'))
 | |
|     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'))
 | |
|   end)
 | |
| 
 | |
|   it(':setlocal in a normal buffer', function()
 | |
|     command('new')
 | |
|     -- :setlocal to -1.
 | |
|     command('setlocal scrollback=-1')
 | |
|     eq(-1, curbufmeths.get_option('scrollback'))
 | |
|     -- :setlocal to anything except -1. Currently, this just has no effect.
 | |
|     command('setlocal scrollback=42')
 | |
|     eq(42, curbufmeths.get_option('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'))
 | |
|     command('terminal')
 | |
|     eq(42, curbufmeths.get_option('scrollback'))  -- inherits global default
 | |
|     command('setlocal scrollback=99')
 | |
|     eq(99, curbufmeths.get_option('scrollback'))
 | |
|     command('set scrollback<')                    -- reset to global default
 | |
|     eq(42, curbufmeths.get_option('scrollback'))
 | |
|     command('setglobal scrollback=734')           -- new global default
 | |
|     eq(42, curbufmeths.get_option('scrollback'))  -- local value did not change
 | |
|     command('terminal')
 | |
|     eq(734, curbufmeths.get_option('scrollback'))
 | |
|   end)
 | |
| 
 | |
| end)
 | 
