patch 9.1.0215: Half-page scrolling does not support smooth-scrolling

Problem:  Page-wise scrolling with Ctrl-D/Ctrl-U implements
          it's own logic to change the topline and cursor.
          More logic than necessary for scrolling with Ctrl-F/Ctrl-B
          was removed in patch 9.1.0211.
Solution: Re-use the logic from Ctrl-E/Ctrl-Y/Ctrl-F/Ctrl-B while
          staying backward compatible as much as possible.
          Restore some of the logic that determined how many lines will
          be scrolled (Luuk van Baal)

closes: #14316

Signed-off-by: Luuk van Baal <luukvbaal@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/move.c b/src/move.c
index 772b173..25f419e 100644
--- a/src/move.c
+++ b/src/move.c
@@ -3026,13 +3026,74 @@
 }
 
 /*
+ * Decide how much overlap to use for page-up or page-down scrolling.
+ * This is symmetric, so that doing both keeps the same lines displayed.
+ * Three lines are examined:
+ *
+ *  before CTRL-F          after CTRL-F / before CTRL-B
+ *     etc.                    l1
+ *  l1 last but one line       ------------
+ *  l2 last text line          l2 top text line
+ *  -------------              l3 second text line
+ *  l3                            etc.
+ */
+static int get_scroll_overlap(int dir)
+{
+    lineoff_T loff;
+    int	    min_height = curwin->w_height - 2;
+
+    validate_botline();
+    if (dir == FORWARD && curwin->w_botline > curbuf->b_ml.ml_line_count)
+	return min_height + 2;  // no overlap, still handle 'smoothscroll'
+
+    loff.lnum = dir == FORWARD ? curwin->w_botline : curwin->w_topline - 1;
+#ifdef FEAT_DIFF
+    loff.fill = diff_check_fill(curwin, loff.lnum + dir == BACKWARD)
+		- (dir == FORWARD ? curwin->w_filler_rows : curwin->w_topfill);
+    loff.height = loff.fill > 0 ? 1 : plines_nofill(loff.lnum);
+#else
+    loff.height = plines(loff.lnum);
+#endif
+
+    int h1 = loff.height;
+    if (h1 > min_height)
+	return min_height + 2;  // no overlap
+    if (dir == FORWARD)
+	topline_back(&loff);
+    else
+	botline_forw(&loff);
+
+    int h2 = loff.height;
+    if (h2 == MAXCOL || h2 + h1 > min_height)
+	return min_height + 2;  // no overlap
+    if (dir == FORWARD)
+	topline_back(&loff);
+    else
+	botline_forw(&loff);
+
+    int h3 = loff.height;
+    if (h3 == MAXCOL || h3 + h2 > min_height)
+	return min_height + 2;  // no overlap
+    if (dir == FORWARD)
+	topline_back(&loff);
+    else
+	botline_forw(&loff);
+
+    int h4 = loff.height;
+    if (h4 == MAXCOL || h4 + h3 + h2 > min_height || h3 + h2 + h1 > min_height)
+	return min_height + 1;  // 1 line overlap
+    else
+	return min_height;      // 2 lines overlap
+}
+
+/*
  * Move screen "count" pages up ("dir" is BACKWARD) or down ("dir" is FORWARD)
  * and update the screen.
  *
  * Return FAIL for failure, OK otherwise.
  */
     int
-onepage(int dir, long count)
+pagescroll(int dir, long count, int half)
 {
 #ifdef FEAT_DIFF
     int		prev_topfill = curwin->w_topfill;
@@ -3040,9 +3101,17 @@
     linenr_T	prev_topline = curwin->w_topline;
     colnr_T	prev_skipcol = curwin->w_skipcol;
 
-    // Scroll 'window' or current window height lines.
-    count *= ((ONE_WINDOW && p_window > 0 && p_window < Rows - 1) ?
-					    p_window : curwin->w_height) - 2;
+    if (half)
+    {
+	// Scroll [count], 'scroll' or current window height lines.
+	if (count)
+	    curwin->w_p_scr = MIN(curwin->w_height, count);
+	count = MIN(curwin->w_height, curwin->w_p_scr);
+    }
+    else
+	// Scroll 'window' or current window height lines.
+	count *= ((ONE_WINDOW && p_window > 0 && p_window < Rows - 1) ?
+					    p_window - 2 : get_scroll_overlap(dir));
 
     if (curwin->w_p_sms)
 	scroll_redraw(dir == FORWARD, count);
@@ -3063,6 +3132,10 @@
 	    scroll_redraw(dir == FORWARD, count);
 	curwin->w_p_sms = FALSE;
     }
+#ifdef FEAT_FOLDING
+	// Move cursor to first line of closed fold.
+	foldAdjustCursor();
+#endif
 
     int nochange = curwin->w_topline == prev_topline
 #ifdef FEAT_DIFF
@@ -3070,198 +3143,22 @@
 #endif
 	&& curwin->w_skipcol == prev_skipcol;
 
+    // Error if the viewport did not change and the cursor is already
+    // at the boundary.
     if (nochange)
-	beep_flush();
+    {
+	int prev_cursor = curwin->w_cursor.lnum;
+	curwin->w_cursor.lnum += (count + 1) * (dir == FORWARD ? 1 : -1);
+	check_cursor();
+	if (curwin->w_cursor.lnum == prev_cursor)
+	    beep_flush();
+    }
     else if (!curwin->w_p_sms || curwin->w_skipcol == prev_skipcol)
 	beginline(BL_SOL | BL_FIX);
 
     return nochange;
 }
 
-/*
- * Scroll 'scroll' lines up or down.
- */
-    void
-halfpage(int flag, linenr_T Prenum)
-{
-    long	scrolled = 0;
-    int		i;
-    int		n;
-    int		room;
-
-    if (Prenum)
-	curwin->w_p_scr = (Prenum > curwin->w_height) ?
-						curwin->w_height : Prenum;
-    n = (curwin->w_p_scr <= curwin->w_height) ?
-				    curwin->w_p_scr : curwin->w_height;
-
-    update_topline();
-    validate_botline();
-    room = curwin->w_empty_rows;
-#ifdef FEAT_DIFF
-    room += curwin->w_filler_rows;
-#endif
-    if (flag)
-    {
-	/*
-	 * scroll the text up
-	 */
-	while (n > 0 && curwin->w_botline <= curbuf->b_ml.ml_line_count)
-	{
-#ifdef FEAT_DIFF
-	    if (curwin->w_topfill > 0)
-	    {
-		i = 1;
-		--n;
-		--curwin->w_topfill;
-	    }
-	    else
-#endif
-	    {
-		i = PLINES_NOFILL(curwin->w_topline);
-		n -= i;
-		if (n < 0 && scrolled > 0)
-		    break;
-#ifdef FEAT_FOLDING
-		(void)hasFolding(curwin->w_topline, NULL, &curwin->w_topline);
-#endif
-		++curwin->w_topline;
-#ifdef FEAT_DIFF
-		curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline);
-#endif
-
-		if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
-		{
-		    ++curwin->w_cursor.lnum;
-		    curwin->w_valid &=
-				    ~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL);
-		}
-	    }
-	    curwin->w_valid &= ~(VALID_CROW|VALID_WROW);
-	    scrolled += i;
-
-	    /*
-	     * Correct w_botline for changed w_topline.
-	     * Won't work when there are filler lines.
-	     */
-#ifdef FEAT_DIFF
-	    if (curwin->w_p_diff)
-		curwin->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP);
-	    else
-#endif
-	    {
-		room += i;
-		do
-		{
-		    i = plines(curwin->w_botline);
-		    if (i > room)
-			break;
-#ifdef FEAT_FOLDING
-		    (void)hasFolding(curwin->w_botline, NULL,
-							  &curwin->w_botline);
-#endif
-		    ++curwin->w_botline;
-		    room -= i;
-		} while (curwin->w_botline <= curbuf->b_ml.ml_line_count);
-	    }
-	}
-
-	/*
-	 * When hit bottom of the file: move cursor down.
-	 */
-	if (n > 0)
-	{
-# ifdef FEAT_FOLDING
-	    if (hasAnyFolding(curwin))
-	    {
-		while (--n >= 0
-			&& curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
-		{
-		    (void)hasFolding(curwin->w_cursor.lnum, NULL,
-						      &curwin->w_cursor.lnum);
-		    ++curwin->w_cursor.lnum;
-		}
-	    }
-	    else
-# endif
-		curwin->w_cursor.lnum += n;
-	    check_cursor_lnum();
-	}
-    }
-    else
-    {
-	/*
-	 * scroll the text down
-	 */
-	while (n > 0 && curwin->w_topline > 1)
-	{
-#ifdef FEAT_DIFF
-	    if (curwin->w_topfill < diff_check_fill(curwin, curwin->w_topline))
-	    {
-		i = 1;
-		--n;
-		++curwin->w_topfill;
-	    }
-	    else
-#endif
-	    {
-		i = PLINES_NOFILL(curwin->w_topline - 1);
-		n -= i;
-		if (n < 0 && scrolled > 0)
-		    break;
-		--curwin->w_topline;
-#ifdef FEAT_FOLDING
-		(void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
-#endif
-#ifdef FEAT_DIFF
-		curwin->w_topfill = 0;
-#endif
-	    }
-	    curwin->w_valid &= ~(VALID_CROW|VALID_WROW|
-					      VALID_BOTLINE|VALID_BOTLINE_AP);
-	    scrolled += i;
-	    if (curwin->w_cursor.lnum > 1)
-	    {
-		--curwin->w_cursor.lnum;
-		curwin->w_valid &= ~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL);
-	    }
-	}
-
-	/*
-	 * When hit top of the file: move cursor up.
-	 */
-	if (n > 0)
-	{
-	    if (curwin->w_cursor.lnum <= (linenr_T)n)
-		curwin->w_cursor.lnum = 1;
-	    else
-# ifdef FEAT_FOLDING
-	    if (hasAnyFolding(curwin))
-	    {
-		while (--n >= 0 && curwin->w_cursor.lnum > 1)
-		{
-		    --curwin->w_cursor.lnum;
-		    (void)hasFolding(curwin->w_cursor.lnum,
-						&curwin->w_cursor.lnum, NULL);
-		}
-	    }
-	    else
-# endif
-		curwin->w_cursor.lnum -= n;
-	}
-    }
-# ifdef FEAT_FOLDING
-    // Move cursor to first line of closed fold.
-    foldAdjustCursor();
-# endif
-#ifdef FEAT_DIFF
-    check_topfill(curwin, !flag);
-#endif
-    cursor_correct();
-    beginline(BL_SOL | BL_FIX);
-    redraw_later(UPD_VALID);
-}
-
     void
 do_check_cursorbind(void)
 {