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 #11502)
diff --git a/src/move.c b/src/move.c
index 66e8c18..4205445 100644
--- a/src/move.c
+++ b/src/move.c
@@ -2460,18 +2460,50 @@
     used = curwin->w_cline_height;
 #endif
 
-    // 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_NOFILL(lnum);
+	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_NOFILL(lnum);
+
+	    // 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 =
+#ifdef FEAT_DIFF
+			    plines_win_nofill
+#else
+			    plines_win
+#endif
+					(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/src/testdir/dumps/Test_smooth_long_1.dump b/src/testdir/dumps/Test_smooth_long_1.dump
index a6f4cfb..86c27ed 100644
--- a/src/testdir/dumps/Test_smooth_long_1.dump
+++ b/src/testdir/dumps/Test_smooth_long_1.dump
@@ -3,4 +3,4 @@
 |h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o
 |t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o
 |f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e
-| @21|3|,|1|0| @9|B|o|t| 
+| @21|3|,|1|0| @9|6@1|%| 
diff --git a/src/testdir/dumps/Test_smooth_long_10.dump b/src/testdir/dumps/Test_smooth_long_10.dump
new file mode 100644
index 0000000..a7aee24
--- /dev/null
+++ b/src/testdir/dumps/Test_smooth_long_10.dump
@@ -0,0 +1,6 @@
+>f+0&#ffffff0|o|u|r| @35
+|~+0#4040ff13&| @38
+|~| @38
+|~| @38
+|~| @38
+|:+0#0000000&|s|e|t| |s|c|r|o|l@1|o| @9|4|,|1| @10|B|o|t| 
diff --git a/src/testdir/dumps/Test_smooth_long_11.dump b/src/testdir/dumps/Test_smooth_long_11.dump
new file mode 100644
index 0000000..7f5bff7
--- /dev/null
+++ b/src/testdir/dumps/Test_smooth_long_11.dump
@@ -0,0 +1,6 @@
+|<+0#4040ff13#ffffff0@2|t+0#0000000&|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t
+|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o
+|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o
+|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |e|n|d| @11
+>f|o|u|r| @35
+@22|4|,|1| @10|B|o|t| 
diff --git a/src/testdir/dumps/Test_smooth_long_12.dump b/src/testdir/dumps/Test_smooth_long_12.dump
new file mode 100644
index 0000000..b82fca5
--- /dev/null
+++ b/src/testdir/dumps/Test_smooth_long_12.dump
@@ -0,0 +1,6 @@
+|<+0#4040ff13#ffffff0@2|t+0#0000000&|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t
+|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o
+|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o
+|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |e|n|d| @11
+|f|o|u>r| @35
+@22|4|,|4| @10|B|o|t| 
diff --git a/src/testdir/dumps/Test_smooth_long_2.dump b/src/testdir/dumps/Test_smooth_long_2.dump
index 0c2634f..6800ba4 100644
--- a/src/testdir/dumps/Test_smooth_long_2.dump
+++ b/src/testdir/dumps/Test_smooth_long_2.dump
@@ -3,4 +3,4 @@
 |t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o
 |f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e
 |x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w
-| @21|3|,|5|0| @9|B|o|t| 
+| @21|3|,|5|0| @9|6@1|%| 
diff --git a/src/testdir/dumps/Test_smooth_long_3.dump b/src/testdir/dumps/Test_smooth_long_3.dump
index c06144c..63d9794 100644
--- a/src/testdir/dumps/Test_smooth_long_3.dump
+++ b/src/testdir/dumps/Test_smooth_long_3.dump
@@ -3,4 +3,4 @@
 | |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| 
 |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t
 | |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t
-| @21|3|,|2|5|0| @8|B|o|t| 
+| @21|3|,|2|5|0| @8|6@1|%| 
diff --git a/src/testdir/dumps/Test_smooth_long_4.dump b/src/testdir/dumps/Test_smooth_long_4.dump
index 98f2c62..479dee4 100644
--- a/src/testdir/dumps/Test_smooth_long_4.dump
+++ b/src/testdir/dumps/Test_smooth_long_4.dump
@@ -3,4 +3,4 @@
 |t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o
 |f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e
 |x|t| |w|i|t|h| |l>o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w
-| @21|3|,|2|1|0| @8|B|o|t| 
+| @21|3|,|2|1|0| @8|6@1|%| 
diff --git a/src/testdir/dumps/Test_smooth_long_5.dump b/src/testdir/dumps/Test_smooth_long_5.dump
index 901baca..0b728b2 100644
--- a/src/testdir/dumps/Test_smooth_long_5.dump
+++ b/src/testdir/dumps/Test_smooth_long_5.dump
@@ -3,4 +3,4 @@
 |h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o
 |t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o
 |f| |t|e|x|t| |w|i>t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e
-| @21|3|,|1|7|0| @8|B|o|t| 
+| @21|3|,|1|7|0| @8|6@1|%| 
diff --git a/src/testdir/dumps/Test_smooth_long_6.dump b/src/testdir/dumps/Test_smooth_long_6.dump
index b7ef6bf..ba48c28 100644
--- a/src/testdir/dumps/Test_smooth_long_6.dump
+++ b/src/testdir/dumps/Test_smooth_long_6.dump
@@ -3,4 +3,4 @@
 |t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o
 |f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e
 |x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w
-| @21|3|,|9|0| @9|B|o|t| 
+| @21|3|,|9|0| @9|6@1|%| 
diff --git a/src/testdir/dumps/Test_smooth_long_7.dump b/src/testdir/dumps/Test_smooth_long_7.dump
index 27cb393..222e001 100644
--- a/src/testdir/dumps/Test_smooth_long_7.dump
+++ b/src/testdir/dumps/Test_smooth_long_7.dump
@@ -3,4 +3,4 @@
 |t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o
 |f| |t|e|x|t| |w|i>t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e
 |x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w
-| @21|3|,|1|7|0| @8|B|o|t| 
+| @21|3|,|1|7|0| @8|6@1|%| 
diff --git a/src/testdir/dumps/Test_smooth_long_8.dump b/src/testdir/dumps/Test_smooth_long_8.dump
index 8bdc889..f4687be 100644
--- a/src/testdir/dumps/Test_smooth_long_8.dump
+++ b/src/testdir/dumps/Test_smooth_long_8.dump
@@ -3,4 +3,4 @@
 |t|s| |o|f| |t|e|x>t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o
 |f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e
 |x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w
-|:|s|e|t| |s|c|r|o|l@1|o| @9|3|,|1|3|0| @8|B|o|t| 
+|:|s|e|t| |s|c|r|o|l@1|o| @9|3|,|1|3|0| @8|6@1|%| 
diff --git a/src/testdir/dumps/Test_smooth_long_9.dump b/src/testdir/dumps/Test_smooth_long_9.dump
index d3e6a08..8365ecc 100644
--- a/src/testdir/dumps/Test_smooth_long_9.dump
+++ b/src/testdir/dumps/Test_smooth_long_9.dump
@@ -3,4 +3,4 @@
 |f| |t|e|x|t| |w|i>t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e
 |x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w
 |i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| 
-@22|3|,|1|7|0| @8|B|o|t| 
+@22|3|,|1|7|0| @8|6@1|%| 
diff --git a/src/testdir/test_scroll_opt.vim b/src/testdir/test_scroll_opt.vim
index 3975a1f..d40c650 100644
--- a/src/testdir/test_scroll_opt.vim
+++ b/src/testdir/test_scroll_opt.vim
@@ -246,7 +246,7 @@
 
   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
@@ -287,6 +287,30 @@
   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
diff --git a/src/version.c b/src/version.c
index 588c312..f6640c3 100644
--- a/src/version.c
+++ b/src/version.c
@@ -696,6 +696,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    908,
+/**/
     907,
 /**/
     906,