" Test for 'scroll', 'scrolloff', 'smoothscroll', etc. source check.vim source screendump.vim source mouse.vim func Test_reset_scroll() let scr = &l:scroll setlocal scroll=1 setlocal scroll& call assert_equal(scr, &l:scroll) setlocal scroll=1 setlocal scroll=0 call assert_equal(scr, &l:scroll) try execute 'setlocal scroll=' . (winheight(0) + 1) " not reached call assert_false(1) catch call assert_exception('E49:') endtry split let scr = &l:scroll setlocal scroll=1 setlocal scroll& call assert_equal(scr, &l:scroll) setlocal scroll=1 setlocal scroll=0 call assert_equal(scr, &l:scroll) quit! endfunc func Test_scolloff_even_line_count() new resize 6 setlocal scrolloff=3 call setline(1, range(20)) normal 2j call assert_equal(1, getwininfo(win_getid())[0].topline) normal j call assert_equal(1, getwininfo(win_getid())[0].topline) normal j call assert_equal(2, getwininfo(win_getid())[0].topline) normal j call assert_equal(3, getwininfo(win_getid())[0].topline) bwipe! endfunc func Test_mouse_scroll_inactive_with_cursorbind() for scb in [0, 1] for so in [0, 1, 2] let msg = $'scb={scb} so={so}' new | only let w1 = win_getid() setlocal cursorbind let &l:scb = scb let &l:so = so call setline(1, range(101, 109)) rightbelow vnew let w2 = win_getid() setlocal cursorbind let &l:scb = scb let &l:so = so call setline(1, range(101, 109)) normal! $ call assert_equal(3, col('.', w1), msg) call assert_equal(3, col('.', w2), msg) call Ntest_setmouse(1, 1) call feedkeys("\", 'xt') call assert_equal(4, line('w0', w1), msg) call assert_equal(4 + so, line('.', w1), msg) call assert_equal(1, line('w0', w2), msg) call assert_equal(1, line('.', w2), msg) call feedkeys("\", 'xt') call assert_equal(7, line('w0', w1), msg) call assert_equal(7 + so, line('.', w1), msg) call assert_equal(1, line('w0', w2), msg) call assert_equal(1, line('.', w2), msg) call feedkeys("\", 'xt') call assert_equal(4, line('w0', w1), msg) call assert_equal(7 + so, line('.', w1), msg) call assert_equal(1, line('w0', w2), msg) call assert_equal(1, line('.', w2), msg) call feedkeys("\", 'xt') call assert_equal(1, line('w0', w1), msg) call assert_equal(7 + so, line('.', w1), msg) call assert_equal(1, line('w0', w2), msg) call assert_equal(1, line('.', w2), msg) normal! 0 call assert_equal(1, line('.', w1), msg) call assert_equal(1, col('.', w1), msg) call assert_equal(1, line('.', w2), msg) call assert_equal(1, col('.', w2), msg) bwipe! bwipe! endfor endfor endfunc func Test_CtrlE_CtrlY_stop_at_end() enew call setline(1, ['one', 'two']) set number exe "normal \" call assert_equal([" 1 one "], ScreenLines(1, 10)) exe "normal \\\" call assert_equal([" 2 two "], ScreenLines(1, 10)) bwipe! set nonumber endfunc func Test_smoothscroll_CtrlE_CtrlY() CheckScreendump let lines =<< trim END vim9script setline(1, [ 'line one', 'word '->repeat(20), 'line three', 'long word '->repeat(7), 'line', 'line', 'line', ]) set smoothscroll :5 END call writefile(lines, 'XSmoothScroll', 'D') let buf = RunVimInTerminal('-S XSmoothScroll', #{rows: 12, cols: 40}) call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smoothscroll_1', {}) call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smoothscroll_2', {}) call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smoothscroll_3', {}) call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smoothscroll_4', {}) call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smoothscroll_5', {}) call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smoothscroll_6', {}) call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smoothscroll_7', {}) call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smoothscroll_8', {}) if has('folding') call term_sendkeys(buf, ":set foldmethod=indent\") " move the cursor so we can reuse the same dumps call term_sendkeys(buf, "5G") call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smoothscroll_1', {}) call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smoothscroll_2', {}) call term_sendkeys(buf, "7G") call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smoothscroll_7', {}) call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smoothscroll_8', {}) endif call StopVimInTerminal(buf) endfunc func Test_smoothscroll_multibyte() CheckScreendump let lines =<< trim END set scrolloff=0 smoothscroll call setline(1, [repeat('ϛ', 45), repeat('2', 36)]) exe "normal G35l\k" END call writefile(lines, 'XSmoothMultibyte', 'D') let buf = RunVimInTerminal('-S XSmoothMultibyte', #{rows: 6, cols: 40}) call VerifyScreenDump(buf, 'Test_smoothscroll_multi_1', {}) call StopVimInTerminal(buf) endfunc func Test_smoothscroll_number() CheckScreendump let lines =<< trim END vim9script setline(1, [ 'one ' .. 'word '->repeat(20), 'two ' .. 'long word '->repeat(7), 'line', 'line', 'line', ]) set smoothscroll set splitkeep=topline set number cpo+=n :3 def g:DoRel() set number relativenumber scrolloff=0 :%del setline(1, [ 'one', 'very long text '->repeat(12), 'three', ]) exe "normal 2Gzt\" enddef END call writefile(lines, 'XSmoothNumber', 'D') let buf = RunVimInTerminal('-S XSmoothNumber', #{rows: 12, cols: 40}) call VerifyScreenDump(buf, 'Test_smooth_number_1', {}) call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smooth_number_2', {}) call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smooth_number_3', {}) call term_sendkeys(buf, ":set cpo-=n\") call VerifyScreenDump(buf, 'Test_smooth_number_4', {}) call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smooth_number_5', {}) call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smooth_number_6', {}) call term_sendkeys(buf, ":botright split\\gg") call VerifyScreenDump(buf, 'Test_smooth_number_7', {}) call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smooth_number_8', {}) call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smooth_number_9', {}) call term_sendkeys(buf, ":close\") call term_sendkeys(buf, ":call DoRel()\") call VerifyScreenDump(buf, 'Test_smooth_number_10', {}) call StopVimInTerminal(buf) endfunc func Test_smoothscroll_list() CheckScreendump let lines =<< trim END vim9script set smoothscroll scrolloff=0 set list setline(1, [ 'one', 'very long text '->repeat(12), 'three', ]) exe "normal 2Gzt\" END call writefile(lines, 'XSmoothList', 'D') let buf = RunVimInTerminal('-S XSmoothList', #{rows: 8, cols: 40}) call VerifyScreenDump(buf, 'Test_smooth_list_1', {}) call term_sendkeys(buf, ":set listchars+=precedes:#\") call VerifyScreenDump(buf, 'Test_smooth_list_2', {}) call StopVimInTerminal(buf) endfunc func Test_smoothscroll_diff_mode() CheckScreendump CheckFeature diff let lines =<< trim END vim9script var text = 'just some text here' setline(1, text) set smoothscroll diffthis new setline(1, text) set smoothscroll diffthis END call writefile(lines, 'XSmoothDiff', 'D') let buf = RunVimInTerminal('-S XSmoothDiff', #{rows: 8}) call VerifyScreenDump(buf, 'Test_smooth_diff_1', {}) call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smooth_diff_1', {}) call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smooth_diff_1', {}) call StopVimInTerminal(buf) endfunc func Test_smoothscroll_diff_change_line_default() CheckScreendump CheckFeature diff " Uses the new diffopt default with indent-heuristic and inline:char let lines =<< trim END set diffopt=internal,filler,closeoff,indent-heuristic,inline:char,followwrap smoothscroll call setline(1, repeat(' abc', &columns)) call setline(2, 'bar') call setline(3, repeat(' abc', &columns)) vnew call setline(1, repeat(' abc', &columns)) call setline(2, 'foo') call setline(3, 'bar') call setline(4, repeat(' abc', &columns)) windo exe "normal! 2gg5\" windo diffthis END call writefile(lines, 'XSmoothDiffChangeLine', 'D') let buf = RunVimInTerminal('-S XSmoothDiffChangeLine', #{rows: 20, columns: 55}) call VerifyScreenDump(buf, 'Test_smooth_diff_change_line_1', {}) call term_sendkeys(buf, "Abar") call VerifyScreenDump(buf, 'Test_smooth_diff_change_line_2', {}) call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smooth_diff_change_line_3a', {}) call term_sendkeys(buf, "yyp") call VerifyScreenDump(buf, 'Test_smooth_diff_change_line_4', {}) call StopVimInTerminal(buf) endfunc func Test_smoothscroll_diff_change_line() CheckScreendump CheckFeature diff " Uses the old diffopt default let lines =<< trim END set diffopt=internal,filler,closeoff,followwrap,inline:simple smoothscroll call setline(1, repeat(' abc', &columns)) call setline(2, 'bar') call setline(3, repeat(' abc', &columns)) vnew call setline(1, repeat(' abc', &columns)) call setline(2, 'foo') call setline(3, 'bar') call setline(4, repeat(' abc', &columns)) windo exe "normal! 2gg5\" windo diffthis END call writefile(lines, 'XSmoothDiffChangeLine', 'D') let buf = RunVimInTerminal('-S XSmoothDiffChangeLine', #{rows: 20, columns: 55}) call VerifyScreenDump(buf, 'Test_smooth_diff_change_line_1', {}) call term_sendkeys(buf, "Abar") call VerifyScreenDump(buf, 'Test_smooth_diff_change_line_2', {}) call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smooth_diff_change_line_3', {}) call term_sendkeys(buf, "yyp") call VerifyScreenDump(buf, 'Test_smooth_diff_change_line_4', {}) call StopVimInTerminal(buf) endfunc func Test_smoothscroll_wrap_scrolloff_zero() CheckScreendump let lines =<< trim END vim9script setline(1, ['Line' .. (' with some text'->repeat(7))]->repeat(7)) set smoothscroll scrolloff=0 :3 END call writefile(lines, 'XSmoothWrap', 'D') let buf = RunVimInTerminal('-S XSmoothWrap', #{rows: 8, cols: 40}) call VerifyScreenDump(buf, 'Test_smooth_wrap_1', {}) " moving cursor down - whole bottom line shows call term_sendkeys(buf, "j") call VerifyScreenDump(buf, 'Test_smooth_wrap_2', {}) call term_sendkeys(buf, "\j") call VerifyScreenDump(buf, 'Test_smooth_wrap_3', {}) call term_sendkeys(buf, "G") call VerifyScreenDump(buf, 'Test_smooth_wrap_4', {}) call term_sendkeys(buf, "4\G") call VerifyScreenDump(buf, 'Test_smooth_wrap_4', {}) " moving cursor up right after the <<< marker - no need to show whole line call term_sendkeys(buf, "2gj3l2k") call VerifyScreenDump(buf, 'Test_smooth_wrap_5', {}) " moving cursor up where the <<< marker is - whole top line shows call term_sendkeys(buf, "2j02k") call VerifyScreenDump(buf, 'Test_smooth_wrap_6', {}) call StopVimInTerminal(buf) endfunc func Test_smoothscroll_wrap_long_line() CheckScreendump let lines =<< trim END vim9script setline(1, ['one', 'two', 'Line' .. (' with lots of text'->repeat(30)) .. ' end', 'four']) set smoothscroll scrolloff=0 normal 3G10|zt END call writefile(lines, 'XSmoothWrap', 'D') let buf = RunVimInTerminal('-S XSmoothWrap', #{rows: 6, cols: 40}) call VerifyScreenDump(buf, 'Test_smooth_long_1', {}) " scrolling up, cursor moves screen line down call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smooth_long_2', {}) call term_sendkeys(buf, "5\") call VerifyScreenDump(buf, 'Test_smooth_long_3', {}) " scrolling down, cursor moves screen line up call term_sendkeys(buf, "5\") call VerifyScreenDump(buf, 'Test_smooth_long_4', {}) call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smooth_long_5', {}) " 'scrolloff' set to 1, scrolling up, cursor moves screen line down call term_sendkeys(buf, ":set scrolloff=1\") call term_sendkeys(buf, "10|\") call VerifyScreenDump(buf, 'Test_smooth_long_6', {}) " 'scrolloff' set to 1, scrolling down, cursor moves screen line up call term_sendkeys(buf, "\") call term_sendkeys(buf, "gjgj") call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smooth_long_7', {}) " 'scrolloff' set to 2, scrolling up, cursor moves screen line down call term_sendkeys(buf, ":set scrolloff=2\") call term_sendkeys(buf, "10|\") call VerifyScreenDump(buf, 'Test_smooth_long_8', {}) " 'scrolloff' set to 2, scrolling down, cursor moves screen line up call term_sendkeys(buf, "\") call term_sendkeys(buf, "gj") call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smooth_long_9', {}) " 'scrolloff' set to 0, move cursor down one line. " Cursor should move properly, and since this is a really long line, it will " be put on top of the screen. call term_sendkeys(buf, ":set scrolloff=0\") call term_sendkeys(buf, "0j") call VerifyScreenDump(buf, 'Test_smooth_long_10', {}) " Test zt/zz/zb that they work properly when a long line is above it call term_sendkeys(buf, "zt") call VerifyScreenDump(buf, 'Test_smooth_long_11', {}) call term_sendkeys(buf, "zz") call VerifyScreenDump(buf, 'Test_smooth_long_12', {}) call term_sendkeys(buf, "zb") call VerifyScreenDump(buf, 'Test_smooth_long_13', {}) " Repeat the step and move the cursor down again. " This time, use a shorter long line that is barely long enough to span more " than one window. Note that the cursor is at the bottom this time because " Vim prefers to do so if we are scrolling a few lines only. call term_sendkeys(buf, ":call setline(1, ['one', 'two', 'Line' .. (' with lots of text'->repeat(10)) .. ' end', 'four'])\") " Currently visible lines were replaced, test that the lines and cursor " are correctly displayed. call VerifyScreenDump(buf, 'Test_smooth_long_14', {}) call term_sendkeys(buf, "3Gzt") call term_sendkeys(buf, "j") call VerifyScreenDump(buf, 'Test_smooth_long_15', {}) " Repeat the step but this time start it when the line is smooth-scrolled by " one line. This tests that the offset calculation is still correct and " still end up scrolling down to the next line with cursor at bottom of " screen. call term_sendkeys(buf, "3Gzt") call term_sendkeys(buf, "\j") call VerifyScreenDump(buf, 'Test_smooth_long_16', {}) call StopVimInTerminal(buf) endfunc func Test_smoothscroll_one_long_line() CheckScreendump let lines =<< trim END vim9script setline(1, 'with lots of text '->repeat(7)) set smoothscroll scrolloff=0 END call writefile(lines, 'XSmoothOneLong', 'D') let buf = RunVimInTerminal('-S XSmoothOneLong', #{rows: 6, cols: 40}) call VerifyScreenDump(buf, 'Test_smooth_one_long_1', {}) call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smooth_one_long_2', {}) call term_sendkeys(buf, "0") call VerifyScreenDump(buf, 'Test_smooth_one_long_1', {}) call StopVimInTerminal(buf) endfunc func Test_smoothscroll_long_line_showbreak() CheckScreendump let lines =<< trim END vim9script # a line that spans four screen lines setline(1, 'with lots of text in one line '->repeat(6)) set smoothscroll scrolloff=0 &showbreak = '+++ ' END call writefile(lines, 'XSmoothLongShowbreak', 'D') let buf = RunVimInTerminal('-S XSmoothLongShowbreak', #{rows: 6, cols: 40}) call VerifyScreenDump(buf, 'Test_smooth_long_showbreak_1', {}) call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smooth_long_showbreak_2', {}) call term_sendkeys(buf, "0") call VerifyScreenDump(buf, 'Test_smooth_long_showbreak_1', {}) call StopVimInTerminal(buf) endfunc " Check that 'smoothscroll' marker is drawn over double-width char correctly. " Run with multiple encodings. func Test_smoothscroll_marker_over_double_width() " Run this in a separate Vim instance to avoid messing up. let after =<< trim [CODE] scriptencoding utf-8 call setline(1, 'a'->repeat(&columns) .. '口'->repeat(10)) setlocal smoothscroll redraw exe "norm \" redraw " Check the chars one by one. Don't check the whole line concatenated. call assert_equal('<', screenstring(1, 1)) call assert_equal('<', screenstring(1, 2)) call assert_equal('<', screenstring(1, 3)) call assert_equal(' ', screenstring(1, 4)) call assert_equal('口', screenstring(1, 5)) call assert_equal('口', screenstring(1, 7)) call assert_equal('口', screenstring(1, 9)) call assert_equal('口', screenstring(1, 11)) call assert_equal('口', screenstring(1, 13)) call assert_equal('口', screenstring(1, 15)) call writefile(v:errors, 'Xresult') qall! [CODE] let encodings = ['utf-8', 'cp932', 'cp936', 'cp949', 'cp950'] if !has('win32') let encodings += ['euc-jp'] endif if has('nvim') let encodings = ['utf-8'] endif for enc in encodings let msg = 'enc=' .. enc if RunVim([], after, $'--clean --cmd "set encoding={enc}"') call assert_equal([], readfile('Xresult'), msg) endif call delete('Xresult') endfor endfunc " Same as the test above, but check the text actually shown on screen. " Only run with UTF-8 encoding. func Test_smoothscroll_marker_over_double_width_dump() CheckScreendump let lines =<< trim END call setline(1, 'a'->repeat(&columns) .. '口'->repeat(10)) setlocal smoothscroll END call writefile(lines, 'XSmoothMarkerOverDoubleWidth', 'D') let buf = RunVimInTerminal('-S XSmoothMarkerOverDoubleWidth', #{rows: 6, cols: 40}) call VerifyScreenDump(buf, 'Test_smooth_marker_over_double_width_1', {}) call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smooth_marker_over_double_width_2', {}) call StopVimInTerminal(buf) endfunc func s:check_col_calc(win_col, win_line, buf_col) call assert_equal(a:win_col, wincol()) call assert_equal(a:win_line, winline()) call assert_equal(a:buf_col, col('.')) endfunc " Test that if the current cursor is on a smooth scrolled line, we correctly " reposition it. Also check that we don't miscalculate the values by checking " the consistency between wincol() and col('.') as they are calculated " separately in code. func Test_smoothscroll_cursor_position() call NewWindow(10, 20) setl smoothscroll wrap call setline(1, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") call s:check_col_calc(1, 1, 1) exe "normal \" " Move down another line to avoid blocking the <<< display call s:check_col_calc(1, 2, 41) exe "normal \" call s:check_col_calc(1, 3, 41) " Test "g0/g" exe "normal gg\" norm $gkg0 call s:check_col_calc(4, 1, 24) " Test moving the cursor behind the <<< display with 'virtualedit' set virtualedit=all exe "normal \gkh" call s:check_col_calc(3, 2, 23) set virtualedit& normal gg3l exe "normal \" " Move down only 1 line when we are out of the range of the <<< display call s:check_col_calc(4, 1, 24) exe "normal \" call s:check_col_calc(4, 2, 24) normal ggg$ exe "normal \" call s:check_col_calc(20, 1, 40) exe "normal \" call s:check_col_calc(20, 2, 40) normal gg " Test number, where we have indented lines setl number call s:check_col_calc(5, 1, 1) exe "normal \" " Move down only 1 line when the <<< display is on the number column call s:check_col_calc(5, 1, 17) exe "normal \" call s:check_col_calc(5, 2, 17) normal ggg$ exe "normal \" call s:check_col_calc(20, 1, 32) exe "normal \" call s:check_col_calc(20, 2, 32) normal gg setl numberwidth=1 " Move down another line when numberwidth is too short to cover the whole " <<< display call s:check_col_calc(3, 1, 1) exe "normal \" call s:check_col_calc(3, 2, 37) exe "normal \" call s:check_col_calc(3, 3, 37) normal ggl " Only move 1 line down when we are just past the <<< display call s:check_col_calc(4, 1, 2) exe "normal \" call s:check_col_calc(4, 1, 20) exe "normal \" call s:check_col_calc(4, 2, 20) normal gg setl numberwidth& " Test number + showbreak, so test that the additional indentation works setl number showbreak=+++ call s:check_col_calc(5, 1, 1) exe "normal \" call s:check_col_calc(8, 1, 17) exe "normal \" call s:check_col_calc(8, 2, 17) normal gg " Test number + cpo+=n mode, where wrapped lines aren't indented setl number cpo+=n showbreak= call s:check_col_calc(5, 1, 1) exe "normal \" call s:check_col_calc(1, 2, 37) exe "normal \" call s:check_col_calc(1, 3, 37) normal gg " Test list + listchars "precedes", where there is always 1 overlap " regardless of number and cpo-=n. setl number list listchars=precedes:< cpo-=n call s:check_col_calc(5, 1, 1) exe "normal 3|\h" call s:check_col_calc(6, 1, 18) norm h call s:check_col_calc(5, 2, 17) normal gg bwipe! endfunc func Test_smoothscroll_cursor_scrolloff() call NewWindow(10, 20) setl smoothscroll wrap setl scrolloff=3 " 120 chars are 6 screen lines call setline(1, "abcdefghijklmnopqrstABCDEFGHIJKLMNOPQRSTabcdefghijklmnopqrstABCDEFGHIJKLMNOPQRSTabcdefghijklmnopqrstABCDEFGHIJKLMNOPQRST") call setline(2, "below") call s:check_col_calc(1, 1, 1) " CTRL-E shows "<<" call s:check_col_calc(1, 4, 81) " cursor on start of second line, "gk" moves into first line, skipcol doesn't " change exe "normal G0gk" call s:check_col_calc(1, 5, 101) " move cursor left one window width worth, scrolls one screen line exe "normal 20h" call s:check_col_calc(1, 5, 81) " move cursor left one window width worth, scrolls one screen line exe "normal 20h" call s:check_col_calc(1, 4, 61) " cursor on last line, "gk" should not cause a scroll set scrolloff=0 normal G0 call s:check_col_calc(1, 7, 1) normal gk call s:check_col_calc(1, 6, 101) bwipe! endfunc " Test that mouse picking is still accurate when we have smooth scrolled lines func Test_smoothscroll_mouse_pos() CheckNotGui CheckUnix let save_mouse = &mouse "let save_term = &term "let save_ttymouse = &ttymouse set mouse=a "term=xterm ttymouse=xterm2 call NewWindow(10, 20) setl smoothscroll wrap " First line will wrap to 3 physical lines. 2nd/3rd lines are short lines. call setline(1, ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", "line 2", "line 3"]) func s:check_mouse_click(row, col, buf_row, buf_col) call MouseLeftClick(a:row, a:col) call assert_equal(a:col, wincol()) call assert_equal(a:row, winline()) call assert_equal(a:buf_row, line('.')) call assert_equal(a:buf_col, col('.')) endfunc " Check that clicking without scroll works first. call s:check_mouse_click(3, 5, 1, 45) call s:check_mouse_click(4, 1, 2, 1) call s:check_mouse_click(4, 6, 2, 6) call s:check_mouse_click(5, 1, 3, 1) call s:check_mouse_click(5, 6, 3, 6) " Smooth scroll, and checks that this didn't mess up mouse clicking exe "normal \" call s:check_mouse_click(2, 5, 1, 45) call s:check_mouse_click(3, 1, 2, 1) call s:check_mouse_click(3, 6, 2, 6) call s:check_mouse_click(4, 1, 3, 1) call s:check_mouse_click(4, 6, 3, 6) exe "normal \" call s:check_mouse_click(1, 5, 1, 45) call s:check_mouse_click(2, 1, 2, 1) call s:check_mouse_click(2, 6, 2, 6) call s:check_mouse_click(3, 1, 3, 1) call s:check_mouse_click(3, 6, 3, 6) " Make a new first line 11 physical lines tall so it's taller than window " height, to test overflow calculations with really long lines wrapping. normal gg call setline(1, "12345678901234567890"->repeat(11)) exe "normal 6\" call s:check_mouse_click(5, 1, 1, 201) call s:check_mouse_click(6, 1, 2, 1) call s:check_mouse_click(7, 1, 3, 1) let &mouse = save_mouse "let &term = save_term "let &ttymouse = save_ttymouse bwipe! endfunc " this was dividing by zero func Test_smoothscroll_zero_width() CheckScreendump let lines =<< trim END winsize 0 0 vsplit vsplit vsplit vsplit vsplit sil norm H set wrap set smoothscroll set number END call writefile(lines, 'XSmoothScrollZero', 'D') let buf = RunVimInTerminal('-u NONE -i NONE -n -m -X -Z -e -s -S XSmoothScrollZero', #{rows: 6, cols: 60, wait_for_ruler: 0}) call VerifyScreenDump(buf, 'Test_smoothscroll_zero_1', {}) call term_sendkeys(buf, ":sil norm \\\\\") call VerifyScreenDump(buf, 'Test_smoothscroll_zero_2', {}) call StopVimInTerminal(buf) endfunc " this was unnecessarily inserting lines func Test_smoothscroll_ins_lines() CheckScreendump let lines =<< trim END set wrap set smoothscroll set scrolloff=0 set conceallevel=2 call setline(1, [ \'line one' .. 'with lots of text in one line '->repeat(2), \'line two', \'line three', \'line four', \'line five' \]) END call writefile(lines, 'XSmoothScrollInsLines', 'D') let buf = RunVimInTerminal('-S XSmoothScrollInsLines', #{rows: 6, cols: 40}) call term_sendkeys(buf, "\gjgk") call VerifyScreenDump(buf, 'Test_smooth_ins_lines', {}) call StopVimInTerminal(buf) endfunc " this placed the cursor in the command line func Test_smoothscroll_cursormoved_line() CheckScreendump let lines =<< trim END set smoothscroll call setline(1, [ \'', \'_'->repeat(&lines * &columns), \(('_')->repeat(&columns - 2) .. 'xxx')->repeat(2) \]) autocmd CursorMoved * eval [line('w0'), line('w$')] call search('xxx') END call writefile(lines, 'XSmoothCursorMovedLine', 'D') let buf = RunVimInTerminal('-S XSmoothCursorMovedLine', #{rows: 6}) call VerifyScreenDump(buf, 'Test_smooth_cursormoved_line', {}) call StopVimInTerminal(buf) endfunc func Test_smoothscroll_eob() CheckScreendump let lines =<< trim END set smoothscroll call setline(1, ['']->repeat(100)) norm G END call writefile(lines, 'XSmoothEob', 'D') let buf = RunVimInTerminal('-S XSmoothEob', #{rows: 10}) " does not scroll halfway when scrolling to end of buffer call VerifyScreenDump(buf, 'Test_smooth_eob_1', {}) " cursor is not placed below window call term_sendkeys(buf, ":call setline(92, 'a'->repeat(100))\\\G") call VerifyScreenDump(buf, 'Test_smooth_eob_2', {}) call StopVimInTerminal(buf) endfunc " skipcol should not reset when doing incremental search on the same word func Test_smoothscroll_incsearch() CheckRunVimInTerminal let lines =<< trim END set smoothscroll number scrolloff=0 incsearch call setline(1, repeat([''], 20)) call setline(11, repeat('a', 100)) call setline(14, 'bbbb') END call writefile(lines, 'XSmoothIncsearch', 'D') let buf = RunVimInTerminal('-S XSmoothIncsearch', #{rows: 8, cols: 40}) let g:test_is_flaky = 1 call term_sendkeys(buf, "/b") call WaitForAssert({-> assert_match('^/b\s*$', term_getline(buf, 8))}, 1000) let view1 = map(range(1, 7), {_, i -> term_getline(buf, i)}) call term_sendkeys(buf, "b") call WaitForAssert({-> assert_match('^/bb\s*$', term_getline(buf, 8))}, 1000) call assert_equal(view1, map(range(1, 7), {_, i -> term_getline(buf, i)})) call term_sendkeys(buf, "b") call WaitForAssert({-> assert_match('^/bbb\s*$', term_getline(buf, 8))}, 1000) call assert_equal(view1, map(range(1, 7), {_, i -> term_getline(buf, i)})) call term_sendkeys(buf, "b") call WaitForAssert({-> assert_match('^/bbbb\s*$', term_getline(buf, 8))}, 1000) call assert_equal(view1, map(range(1, 7), {_, i -> term_getline(buf, i)})) call term_sendkeys(buf, "\") call StopVimInTerminal(buf) endfunc " Test scrolling multiple lines and stopping at non-zero skipcol. func Test_smoothscroll_multi_skipcol() CheckScreendump let lines =<< trim END setlocal cursorline scrolloff=0 smoothscroll call setline(1, repeat([''], 8)) call setline(3, repeat('a', 50)) call setline(4, repeat('a', 50)) call setline(7, 'bbb') call setline(8, 'ccc') redraw END call writefile(lines, 'XSmoothMultiSkipcol', 'D') let buf = RunVimInTerminal('-S XSmoothMultiSkipcol', #{rows: 10, cols: 40}) call VerifyScreenDump(buf, 'Test_smooth_multi_skipcol_1', {}) call term_sendkeys(buf, "3\") call VerifyScreenDump(buf, 'Test_smooth_multi_skipcol_2', {}) call term_sendkeys(buf, "2\") call VerifyScreenDump(buf, 'Test_smooth_multi_skipcol_3', {}) call StopVimInTerminal(buf) endfunc " this was dividing by zero bug in scroll_cursor_bot func Test_smoothscroll_zero_width_scroll_cursor_bot_noruler() CheckScreendump let lines =<< trim END set noruler silent normal yy silent normal 19p set cpoptions+=n vsplit vertical resize 0 set foldcolumn=1 set number set smoothscroll silent normal 20G END call writefile(lines, 'XSmoothScrollZeroBot', 'D') let buf = RunVimInTerminal('-u NONE -S XSmoothScrollZeroBot', #{rows: 19}) call VerifyScreenDump(buf, 'Test_smoothscroll_zero_bot', {}) call StopVimInTerminal(buf) endfunc func Test_smoothscroll_zero_width_scroll_cursor_bot_ruler() CheckScreendump let lines =<< trim END set ruler silent normal yy silent normal 19p set cpoptions+=n vsplit vertical resize 0 set foldcolumn=1 set number set smoothscroll silent normal 20G END call writefile(lines, 'XSmoothScrollZeroBot', 'D') let buf = RunVimInTerminal('-u NONE -S XSmoothScrollZeroBot', #{rows: 19}) call VerifyScreenDump(buf, 'Test_smoothscroll_zero_bot_ruler', {}) call StopVimInTerminal(buf) endfunc " scroll_cursor_top() should reset skipcol when it changes topline func Test_smoothscroll_cursor_top_noruler() CheckScreendump let lines =<< trim END set smoothscroll scrolloff=2 noruler new | 11resize | wincmd j call setline(1, ['line1', 'line2', 'line3'->repeat(20), 'line4']) exe "norm G3\k" END call writefile(lines, 'XSmoothScrollCursorTop', 'D') let buf = RunVimInTerminal('-u NONE -S XSmoothScrollCursorTop', #{rows: 12, cols: 40}) call VerifyScreenDump(buf, 'Test_smoothscroll_cursor_top', {}) call StopVimInTerminal(buf) endfunc func Test_smoothscroll_cursor_top_ruler() CheckScreendump let lines =<< trim END set smoothscroll scrolloff=2 ruler new | 11resize | wincmd j call setline(1, ['line1', 'line2', 'line3'->repeat(20), 'line4']) exe "norm G3\k" END call writefile(lines, 'XSmoothScrollCursorTop', 'D') let buf = RunVimInTerminal('-u NONE -S XSmoothScrollCursorTop', #{rows: 12, cols: 40}) call VerifyScreenDump(buf, 'Test_smoothscroll_cursor_ru_top', {}) call StopVimInTerminal(buf) endfunc " Division by zero, shouldn't crash func Test_smoothscroll_crash() CheckScreendump let lines =<< trim END 20 new vsp put =repeat('aaaa', 20) set nu fdc=1 smoothscroll cpo+=n vert resize 0 exe "norm! 0\" END call writefile(lines, 'XSmoothScrollCrash', 'D') let buf = RunVimInTerminal('-u NONE -S XSmoothScrollCrash', #{rows: 12, cols: 40}) call term_sendkeys(buf, "2\\") call StopVimInTerminal(buf) endfunc func Test_smoothscroll_insert_bottom_noruler() CheckScreendump let lines =<< trim END call setline(1, repeat([repeat('A very long line ...', 10)], 5)) set wrap smoothscroll scrolloff=0 noruler END call writefile(lines, 'XSmoothScrollInsertBottom', 'D') let buf = RunVimInTerminal('-u NONE -S XSmoothScrollInsertBottom', #{rows: 9, cols: 40}) call term_sendkeys(buf, "Go123456789\") call VerifyScreenDump(buf, 'Test_smoothscroll_insert_bottom', {}) call StopVimInTerminal(buf) endfunc func Test_smoothscroll_insert_bottom_ruler() CheckScreendump let lines =<< trim END call setline(1, repeat([repeat('A very long line ...', 10)], 5)) set wrap smoothscroll scrolloff=0 ruler END call writefile(lines, 'XSmoothScrollInsertBottom', 'D') let buf = RunVimInTerminal('-u NONE -S XSmoothScrollInsertBottom', #{rows: 9, cols: 40}) call term_sendkeys(buf, "Go123456789\") call VerifyScreenDump(buf, 'Test_smoothscroll_insert_bottom_ruler', {}) call StopVimInTerminal(buf) endfunc func Test_smoothscroll_in_qf_window_noruler() CheckFeature quickfix CheckScreendump let lines =<< trim END set nocompatible display=lastline noruler copen 5 setlocal number smoothscroll let g:l = [{'text': 'foo'}] + repeat([{'text': join(range(30))}], 10) call setqflist(g:l, 'r') normal! G wincmd t let g:l1 = [{'text': join(range(1000))}] END call writefile(lines, 'XSmoothScrollInQfWindow', 'D') let buf = RunVimInTerminal('-u NONE -S XSmoothScrollInQfWindow', #{rows: 20, cols: 60}) call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_1', {}) call term_sendkeys(buf, ":call setqflist([], 'r')\") call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_2', {}) call term_sendkeys(buf, ":call setqflist(g:l, 'r')\") call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_3', {}) call term_sendkeys(buf, ":call setqflist(g:l1, 'r')\") call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_4', {}) call term_sendkeys(buf, "\b$\t") call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_5', {}) call term_sendkeys(buf, ":call setqflist([], 'r')\") call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_2', {}) call term_sendkeys(buf, ":call setqflist(g:l1, 'r')\") call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_4', {}) call term_sendkeys(buf, "\b$\t") call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_5', {}) call term_sendkeys(buf, ":call setqflist(g:l, 'r')\") call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_3', {}) call StopVimInTerminal(buf) endfunc func Test_smoothscroll_in_qf_window_ruler() CheckFeature quickfix CheckScreendump let lines =<< trim END set nocompatible display=lastline ruler copen 5 setlocal number smoothscroll let g:l = [{'text': 'foo'}] + repeat([{'text': join(range(30))}], 10) call setqflist(g:l, 'r') normal! G wincmd t let g:l1 = [{'text': join(range(1000))}] END call writefile(lines, 'XSmoothScrollInQfWindow', 'D') let buf = RunVimInTerminal('-u NONE -S XSmoothScrollInQfWindow', #{rows: 20, cols: 60}) call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_ru_1', {}) call term_sendkeys(buf, ":call setqflist([], 'r')\") call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_ru_2', {}) call term_sendkeys(buf, ":call setqflist(g:l, 'r')\") call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_ru_3', {}) call term_sendkeys(buf, ":call setqflist(g:l1, 'r')\") call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_ru_4', {}) call term_sendkeys(buf, "\b$\t") call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_ru_5', {}) call term_sendkeys(buf, ":call setqflist([], 'r')\") call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_ru_2', {}) call term_sendkeys(buf, ":call setqflist(g:l1, 'r')\") call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_ru_4', {}) call term_sendkeys(buf, "\b$\t") call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_ru_5', {}) call term_sendkeys(buf, ":call setqflist(g:l, 'r')\") call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_ru_3', {}) call StopVimInTerminal(buf) endfunc func Test_smoothscroll_in_zero_width_window() set cpo+=n number smoothscroll set winwidth=99999 winminwidth=0 vsplit call assert_equal(0, winwidth(winnr('#'))) call win_execute(win_getid(winnr('#')), "norm! \") only! set winwidth& winminwidth& set cpo-=n nonumber nosmoothscroll endfunc func Test_smoothscroll_textoff_small_winwidth() set smoothscroll number call setline(1, 'llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch') vsplit let textoff = getwininfo(win_getid())[0].textoff execute 'vertical resize' textoff + 1 redraw call assert_equal(0, winsaveview().skipcol) execute "normal! 0\" redraw call assert_equal(1, winsaveview().skipcol) execute 'vertical resize' textoff - 1 " This caused a signed integer overflow. redraw call assert_equal(1, winsaveview().skipcol) execute 'vertical resize' textoff " This caused an infinite loop. redraw call assert_equal(1, winsaveview().skipcol) %bw! set smoothscroll& number& endfunc func Test_smoothscroll_page() call NewWindow(10, 40) setlocal smoothscroll call setline(1, 'abcde '->repeat(150)) exe "norm! \" call assert_equal(400, winsaveview().skipcol) exe "norm! \" call assert_equal(800, winsaveview().skipcol) exe "norm! \" call assert_equal(880, winsaveview().skipcol) exe "norm! \" call assert_equal(480, winsaveview().skipcol) exe "norm! \" call assert_equal(80, winsaveview().skipcol) exe "norm! \" call assert_equal(0, winsaveview().skipcol) " Half-page scrolling does not go beyond end of buffer and moves the cursor. " Even with 'nostartofline', the correct amount of lines is scrolled. setl nostartofline exe "norm! 15|\" call assert_equal(200, winsaveview().skipcol) call assert_equal(215, col('.')) exe "norm! \" call assert_equal(400, winsaveview().skipcol) call assert_equal(415, col('.')) exe "norm! \" call assert_equal(520, winsaveview().skipcol) call assert_equal(615, col('.')) exe "norm! \" call assert_equal(520, winsaveview().skipcol) call assert_equal(815, col('.')) exe "norm! \" call assert_equal(520, winsaveview().skipcol) call assert_equal(895, col('.')) exe "norm! \" call assert_equal(320, winsaveview().skipcol) call assert_equal(695, col('.')) exe "norm! \" call assert_equal(120, winsaveview().skipcol) call assert_equal(495, col('.')) exe "norm! \" call assert_equal(0, winsaveview().skipcol) call assert_equal(295, col('.')) exe "norm! \" call assert_equal(0, winsaveview().skipcol) call assert_equal(95, col('.')) exe "norm! \" call assert_equal(0, winsaveview().skipcol) call assert_equal(15, col('.')) bwipe! endfunc func Test_smoothscroll_next_topline() call NewWindow(10, 40) setlocal smoothscroll call setline(1, ['abcde '->repeat(150)]->repeat(2)) " Scrolling a screenline that causes the cursor to move to the next buffer " line should not skip part of that line to bring the cursor into view. exe "norm! 22\" call assert_equal(880, winsaveview().skipcol) exe "norm! \" redraw call assert_equal(0, winsaveview().skipcol) " Also when scrolling back. exe "norm! G\" redraw call assert_equal(880, winsaveview().skipcol) " Cursor in correct place when not in the first screenline of a buffer line. exe "norm! gg4gj20\\" redraw call assert_equal(2, line('w0')) " Cursor does not end up above topline, adjusting topline later. setlocal nu cpo+=n exe "norm! G$g013\" redraw call assert_equal(2, line('.')) call assert_equal(0, winsaveview().skipcol) set cpo-=n bwipe! endfunc func Test_smoothscroll_long_line_zb() call NewWindow(10, 40) call setline(1, 'abcde '->repeat(150)) " Also works without 'smoothscroll' when last line of buffer doesn't fit. " Used to set topline to buffer line count plus one, causing an empty screen. norm zb redraw call assert_equal(1, winsaveview().topline) " Moving cursor to bottom works on line that doesn't fit with 'smoothscroll'. " Skipcol was adjusted later for cursor being on not visible part of line. setlocal smoothscroll norm zb redraw call assert_equal(520, winsaveview().skipcol) bwipe! endfunc func Test_smooth_long_scrolloff_noruler() CheckScreendump let lines =<< trim END set smoothscroll scrolloff=3 set noruler call setline(1, ['one', 'two long '->repeat(100), 'three', 'four', 'five', 'six']) END call writefile(lines, 'XSmoothLongScrolloff', 'D') let buf = RunVimInTerminal('-u NONE -S XSmoothLongScrolloff', #{rows: 8, cols: 40}) call term_sendkeys(buf, ":norm j721|\") call VerifyScreenDump(buf, 'Test_smooth_long_scrolloff_1', {}) call term_sendkeys(buf, "gj") call VerifyScreenDump(buf, 'Test_smooth_long_scrolloff_2', {}) call term_sendkeys(buf, "gj") call VerifyScreenDump(buf, 'Test_smooth_long_scrolloff_3', {}) call term_sendkeys(buf, "gj") call VerifyScreenDump(buf, 'Test_smooth_long_scrolloff_4', {}) call term_sendkeys(buf, "gj") call VerifyScreenDump(buf, 'Test_smooth_long_scrolloff_5', {}) call term_sendkeys(buf, "gj") call VerifyScreenDump(buf, 'Test_smooth_long_scrolloff_6', {}) call term_sendkeys(buf, "gk") call VerifyScreenDump(buf, 'Test_smooth_long_scrolloff_7', {}) call StopVimInTerminal(buf) endfunc func Test_smooth_long_scrolloff_ruler() CheckScreendump let lines =<< trim END set smoothscroll scrolloff=3 ruler call setline(1, ['one', 'two long '->repeat(100), 'three', 'four', 'five', 'six']) END call writefile(lines, 'XSmoothLongScrolloff', 'D') let buf = RunVimInTerminal('-u NONE -S XSmoothLongScrolloff', #{rows: 8, cols: 40}) call term_sendkeys(buf, ":norm j721|\") call VerifyScreenDump(buf, 'Test_smooth_long_scrolloff_ru_1', {}) call term_sendkeys(buf, "gj") call VerifyScreenDump(buf, 'Test_smooth_long_scrolloff_ru_2', {}) call term_sendkeys(buf, "gj") call VerifyScreenDump(buf, 'Test_smooth_long_scrolloff_ru_3', {}) call term_sendkeys(buf, "gj") call VerifyScreenDump(buf, 'Test_smooth_long_scrolloff_ru_4', {}) call term_sendkeys(buf, "gj") call VerifyScreenDump(buf, 'Test_smooth_long_scrolloff_ru_5', {}) call term_sendkeys(buf, "gj") call VerifyScreenDump(buf, 'Test_smooth_long_scrolloff_ru_6', {}) call term_sendkeys(buf, "gk") call VerifyScreenDump(buf, 'Test_smooth_long_scrolloff_ru_7', {}) call StopVimInTerminal(buf) endfunc func Test_smoothscroll_listchars_eol() call NewWindow(10, 40) setlocal list listchars=eol:$ scrolloff=0 smoothscroll call setline(1, repeat('-', 40)) call append(1, repeat(['foobar'], 10)) normal! G call assert_equal(2, line('w0')) call assert_equal(0, winsaveview().skipcol) exe "normal! \" call assert_equal(1, line('w0')) call assert_equal(40, winsaveview().skipcol) exe "normal! \" call assert_equal(1, line('w0')) call assert_equal(0, winsaveview().skipcol) exe "normal! \" call assert_equal(1, line('w0')) call assert_equal(0, winsaveview().skipcol) exe "normal! \" call assert_equal(1, line('w0')) call assert_equal(40, winsaveview().skipcol) exe "normal! \" call assert_equal(2, line('w0')) call assert_equal(0, winsaveview().skipcol) for ve in ['', 'all', 'onemore'] let &virtualedit = ve normal! gg call assert_equal(1, line('w0')) call assert_equal(0, winsaveview().skipcol) exe "normal! \" redraw " redrawing should not cause another scroll call assert_equal(1, line('w0')) call assert_equal(40, winsaveview().skipcol) exe "normal! \" redraw call assert_equal(2, line('w0')) call assert_equal(0, winsaveview().skipcol) if ve != 'all' call assert_equal([0, 2, 1, 0], getpos('.')) endif endfor set virtualedit& bwipe! endfunc " scrolloffpad contract: " - augment scrolloff only under EOF pressure (insufficient real lines below); " - do not change explicit "z" viewport placement command semantics; " - current scope is EOF-only, so BOF behavior remains unchanged. func Test_scrolloffpad_zb_keeps_bottom_command_semantics() new resize 12 setlocal scrolloff=10 call setline(1, map(range(1, 300), 'printf("line %d", v:val)')) setlocal scrolloffpad=0 normal! gg150Gzb let baseline = [line('.'), line('w$'), winline()] setlocal scrolloffpad=1 normal! gg150Gzb call assert_equal(baseline, [line('.'), line('w$'), winline()]) bwipe! endfunc func Test_scrolloffpad_zminus_keeps_bottom_beginline_semantics() new resize 12 setlocal scrolloff=10 call setline(1, map(range(1, 300), 'printf(" line %d", v:val)')) setlocal scrolloffpad=0 normal! gg150Gz- let baseline = [line('.'), line('w$'), winline(), col('.')] call assert_equal(match(getline('.'), '\S') + 1, col('.')) setlocal scrolloffpad=1 normal! gg150Gz- call assert_equal(baseline, [line('.'), line('w$'), winline(), col('.')]) call assert_equal(match(getline('.'), '\S') + 1, col('.')) bwipe! endfunc func Test_scrolloffpad_zb_is_one_shot_then_scrolloff_reapplies() new resize 12 setlocal scrolloff=10 call setline(1, map(range(1, 300), 'printf("line %d", v:val)')) let after_zb = {} let after_j = {} for sop in [0, 1] let &l:scrolloffpad = sop normal! gg150Gzb let after_zb[sop] = [line('.'), line('w$'), winline(), winsaveview().topline] normal! j let after_j[sop] = [line('.'), line('w$'), winline(), winsaveview().topline] call assert_notequal(after_zb[sop][3], after_j[sop][3]) call assert_true(line('.') < line('w$')) endfor call assert_equal(after_zb[0], after_zb[1]) call assert_equal(after_j[0], after_j[1]) bwipe! endfunc func Test_scrolloffpad_has_no_mid_buffer_effect() new resize 12 setlocal scrolloff=10 scrolloffpad=0 call setline(1, map(range(1, 500), 'printf("line %d", v:val)')) normal! gg150G let topline_without_pad = winsaveview().topline setlocal scrolloffpad=1 normal! gg150G let topline_with_pad = winsaveview().topline call assert_equal(topline_without_pad, topline_with_pad) bwipe! endfunc func Test_scrolloffpad_changes_eof_pressure_only() new resize 12 setlocal scrolloff=10 scrolloffpad=0 call setline(1, map(range(1, 200), 'printf("line %d", v:val)')) normal! ggG let view_without_pad = winsaveview() let cursor_without_pad = line('.') let row_without_pad = winline() setlocal scrolloffpad=1 normal! ggG let view_with_pad = winsaveview() let row_with_pad = winline() call assert_equal(line('$'), line('.')) call assert_equal(cursor_without_pad, line('.')) call assert_notequal(view_without_pad.topline, view_with_pad.topline) call assert_true(row_with_pad < row_without_pad) bwipe! endfunc func Test_scrolloffpad_large_scrolloff_no_overflow() new resize 12 call setline(1, map(range(1, 200), 'printf("line %d", v:val)')) setlocal scrolloff=2147483647 scrolloffpad=0 normal! ggG let view_without_pad = winsaveview() let row_without_pad = winline() setlocal scrolloffpad=1 normal! ggG let view_with_pad = winsaveview() let row_with_pad = winline() call assert_equal(line('$'), line('.')) call assert_notequal(view_without_pad.topline, view_with_pad.topline) call assert_true(row_with_pad < row_without_pad) bwipe! endfunc func Test_scrolloffpad_boolean_gate_values() new resize 12 setlocal scrolloff=10 call setline(1, map(range(1, 200), 'printf("line %d", v:val)')) let views = {} let rows = {} for sop in [0, 1, 2] let &l:scrolloffpad = sop normal! ggG let views[sop] = winsaveview() let rows[sop] = winline() call assert_equal(line('$'), line('.')) endfor call assert_equal(views[1].topline, views[2].topline) call assert_equal(rows[1], rows[2]) call assert_notequal(views[0].topline, views[1].topline) call assert_true(rows[1] < rows[0]) bwipe! endfunc func Test_scrolloffpad_requires_scrolloff_nonzero() new resize 12 call setline(1, map(range(1, 200), 'printf("line %d", v:val)')) let states = {} for so in [0, 10] let states[so] = {} for sop in [0, 1] let &l:scrolloff = so let &l:scrolloffpad = sop normal! ggG let states[so][sop] = [line('.'), line('w0'), line('w$'), winline()] call assert_equal(line('$'), line('.')) endfor endfor call assert_equal(states[0][0], states[0][1]) call assert_notequal(states[10][0], states[10][1]) call assert_true(states[10][1][3] < states[10][0][3]) bwipe! endfunc func Test_scrolloffpad_search_to_eof() new resize 12 setlocal scrolloff=10 call setline(1, map(range(1, 200), 'printf("line %d", v:val)')) call setline(line('$'), 'EOF TARGET') let states = {} for sop in [0, 1] let &l:scrolloffpad = sop normal! gg call assert_true(search('EOF TARGET') > 0) let states[sop] = [line('.'), line('w0'), line('w$'), winline()] call assert_equal(line('$'), line('.')) endfor call assert_notequal(states[0], states[1]) call assert_true(states[1][3] < states[0][3]) bwipe! endfunc func Test_scrolloffpad_paging_to_eof() new resize 12 setlocal scrolloff=10 call setline(1, map(range(1, 240), 'printf("line %d", v:val)')) let states = {} for sop in [0, 1] let &l:scrolloffpad = sop normal! gg let prev = -1 for _ in range(1, 200) execute "normal! \" if line('.') == prev break endif let prev = line('.') endfor let states[sop] = [line('.'), line('w0'), line('w$'), winline()] call assert_equal(line('$'), line('w$')) endfor call assert_notequal(states[0], states[1]) call assert_true(states[1][3] < states[0][3]) bwipe! endfunc func Test_scrolloffpad_autocmd_append_at_eof() let states = {} for sop in [0, 1] new resize 12 setlocal scrolloff=10 let &l:scrolloffpad = sop call setline(1, map(range(1, 120), 'printf("line %d", v:val)')) let b:scrolloffpad_appended = 0 augroup ScrolloffpadAppendAtEof autocmd! autocmd CursorMoved if b:scrolloffpad_appended == 0 && line('.') == line('$') | call append('$', 'appended') | let b:scrolloffpad_appended = 1 | endif augroup END normal! ggG doautocmd CursorMoved let states[sop] = [ \ line('.'), \ line('$'), \ line('w0'), \ line('w$'), \ winline(), \ b:scrolloffpad_appended, \ ] call assert_equal(1, b:scrolloffpad_appended) call assert_equal(states[sop][1] - 1, states[sop][0]) augroup ScrolloffpadAppendAtEof autocmd! augroup END bwipe! endfor call assert_notequal(states[0], states[1]) call assert_true(states[1][4] < states[0][4]) endfunc func Test_scrolloffpad_eof_no_reverse_scroll_on_j() new resize 20 setlocal scrolloff=20 scrolloffpad=1 call setline(1, map(range(1, 80), 'printf("line %d", v:val)')) normal! gg let prev_topline = winsaveview().topline for lnum in range(2, line('$')) normal! j let cur_topline = winsaveview().topline call assert_true( \ cur_topline >= prev_topline, \ printf('topline moved backwards at line %d: %d -> %d', \ lnum, prev_topline, cur_topline)) let prev_topline = cur_topline endfor bwipe! endfunc func Test_scrolloffpad_bof_unchanged() new resize 12 setlocal scrolloff=10 scrolloffpad=0 call setline(1, map(range(1, 200), 'printf("line %d", v:val)')) normal! Ggg let view_without_pad = winsaveview() let w0_without_pad = line('w0') setlocal scrolloffpad=1 normal! Ggg let view_with_pad = winsaveview() let w0_with_pad = line('w0') call assert_equal(1, w0_without_pad) call assert_equal(1, w0_with_pad) call assert_equal(view_without_pad.topline, view_with_pad.topline) bwipe! endfunc func Test_scrolloffpad_mouse_drag_uses_drag_scrolloff() CheckFeature mouse let save_mouse = &mouse set mouse=a new resize 20 call setline(1, map(range(1, 240), 'printf("line %d", v:val)')) setlocal scrolloff=50 let after_drag = {} for sop in [0, 1] let &l:scrolloffpad = sop normal! gg160Gzt normal! v call Ntest_setmouse(2, 1) call feedkeys("\", 'xt') call Ntest_setmouse(3, 1) call feedkeys("\", 'xt') let after_drag[sop] = [winsaveview().topline, line('.'), winline()] call feedkeys("\", 'xt') endfor call assert_equal(after_drag[0], after_drag[1]) bwipe! let &mouse = save_mouse endfunc func Test_scrolloffpad_basic() CheckScreendump CheckRunVimInTerminal let save_termwinsize = &termwinsize set termwinsize= let lines =<< trim END set scrolloff=10 set scrolloffpad=5 enew! call setline(1, map(range(1, 100), 'printf("line %d", v:val)')) normal! gg END call writefile(lines, 'XScrolloffpadBasic', 'D') let buf = RunVimInTerminal('-S XScrolloffpadBasic', {'rows': 20, 'cols': 78}) " Enabled: scrolloffpad > 0, expect EOF centering/padding call term_sendkeys(buf, "\:\normal! G\") call term_sendkeys(buf, "\") call TermWait(buf) call VerifyScreenDump(buf, 'Test_scrolloffpad_basic_1', {}) " Beginning-of-file is unchanged (Top) call term_sendkeys(buf, "\:\normal! gg\") call term_sendkeys(buf, "\") call TermWait(buf) call VerifyScreenDump(buf, 'Test_scrolloffpad_basic_2', {}) " Gating: disable scrolloffpad, then go to EOF again " Expect normal EOF behavior (no extra centering/padding) call term_sendkeys(buf, "\:\set scrolloffpad=0\") call term_sendkeys(buf, "\:\normal! G\") call term_sendkeys(buf, "\") call TermWait(buf) call VerifyScreenDump(buf, 'Test_scrolloffpad_basic_3', {}) call StopVimInTerminal(buf) let &termwinsize = save_termwinsize endfunc func Test_scrolloffpad_smoothscroll() CheckScreendump CheckRunVimInTerminal let save_termwinsize = &termwinsize set termwinsize= let lines =<< trim END set smoothscroll scrolloff=10 scrolloffpad=1 enew! call setline(1, map(range(1, 100), 'printf("line %d", v:val)')) normal! gg END call writefile(lines, 'XScrolloffpadSmoothscroll', 'D') let buf = RunVimInTerminal('-S XScrolloffpadSmoothscroll', #{rows: 20, cols: 78}) call term_sendkeys(buf, "\:\normal! G\") call term_sendkeys(buf, "\") call TermWait(buf) call VerifyScreenDump(buf, 'Test_scrolloffpad_smoothscroll_1', {}) call term_sendkeys(buf, "\:\call setline(line('$'), repeat('LONG ', 30))\") call term_sendkeys(buf, "\:\normal! 41|\") call term_sendkeys(buf, "\") call TermWait(buf) call VerifyScreenDump(buf, 'Test_scrolloffpad_smoothscroll_2', {}) call StopVimInTerminal(buf) let &termwinsize = save_termwinsize endfunc func Test_scrolloffpad_insert_eof() let save_so = &scrolloff let save_sop = &scrolloffpad set scrolloff=10 scrolloffpad=1 enew! call setline(1, map(range(1, 200), 'printf("line %d", v:val)')) normal! G let topline_before = winsaveview().topline call feedkeys("i\", 'xt') call assert_equal(topline_before, winsaveview().topline) exe "normal! \" let topline_after = winsaveview().topline call feedkeys("i\", 'xt') call assert_equal(topline_after, winsaveview().topline) let &scrolloff = save_so let &scrolloffpad = save_sop bwipe! endfunc func Test_scrolloffpad_in_diff_mode() CheckFeature diff let save_so = &scrolloff let save_sop = &scrolloffpad let save_splitright = &splitright set nosplitright set scrolloff=10 set scrolloffpad=0 enew call setline(1, map(range(1, 100), {_, v -> 'line ' .. v})) diffthis vnew call setline(1, map(range(1, 100), {_, v -> 'line ' .. v})) " Make buffers minimally different to avoid diff folding everything. call setline(50, 'DIFF LINE 50') diffthis windo normal! zR windo normal! gg wincmd = let rows_without = [] let rows_with = [] let near_states = [] let eof_states = [] for sop in [0, 1] let &scrolloffpad = sop " Near EOF with real text visible in both windows. windo normal! 99G for w in range(1, winnr('$')) execute w .. 'wincmd w' let state = [line('.'), line('w0'), line('w$'), winline()] call assert_equal(99, state[0]) call assert_equal(100, state[2]) if sop == 0 call add(near_states, state) endif endfor call assert_equal(near_states[0], near_states[1]) " EOF in both windows: scrolloffpad should raise the cursor row. windo normal! G for w in range(1, winnr('$')) execute w .. 'wincmd w' let state = [line('.'), line('w0'), line('w$'), winline()] call assert_equal(line('$'), state[0]) if sop == 0 call add(eof_states, state) call add(rows_without, state[3]) else call add(rows_with, state[3]) endif endfor call assert_equal(eof_states[0], eof_states[1]) endfor call assert_true(rows_with[0] < rows_without[0]) call assert_true(rows_with[1] < rows_without[1]) windo diffoff %bwipe! let &scrolloff = save_so let &scrolloffpad = save_sop let &splitright = save_splitright endfunc func Test_scrolloffpad_diff_eof_filler_behavior() CheckFeature diff let save_so = &scrolloff let save_sop = &scrolloffpad let save_diffopt = &diffopt let save_splitright = &splitright set diffopt+=filler set scrolloff=10 set scrolloffpad=0 set nosplitright 20new call setline(1, map(range(1, 100), {_, v -> 'left ' .. v})) diffthis let short_wid = win_getid() vnew call setline(1, map(range(1, 120), {_, v -> 'right ' .. v})) diffthis let long_wid = win_getid() call assert_true(win_gotoid(short_wid)) let short_height = winheight(0) call assert_true(win_gotoid(long_wid)) let long_height = winheight(0) call assert_equal(short_height, long_height) call assert_equal(20, short_height) let ordered_diff_wids = [long_wid, short_wid] let states = {} for sop in [0, 1] execute 'set scrolloffpad=' .. sop for wid in ordered_diff_wids call assert_true(win_gotoid(wid)) normal! gg endfor for wid in ordered_diff_wids call assert_true(win_gotoid(wid)) normal! G endfor call assert_true(win_gotoid(short_wid)) let short_view = winsaveview() let short_state = [ \ line('.'), \ line('$'), \ winline(), \ short_view.topline, \ short_view.topfill, \ diff_filler(line('$') + 1), \ ] call assert_equal(short_state[1], short_state[0]) call assert_true(short_state[5] > 0) call assert_true(win_gotoid(long_wid)) let long_view = winsaveview() let long_state = [ \ line('.'), \ line('$'), \ winline(), \ long_view.topline, \ long_view.topfill, \ ] call assert_true(long_state[0] > 0 && long_state[0] <= long_state[1]) call assert_equal(short_state[0], long_state[0]) let states[sop] = [short_state, long_state] endfor let short_without = states[0][0] let short_with = states[1][0] " Environment/layout can shift direction of movement; require only that " scrolloffpad changes the short-window viewport state under EOF filler. call assert_true(short_with[2] != short_without[2] \ || short_with[3] != short_without[3] \ || short_with[4] != short_without[4]) windo diffoff call assert_true(win_gotoid(short_wid)) only! %bwipe! let &scrolloff = save_so let &scrolloffpad = save_sop let &diffopt = save_diffopt let &splitright = save_splitright endfunc func Test_scrolloffpad_with_folds() CheckScreendump CheckRunVimInTerminal CheckFeature folding let save_termwinsize = &termwinsize set termwinsize= let lines =<< trim END set scrolloff=10 set scrolloffpad=1 enew call setline(1, map(range(1, 120), {_, v -> 'line ' . v})) " Create a large fold near the end of the file. " Fold lines 60-110, leaving 111-120 visible after the fold. set foldmethod=manual set foldenable normal! gg normal! 60G normal! zf50j normal! gg END call writefile(lines, 'XScrolloffpadFolds', 'D') let buf = RunVimInTerminal('-S XScrolloffpadFolds', #{rows: 20, cols: 78}) " Case 1: Jump to end-of-file " With folds present, scrolloffpad should still " keep the cursor positioned with padding below EOF call term_sendkeys(buf, "\:\normal! G\") call term_sendkeys(buf, "\") call TermWait(buf) call VerifyScreenDump(buf, 'Test_scrolloffpad_folds_1', {}) " Case 2: Move to the folded line to ensure the fold is actually in view call term_sendkeys(buf, "\:\normal! 60G\") call term_sendkeys(buf, "\") call TermWait(buf) call VerifyScreenDump(buf, 'Test_scrolloffpad_folds_2', {}) " Case 3: Close the fold explicitly and go to EOF again " Behavior should remain stable with closed folds call term_sendkeys(buf, "\:\normal! zc\") call term_sendkeys(buf, "\:\normal! G\") call term_sendkeys(buf, "\") call TermWait(buf) call VerifyScreenDump(buf, 'Test_scrolloffpad_folds_3', {}) call StopVimInTerminal(buf) let &termwinsize = save_termwinsize endfunc " Resizing to "textoff" after 'smoothscroll' skips part of a wrapped line must " not crash. func Test_smoothscroll_textoff_showbreak() CheckOption smoothscroll CheckRunVimInTerminal let donefile = 'XTest_crash_textoff_showbreak_done' defer delete(donefile) let lines =<< trim END set noswapfile set scrolloff=0 set lines=12 columns=40 call setline(1, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa') set number wrap smoothscroll showbreak=> vsplit let textoff = getwininfo(win_getid())[0].textoff execute "normal! 0\" redraw execute 'vertical resize' textoff redraw call writefile(['done'], 'XTest_crash_textoff_showbreak_done') END call writefile(lines, 'XTest_crash_textoff_showbreak', 'D') let buf = 0 let buf = term_start([GetVimProg(), '--clean'], #{term_rows: 24, term_cols: 80}) call TermWait(buf, 200) call term_sendkeys(buf, ":source XTest_crash_textoff_showbreak\") call WaitForAssert({-> assert_true(filereadable(donefile))}) let status = term_getstatus(buf) call assert_equal('running', status) call assert_true(filereadable(donefile)) call StopVimInTerminal(buf) endfunc " vim: shiftwidth=2 sts=2 expandtab