patch 9.1.0258: half-page scrolling broke backward compatibility

Problem:  Support for 'smoothscroll' in (half-)page scrolling
          broke backward compatibility and can be made to work better.
          (after v9.1.215)
Solution: Restore the previous cursor and end-of-buffer behavior for
          half-page scrolling and improve 'smoothscroll' support.
          (Luuk van Baal)

fixes: #14338
closes: #14377

Signed-off-by: Luuk van Baal <luukvbaal@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/testdir/dumps/Test_cursorline_redraw_2.dump b/src/testdir/dumps/Test_cursorline_redraw_2.dump
index d1b4e4e..323bef8 100644
--- a/src/testdir/dumps/Test_cursorline_redraw_2.dump
+++ b/src/testdir/dumps/Test_cursorline_redraw_2.dump
@@ -1,20 +1,20 @@
-|T+0&#ffffff0|h|e| |{|o|p|t|i|o|n|}| |a|r|g|u|m|e|n|t|s| |t|o| |"|:|s|e|t|"| |m|a|y| |b|e| 
+| +0&#ffffff0@39
+|T|h|e| |{|o|p|t|i|o|n|}| |a|r|g|u|m|e|n|t|s| |t|o| |"|:|s|e|t|"| |m|a|y| |b|e| 
 |r|e|p|e|a|t|e|d|.| @1|F|o|r| |e|x|a|m|p|l|e|:| |>| @14
-@8|:|s|e|t| |a|i| |n|o|s|i| |s|w|=|3| |t|s|=|3| @9
-|I|f| |y|o|u| |m|a|k|e| |a|n| |e|r@1|o|r| |i|n| |o|n|e| |o|f| |t|h|e| |a|r|g|u|m
+| +8&&@7>:|s|e|t| |a|i| |n|o|s|i| |s|w|=|3| |t|s|=|3| @9
+|I+0&&|f| |y|o|u| |m|a|k|e| |a|n| |e|r@1|o|r| |i|n| |o|n|e| |o|f| |t|h|e| |a|r|g|u|m
 |e|n|t|s|,| |a|n| |e|r@1|o|r| |m|e|s@1|a|g|e| |w|i|l@1| |b|e| |g|i|v|e|n| @3
 |a|n|d| |t|h|e| |f|o|l@1|o|w|i|n|g| |a|r|g|u|m|e|n|t|s| |w|i|l@1| |b|e| |i|g|n|o
 |r|e|d|.| @35
 @40
 @40
 @16|*|:|s|e|t|-|v|e|r|b|o|s|e|*| @9
->W+8&&|h|e|n| |'|v|e|r|b|o|s|e|'| |i|s| |n|o|n|-|z|e|r|o|,| |d|i|s|p|l|a|y|i|n|g| |a
+|W|h|e|n| |'|v|e|r|b|o|s|e|'| |i|s| |n|o|n|-|z|e|r|o|,| |d|i|s|p|l|a|y|i|n|g| |a
 |n| |o|p|t|i|o|n| |v|a|l|u|e| |w|i|l@1| |a|l|s|o| |t|e|l@1| |w|h|e|r|e| |i|t| @1
-|w+0&&|a|s| |l|a|s|t| |s|e|t|.| @1|E|x|a|m|p|l|e|:| |>| @14
+|w|a|s| |l|a|s|t| |s|e|t|.| @1|E|x|a|m|p|l|e|:| |>| @14
 @8|:|v|e|r|b|o|s|e| |s|e|t| |s|h|i|f|t|w|i|d|t|h| |c|i|n|d|e|n|t|?
 |<| @1|s|h|i|f|t|w|i|d|t|h|=|4| |~| @22
 @10|L|a|s|t| |s|e|t| |f|r|o|m| |m|o|d|e|l|i|n|e| |l|i|n|e| |1| 
 |~| @38
 @2|c|i|n|d|e|n|t| |~| @28
-|@+0#4040ff13&@2| @36
-| +0#0000000&@21|1|2|,|1| @9|5|0|%| 
+@22|7|,|2|-|9| @8|4@1|%| 
diff --git a/src/testdir/dumps/Test_display_visual_block_scroll.dump b/src/testdir/dumps/Test_display_visual_block_scroll.dump
index 13aae59..36eae0c 100644
--- a/src/testdir/dumps/Test_display_visual_block_scroll.dump
+++ b/src/testdir/dumps/Test_display_visual_block_scroll.dump
@@ -1,7 +1,7 @@
 |{+0#0000001#a8a8a8255| | +0#0000000#ffffff0@72
->}+0&#40ffff15| +0#0000001#a8a8a8255| +0#0000000#ffffff0@72
-|{| @73
-|f| @73
-|g| @73
+|}+0#0000001#a8a8a8255| | +0#0000000#ffffff0@72
+|{+0#0000001#a8a8a8255| | +0#0000000#ffffff0@72
+|f+0#0000001#a8a8a8255| | +0#0000000#ffffff0@72
+>g| +0#0000001#a8a8a8255| +0#0000000#ffffff0@72
 |}| @73
-|-+2&&@1| |V|I|S|U|A|L| |L|I|N|E| |-@1| +0&&@29|4| @8|8|,|1| @10|B|o|t| 
+|-+2&&@1| |V|I|S|U|A|L| |L|I|N|E| |-@1| +0&&@29|7| @8|1@1|,|1| @9|B|o|t| 
diff --git a/src/testdir/test_diffmode.vim b/src/testdir/test_diffmode.vim
index 344571f..b8d0057 100644
--- a/src/testdir/test_diffmode.vim
+++ b/src/testdir/test_diffmode.vim
Binary files differ
diff --git a/src/testdir/test_edit.vim b/src/testdir/test_edit.vim
index 5e3e9cb..cdf2097 100644
--- a/src/testdir/test_edit.vim
+++ b/src/testdir/test_edit.vim
@@ -1302,7 +1302,7 @@
   call feedkeys("A\<PageUp>\<esc>", 'tnix')
   call assert_equal([0, 10, 1, 0], getpos('.'))
   call feedkeys("A\<PageUp>\<esc>", 'tnix')
-  call assert_equal([0, 2, 11, 0], getpos('.'))
+  call assert_equal([0, 10, 11, 0], getpos('.'))
   " <S-Up> is the same as <PageUp>
   " <S-Down> is the same as <PageDown>
   call cursor(1, 1)
@@ -1325,7 +1325,7 @@
   call feedkeys("A\<S-Up>\<esc>", 'tnix')
   call assert_equal([0, 10, 1, 0], getpos('.'))
   call feedkeys("A\<S-Up>\<esc>", 'tnix')
-  call assert_equal([0, 2, 11, 0], getpos('.'))
+  call assert_equal([0, 10, 11, 0], getpos('.'))
   set nostartofline
   call cursor(30, 11)
   norm! zt
@@ -1338,7 +1338,7 @@
   call feedkeys("A\<PageUp>\<esc>", 'tnix')
   call assert_equal([0, 10, 11, 0], getpos('.'))
   call feedkeys("A\<PageUp>\<esc>", 'tnix')
-  call assert_equal([0, 2, 11, 0], getpos('.'))
+  call assert_equal([0, 10, 11, 0], getpos('.'))
   call cursor(1, 1)
   call feedkeys("A\<PageDown>\<esc>", 'tnix')
   call assert_equal([0, 9, 11, 0], getpos('.'))
@@ -1363,7 +1363,7 @@
   call feedkeys("A\<S-Up>\<esc>", 'tnix')
   call assert_equal([0, 10, 11, 0], getpos('.'))
   call feedkeys("A\<S-Up>\<esc>", 'tnix')
-  call assert_equal([0, 2, 11, 0], getpos('.'))
+  call assert_equal([0, 10, 11, 0], getpos('.'))
   call cursor(1, 1)
   call feedkeys("A\<S-Down>\<esc>", 'tnix')
   call assert_equal([0, 9, 11, 0], getpos('.'))
diff --git a/src/testdir/test_normal.vim b/src/testdir/test_normal.vim
index 14440fb..1acdfce 100644
--- a/src/testdir/test_normal.vim
+++ b/src/testdir/test_normal.vim
@@ -1280,13 +1280,9 @@
   exe "normal \<C-D>"
   call assert_equal(46, line('.'))
   exe "normal \<C-U>"
-  call assert_equal(36, line('w0'))
-  call assert_equal(46, line('.'))
+  call assert_equal(36, line('.'))
   exe "normal \<C-U>"
-  call assert_equal(1,  line('w0'))
-  call assert_equal(40, line('.'))
-  exe "normal \<C-U>"
-  call assert_equal(30, line('.'))
+  call assert_equal(1, line('.'))
   exe "normal \<C-U>"
   call assert_equal(1, line('.'))
   set scroll&
@@ -1307,8 +1303,9 @@
   call assert_equal(50, line('.'))
   call assert_equal(100, line('w$'))
   normal z.
+  let lnum = winline()
   exe "normal \<C-D>"
-  call assert_equal(1, winline())
+  call assert_equal(lnum, winline())
   call assert_equal(50, line('.'))
   normal zt
   exe "normal \<C-D>"
@@ -3069,8 +3066,7 @@
   call assert_equal(2, &scroll)
   set scroll=5
   exe "norm! \<c-u>"
-  call assert_equal('3', getline('w0'))
-  call assert_equal('8', getline('.'))
+  call assert_equal('3', getline('.'))
   1
   set scrolloff=5
   exe "norm! \<c-d>"
@@ -3818,7 +3814,7 @@
   call assert_equal(1, winline())
   exe "normal \<C-B>"
   call assert_equal(10, line('.'))
-  call assert_equal(10, winline())
+  call assert_equal(4, winline())
   exe "normal \<C-B>\<C-B>"
   call assert_equal(5, line('.'))
   call assert_equal(5, winline())
diff --git a/src/testdir/test_scroll_opt.vim b/src/testdir/test_scroll_opt.vim
index d7a7907..dd2af01 100644
--- a/src/testdir/test_scroll_opt.vim
+++ b/src/testdir/test_scroll_opt.vim
@@ -552,11 +552,11 @@
   " Test "g0/g<Home>"
   exe "normal gg\<C-E>"
   norm $gkg0
-  call s:check_col_calc(1, 2, 21)
+  call s:check_col_calc(4, 1, 24)
 
   " Test moving the cursor behind the <<< display with 'virtualedit'
   set virtualedit=all
-  exe "normal \<C-E>3lgkh"
+  exe "normal \<C-E>gkh"
   call s:check_col_calc(3, 2, 23)
   set virtualedit&
 
@@ -1017,26 +1017,72 @@
   exe "norm! \<C-B>"
   call assert_equal(0, winsaveview().skipcol)
 
-  exe "norm! \<C-D>"
+  " Half-page scrolling does not go beyond end of buffer and moves the cursor.
+  exe "norm! 0\<C-D>"
   call assert_equal(200, winsaveview().skipcol)
+  call assert_equal(204, col('.'))
   exe "norm! \<C-D>"
   call assert_equal(400, winsaveview().skipcol)
+  call assert_equal(404, col('.'))
   exe "norm! \<C-D>"
-  call assert_equal(600, winsaveview().skipcol)
+  call assert_equal(520, winsaveview().skipcol)
+  call assert_equal(601, col('.'))
   exe "norm! \<C-D>"
-  call assert_equal(800, winsaveview().skipcol)
-  exe "norm! \<C-D>"
-  call assert_equal(880, winsaveview().skipcol)
+  call assert_equal(520, winsaveview().skipcol)
+  call assert_equal(801, col('.'))
   exe "norm! \<C-U>"
-  call assert_equal(680, winsaveview().skipcol)
+  call assert_equal(520, winsaveview().skipcol)
+  call assert_equal(601, col('.'))
   exe "norm! \<C-U>"
-  call assert_equal(480, winsaveview().skipcol)
+  call assert_equal(400, winsaveview().skipcol)
+  call assert_equal(404, col('.'))
   exe "norm! \<C-U>"
-  call assert_equal(280, winsaveview().skipcol)
-  exe "norm! \<C-U>"
-  call assert_equal(80, winsaveview().skipcol)
+  call assert_equal(200, winsaveview().skipcol)
+  call assert_equal(204, col('.'))
   exe "norm! \<C-U>"
   call assert_equal(0, winsaveview().skipcol)
+  call assert_equal(1, 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\<C-E>"
+  call assert_equal(880, winsaveview().skipcol)
+  exe "norm! \<C-E>"
+  redraw
+  call assert_equal(0, winsaveview().skipcol)
+
+  " Cursor in correct place when not in the first screenline of a buffer line.
+  exe "norm! gg4gj20\<C-D>\<C-D>"
+  redraw
+  call assert_equal(2, line('w0'))
+
+  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