diff options
author | Luuk van Baal <luukvbaal@gmail.com> | 2023-04-27 19:40:00 +0200 |
---|---|---|
committer | Luuk van Baal <luukvbaal@gmail.com> | 2023-05-02 13:11:47 +0200 |
commit | 46646a9bb81b72d5579beade64006d6f3dc64d19 (patch) | |
tree | 2b4448c2fad12ea7fdd56f30ceb623d1fb73d5d8 | |
parent | 26a9f0e94eb62047f0c2bb99401a8ac09840d0dd (diff) | |
download | rneovim-46646a9bb81b72d5579beade64006d6f3dc64d19.tar.gz rneovim-46646a9bb81b72d5579beade64006d6f3dc64d19.tar.bz2 rneovim-46646a9bb81b72d5579beade64006d6f3dc64d19.zip |
vim-patch:9.0.0908: with 'smoothscroll' cursor may end up in wrong position
Problem: With 'smoothscroll' cursor may end up in wrong position.
Solution: Correct the computation of screen lines. (Yee Cheng Chin,
closes vim/vim#11502)
https://github.com/vim/vim/commit/361895d2a15b4b0bbbb4c009261eab5b3d69ebf1
Co-authored-by: Yee Cheng Chin <ychin.git@gmail.com>
-rw-r--r-- | src/nvim/move.c | 37 | ||||
-rw-r--r-- | test/functional/legacy/scroll_opt_spec.lua | 41 | ||||
-rw-r--r-- | test/old/testdir/test_scroll_opt.vim | 26 |
3 files changed, 96 insertions, 8 deletions
diff --git a/src/nvim/move.c b/src/nvim/move.c index b0a4a3848a..abe908cfa0 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -1877,18 +1877,43 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) // The lines of the cursor line itself are always used. used = plines_win_nofill(curwin, cln, true); - // If the cursor is below botline, we will at least scroll by the height - // of the cursor line. Correct for empty lines, which are really part of - // botline. + // If the cursor is on or below botline, we will at least scroll by the + // height of the cursor line, which is "used". Correct for empty lines, + // which are really part of botline. if (cln >= curwin->w_botline) { scrolled = used; if (cln == curwin->w_botline) { scrolled -= curwin->w_empty_rows; } min_scrolled = scrolled; - if (cln > curwin->w_botline && curwin->w_p_sms && curwin->w_p_wrap) { - for (linenr_T lnum = curwin->w_botline + 1; lnum <= cln; lnum++) { - min_scrolled += plines_win_nofill(curwin, lnum, true); + if (curwin->w_p_sms && curwin->w_p_wrap) { + // 'smoothscroll' and 'wrap' are set + if (cln > curwin->w_botline) { + // add screen lines below w_botline + for (linenr_T lnum = curwin->w_botline + 1; lnum <= cln; lnum++) { + min_scrolled += plines_win_nofill(curwin, lnum, true); + } + } + + // Calculate how many screen lines the current top line of window + // occupies. If it is occupying more than the entire window, we + // need to scroll the additional clipped lines to scroll past the + // top line before we can move on to the other lines. + int top_plines = plines_win_nofill(curwin, curwin->w_topline, false); + int skip_lines = 0; + int width1 = curwin->w_width - curwin_col_off(); + int width2 = width1 + curwin_col_off2(); + // similar formula is used in curs_columns() + if (curwin->w_skipcol > width1) { + skip_lines += (curwin->w_skipcol - width1) / width2 + 1; + } else if (curwin->w_skipcol > 0) { + skip_lines = 1; + } + + top_plines -= skip_lines; + if (top_plines > curwin->w_height) { + scrolled += (top_plines - curwin->w_height); + min_scrolled += (top_plines - curwin->w_height); } } } diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua index a05470418f..6c8ca2cf97 100644 --- a/test/functional/legacy/scroll_opt_spec.lua +++ b/test/functional/legacy/scroll_opt_spec.lua @@ -410,7 +410,7 @@ describe('smoothscroll', function() it("adjusts the cursor position in a long line", function() screen:try_resize(40, 6) exec([[ - call setline(1, ['one', 'two', 'Line' .. (' with lots of text'->repeat(30))]) + call setline(1, ['one', 'two', 'Line' .. (' with lots of text'->repeat(30)) .. ' end', 'four']) set smoothscroll scrolloff=0 normal 3G10|zt ]]) @@ -502,6 +502,45 @@ describe('smoothscroll', function() ith lots of text with lots of text with | | ]]) + -- '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. + exec('set scrolloff=0') + feed('0j') + screen:expect([[ + ^four | + ~ | + ~ | + ~ | + ~ | + | + ]]) + -- 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. + exec("call setline(1, ['one', 'two', 'Line' .. (' with lots of text'->repeat(10)) .. ' end', 'four'])") + feed('3Gztj') + screen:expect([[ + <<<th lots of text with lots of text wit| + h lots of text with lots of text with lo| + ts of text with lots of text with lots o| + f text with lots of text end | + ^four | + | + ]]) + -- 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. + feed('3Gzt<C-E>j') + screen:expect([[ + <<<th lots of text with lots of text wit| + h lots of text with lots of text with lo| + ts of text with lots of text with lots o| + f text with lots of text end | + fou^r | + | + ]]) end) -- oldtest: Test_smoothscroll_one_long_line() diff --git a/test/old/testdir/test_scroll_opt.vim b/test/old/testdir/test_scroll_opt.vim index b9645898e8..8d1942e037 100644 --- a/test/old/testdir/test_scroll_opt.vim +++ b/test/old/testdir/test_scroll_opt.vim @@ -263,7 +263,7 @@ func Test_smoothscroll_wrap_long_line() let lines =<< trim END vim9script - setline(1, ['one', 'two', 'Line' .. (' with lots of text'->repeat(30))]) + setline(1, ['one', 'two', 'Line' .. (' with lots of text'->repeat(30)) .. ' end', 'four']) set smoothscroll scrolloff=0 normal 3G10|zt END @@ -304,6 +304,30 @@ func Test_smoothscroll_wrap_long_line() call term_sendkeys(buf, "gj") call term_sendkeys(buf, "\<C-Y>") 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\<CR>") + call term_sendkeys(buf, "0j") + call VerifyScreenDump(buf, 'Test_smooth_long_10', {}) + + " 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'])\<CR>") + call term_sendkeys(buf, "3Gzt") + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_smooth_long_11', {}) + + " 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, "\<C-E>j") + call VerifyScreenDump(buf, 'Test_smooth_long_12', {}) call StopVimInTerminal(buf) endfunc |