summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuuk van Baal <luukvbaal@gmail.com>2024-04-08 22:27:41 +0200
committerChristian Brabandt <cb@256bit.org>2024-04-08 22:27:41 +0200
commit9148ba8a46baa3934c44164989cdcdec5d01d9e3 (patch)
tree0ee19963c68177ffbf961a0c7970b6d7d1910e7e
parent196b6678c5483217ea5bc7d047b02c915615dae6 (diff)
patch 9.1.0280: several issues with 'smoothscroll' supportv9.1.0280
Problem: Logic to make sure cursor is in visible part of the screen after scrolling the text with 'smoothscroll' is scattered, asymmetric and contains bugs. Solution: Adjust and create helper function for 'smoothscroll' cursor logic. (Luuk van Baal) closes: #14410 Signed-off-by: Luuk van Baal <luukvbaal@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
-rw-r--r--src/misc1.c2
-rw-r--r--src/move.c286
-rw-r--r--src/normal.c59
-rw-r--r--src/proto/move.pro1
-rw-r--r--src/proto/normal.pro1
-rw-r--r--src/testdir/dumps/Test_smooth_long_6.dump2
-rw-r--r--src/testdir/dumps/Test_smooth_long_7.dump2
-rw-r--r--src/testdir/test_diffmode.vim15
-rw-r--r--src/testdir/test_scroll_opt.vim38
-rw-r--r--src/version.c2
10 files changed, 217 insertions, 191 deletions
diff --git a/src/misc1.c b/src/misc1.c
index 666cd5847d..7ee471233b 100644
--- a/src/misc1.c
+++ b/src/misc1.c
@@ -519,6 +519,8 @@ plines_m_win(win_T *wp, linenr_T first, linenr_T last, int limit_winheight)
#endif
{
#ifdef FEAT_DIFF
+ if (first == wp->w_buffer->b_ml.ml_line_count)
+ count += diff_check_fill(wp, first + 1);
if (first == wp->w_topline)
count += plines_win_nofill(wp, first, limit_winheight)
+ wp->w_topfill;
diff --git a/src/move.c b/src/move.c
index f9e5cf72a6..0bf59c9f90 100644
--- a/src/move.c
+++ b/src/move.c
@@ -59,7 +59,7 @@ adjust_plines_for_skipcol(win_T *wp)
* the window height.
*/
static int
-plines_correct_topline(win_T *wp, linenr_T lnum)
+plines_correct_topline(win_T *wp, linenr_T lnum, int limit_winheight)
{
int n;
#ifdef FEAT_DIFF
@@ -70,7 +70,7 @@ plines_correct_topline(win_T *wp, linenr_T lnum)
n = plines_win(wp, lnum, FALSE);
if (lnum == wp->w_topline)
n -= adjust_plines_for_skipcol(wp);
- if (n > wp->w_height)
+ if (limit_winheight && n > wp->w_height)
n = wp->w_height;
return n;
}
@@ -119,7 +119,7 @@ comp_botline(win_T *wp)
else
#endif
{
- n = plines_correct_topline(wp, lnum);
+ n = plines_correct_topline(wp, lnum, TRUE);
}
if (
#ifdef FEAT_FOLDING
@@ -921,7 +921,7 @@ curs_rows(win_T *wp)
else
#endif
{
- wp->w_cline_row += plines_correct_topline(wp, lnum);
+ wp->w_cline_row += plines_correct_topline(wp, lnum, TRUE);
++lnum;
}
}
@@ -1602,6 +1602,127 @@ f_virtcol2col(typval_T *argvars UNUSED, typval_T *rettv)
#endif
/*
+ * Make sure the cursor is in the visible part of the topline after scrolling
+ * the screen with 'smoothscroll'.
+ */
+static void cursor_correct_sms(void)
+{
+ if (!curwin->w_p_sms ||!curwin->w_p_wrap
+ || curwin->w_cursor.lnum != curwin->w_topline)
+ return;
+
+ long so = get_scrolloff_value();
+ int width1 = curwin->w_width - curwin_col_off();
+ int width2 = width1 + curwin_col_off2();
+ int so_cols = so == 0 ? 0 : width1 + (so - 1) * width2;
+ int space_cols = (curwin->w_height - 1) * width2;
+ int size = so == 0 ? 0 : win_linetabsize(curwin, curwin->w_topline,
+ ml_get(curwin->w_topline), (colnr_T)MAXCOL);
+
+ if (curwin->w_topline == 1 && curwin->w_skipcol == 0)
+ so_cols = 0; // Ignore 'scrolloff' at top of buffer.
+ else if (so_cols > space_cols / 2)
+ so_cols = space_cols / 2; // Not enough room: put cursor in the middle.
+
+ // Not enough screen lines in topline: ignore 'scrolloff'.
+ while (so_cols > size && so_cols - width2 >= width1)
+ so_cols -= width2;
+ if (so_cols >= width1 && so_cols > size)
+ so_cols -= width1;
+
+ // If there is no marker or we have non-zero scrolloff, just ignore it.
+ int overlap = (curwin->w_skipcol == 0 || so_cols != 0) ? 0
+ : sms_marker_overlap(curwin, -1);
+ int top = curwin->w_skipcol + overlap + so_cols;
+ int bot = curwin->w_skipcol + width1 + (curwin->w_height - 1) * width2
+ - so_cols;
+ validate_virtcol();
+ colnr_T col = curwin->w_virtcol;
+
+ if (col < top)
+ {
+ if (col < width1)
+ col += width1;
+ while (width2 > 0 && col < top)
+ col += width2;
+ }
+ else
+ while (width2 > 0 && col >= bot)
+ col -= width2;
+
+ if (col != curwin->w_virtcol)
+ {
+ curwin->w_curswant = col;
+ coladvance(curwin->w_curswant);
+ // validate_virtcol() marked various things as valid, but after
+ // moving the cursor they need to be recomputed
+ curwin->w_valid &=
+ ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL);
+ }
+}
+
+/*
+ * Scroll "count" lines up or down, and redraw.
+ */
+ void
+scroll_redraw(int up, long count)
+{
+ linenr_T prev_topline = curwin->w_topline;
+ int prev_skipcol = curwin->w_skipcol;
+#ifdef FEAT_DIFF
+ int prev_topfill = curwin->w_topfill;
+#endif
+ linenr_T prev_lnum = curwin->w_cursor.lnum;
+
+ if (up)
+ scrollup(count, TRUE);
+ else
+ scrolldown(count, TRUE);
+ if (get_scrolloff_value() > 0)
+ {
+ // Adjust the cursor position for 'scrolloff'. Mark w_topline as
+ // valid, otherwise the screen jumps back at the end of the file.
+ cursor_correct();
+ check_cursor_moved(curwin);
+ curwin->w_valid |= VALID_TOPLINE;
+
+ // If moved back to where we were, at least move the cursor, otherwise
+ // we get stuck at one position. Don't move the cursor up if the
+ // first line of the buffer is already on the screen
+ while (curwin->w_topline == prev_topline
+ && curwin->w_skipcol == prev_skipcol
+#ifdef FEAT_DIFF
+ && curwin->w_topfill == prev_topfill
+#endif
+ )
+ {
+ if (up)
+ {
+ if (curwin->w_cursor.lnum > prev_lnum
+ || cursor_down(1L, FALSE) == FAIL)
+ break;
+ }
+ else
+ {
+ if (curwin->w_cursor.lnum < prev_lnum
+ || prev_topline == 1L
+ || cursor_up(1L, FALSE) == FAIL)
+ break;
+ }
+ // Mark w_topline as valid, otherwise the screen jumps back at the
+ // end of the file.
+ check_cursor_moved(curwin);
+ curwin->w_valid |= VALID_TOPLINE;
+ }
+ }
+
+ cursor_correct_sms();
+ if (curwin->w_cursor.lnum != prev_lnum)
+ coladvance(curwin->w_curswant);
+ redraw_later(UPD_VALID);
+}
+
+/*
* Scroll the current window down by "line_count" logical lines. "CTRL-Y"
*/
void
@@ -1663,9 +1784,6 @@ scrolldown(
#ifdef FEAT_DIFF
curwin->w_topfill = 0;
#endif
- // Adjusting the cursor later should not adjust skipcol.
- if (do_sms)
- curwin->w_curswant = MAXCOL;
#ifdef FEAT_FOLDING
// A sequence of folded lines only counts for one logical line
if (hasFolding(curwin->w_topline, &first, NULL))
@@ -1749,35 +1867,8 @@ scrolldown(
#endif
coladvance(curwin->w_curswant);
}
-
- if (curwin->w_cursor.lnum == curwin->w_topline && do_sms)
- {
- long so = get_scrolloff_value();
- int scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2;
-
- // make sure the cursor is in the visible text
- validate_virtcol();
- int col = curwin->w_virtcol - curwin->w_skipcol + scrolloff_cols;
- int row = 0;
- if (col >= width1)
- {
- col -= width1;
- ++row;
- }
- if (col > width2 && width2 > 0)
- {
- row += col / width2;
- // even so col is not used anymore,
- // make sure it is correct, just in case
- col = col % width2;
- }
- if (row >= curwin->w_height)
- {
- curwin->w_curswant = curwin->w_virtcol
- - (row - curwin->w_height + 1) * width2;
- coladvance(curwin->w_curswant);
- }
- }
+ if (curwin->w_cursor.lnum < curwin->w_topline)
+ curwin->w_cursor.lnum = curwin->w_topline;
}
/*
@@ -1859,9 +1950,6 @@ scrollup(
curwin->w_topfill = diff_check_fill(curwin, lnum);
# endif
curwin->w_skipcol = 0;
- // Adjusting the cursor later should not adjust skipcol.
- if (do_sms)
- curwin->w_curswant = 0;
if (todo > 1 && do_sms)
size = linetabsize(curwin, curwin->w_topline);
}
@@ -1902,47 +1990,6 @@ scrollup(
~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL);
coladvance(curwin->w_curswant);
}
- if (curwin->w_cursor.lnum == curwin->w_topline
- && do_sms && curwin->w_skipcol > 0)
- {
- int col_off = curwin_col_off();
- int col_off2 = curwin_col_off2();
-
- int width1 = curwin->w_width - col_off;
- int width2 = width1 + col_off2;
- int extra2 = col_off - col_off2;
- long so = get_scrolloff_value();
- int scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2;
- int space_cols = (curwin->w_height - 1) * width2;
-
- // If we have non-zero scrolloff, just ignore the marker as we are
- // going past it anyway.
- int overlap = scrolloff_cols != 0 ? 0
- : sms_marker_overlap(curwin, extra2);
-
- // Make sure the cursor is in a visible part of the line, taking
- // 'scrolloff' into account, but using screen lines.
- // If there are not enough screen lines put the cursor in the middle.
- if (scrolloff_cols > space_cols / 2)
- scrolloff_cols = space_cols / 2;
- validate_virtcol();
- if (curwin->w_virtcol < curwin->w_skipcol + overlap + scrolloff_cols)
- {
- colnr_T col = curwin->w_virtcol;
-
- if (col < width1)
- col += width1;
- while (col < curwin->w_skipcol + overlap + scrolloff_cols)
- col += width2;
- curwin->w_curswant = col;
- coladvance(curwin->w_curswant);
-
- // validate_virtcol() marked various things as valid, but after
- // moving the cursor they need to be recomputed
- curwin->w_valid &=
- ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL);
- }
- }
}
/*
@@ -2489,7 +2536,6 @@ scroll_cursor_bot(int min_scroll, int set_topbot)
{
int plines_offset = used + loff.height
- curwin->w_height;
- int overlap = sms_marker_overlap(curwin, -1);
used = curwin->w_height;
#ifdef FEAT_DIFF
curwin->w_topfill = loff.fill;
@@ -2497,7 +2543,6 @@ scroll_cursor_bot(int min_scroll, int set_topbot)
curwin->w_topline = loff.lnum;
curwin->w_skipcol = skipcol_from_plines(
curwin, plines_offset);
- curwin->w_cursor.col = curwin->w_skipcol + overlap;
}
}
break;
@@ -2722,6 +2767,8 @@ scroll_cursor_bot(int min_scroll, int set_topbot)
curwin->w_valid = old_valid;
}
curwin->w_valid |= VALID_TOPLINE;
+
+ cursor_correct_sms();
}
/*
@@ -3094,10 +3141,10 @@ static int get_scroll_overlap(int dir)
}
/*
- * Scroll "count" lines with 'smoothscroll' in direction "dir". Adjust "count"
- * when scrolling more than "count" and return TRUE when scrolling happened.
+ * Scroll "count" lines with 'smoothscroll' in direction "dir". Return TRUE
+ * when scrolling happened.
*/
-static int scroll_with_sms(int dir, long *count)
+static int scroll_with_sms(int dir, long count)
{
int prev_sms = curwin->w_p_sms;
colnr_T prev_skipcol = curwin->w_skipcol;
@@ -3107,11 +3154,10 @@ static int scroll_with_sms(int dir, long *count)
#endif
curwin->w_p_sms = TRUE;
- scroll_redraw(dir == FORWARD, *count);
+ scroll_redraw(dir == FORWARD, count);
// Not actually smoothscrolling but ended up with partially visible line.
- // Continue scrolling and update "count" so that cursor can be moved
- // accordingly for half-page scrolling.
+ // Continue scrolling until skipcol is zero.
if (!prev_sms && curwin->w_skipcol > 0)
{
int fixdir = dir;
@@ -3122,10 +3168,7 @@ static int scroll_with_sms(int dir, long *count)
validate_cursor();
while (curwin->w_skipcol > 0
&& curwin->w_topline < curbuf->b_ml.ml_line_count)
- {
scroll_redraw(fixdir == FORWARD, 1);
- *count += (fixdir == dir ? 1 : -1);
- }
}
curwin->w_p_sms = prev_sms;
@@ -3149,7 +3192,6 @@ pagescroll(int dir, long count, int half)
int nochange = TRUE;
int buflen = curbuf->b_ml.ml_line_count;
colnr_T prev_col = curwin->w_cursor.col;
- colnr_T prev_curswant = curwin->w_curswant;
linenr_T prev_lnum = curwin->w_cursor.lnum;
oparg_T oa = { 0 };
cmdarg_T ca = { 0 };
@@ -3162,34 +3204,45 @@ pagescroll(int dir, long count, int half)
curwin->w_p_scr = MIN(curwin->w_height, count);
count = MIN(curwin->w_height, curwin->w_p_scr);
- // Don't scroll if we already know that it will reveal end of buffer lines.
- if (dir == BACKWARD
- || (curwin->w_botline - 1 < buflen)
- || (curwin->w_p_sms && curwin->w_botline - 1 == buflen
- && curwin->w_skipcol < linetabsize(curwin, buflen)))
+ int curscount = count;
+ // Adjust count so as to not reveal end of buffer lines.
+ if (dir == FORWARD)
{
- nochange = scroll_with_sms(dir, &count);
- validate_botline();
- // Hide any potentially revealed end of buffer lines.
- if (!nochange && curwin->w_botline - 1 == buflen)
+ int n = plines_correct_topline(curwin, curwin->w_topline, FALSE);
+ if (n - count < curwin->w_height && curwin->w_topline < buflen)
+ n += plines_m_win(curwin, curwin->w_topline + 1, buflen, TRUE);
+ if (n - count < curwin->w_height)
+ count = n - curwin->w_height;
+ }
+
+ // Scroll the window and determine number of lines to move the cursor.
+ if (count > 0)
+ {
+ validate_cursor();
+ int prev_wrow = curwin->w_wrow;
+ nochange = scroll_with_sms(dir, count);
+ if (!nochange)
{
- curwin->w_cursor.lnum = buflen;
- scroll_cursor_bot(0, TRUE);
+ validate_cursor();
+ curscount = abs(prev_wrow - curwin->w_wrow);
+ dir = prev_wrow > curwin->w_wrow ? FORWARD : BACKWARD;
}
}
- // Move the cursor "count" screen lines.
- curwin->w_curswant = MAXCOL;
- curwin->w_cursor.col = prev_col;
- curwin->w_cursor.lnum = prev_lnum;
- if (curwin->w_p_wrap)
- nv_screengo(&oa, dir, count);
- else if (dir == FORWARD)
- cursor_down_inner(curwin, count);
- else
- cursor_up_inner(curwin, count);
+ int so = get_scrolloff_value();
+ // Move the cursor the same amount of screen lines except if
+ // 'scrolloff' is set and cursor was at start or end of buffer.
+ if (so == 0 || (prev_lnum != 1 && prev_lnum != buflen))
+ {
+ if (curwin->w_p_wrap)
+ nv_screengo(&oa, dir, curscount);
+ else if (dir == FORWARD)
+ cursor_down_inner(curwin, curscount);
+ else
+ cursor_up_inner(curwin, curscount);
+ }
- if (get_scrolloff_value())
+ if (so > 0)
cursor_correct();
#ifdef FEAT_FOLDING
// Move cursor to first line of closed fold.
@@ -3205,16 +3258,15 @@ pagescroll(int dir, long count, int half)
// Scroll [count] times 'window' or current window height lines.
count *= ((ONE_WINDOW && p_window > 0 && p_window < Rows - 1) ?
MAX(1, p_window - 2) : get_scroll_overlap(dir));
- nochange = scroll_with_sms(dir, &count);
+ nochange = scroll_with_sms(dir, count);
}
- curwin->w_curswant = prev_curswant;
// Error if both the viewport and cursor did not change.
if (nochange)
beep_flush();
else if (!curwin->w_p_sms)
beginline(BL_SOL | BL_FIX);
- else if (p_sol || curwin->w_skipcol)
+ else if (p_sol)
nv_g_home_m_cmd(&ca);
return nochange;
diff --git a/src/normal.c b/src/normal.c
index 955cd365a2..cfe60eec32 100644
--- a/src/normal.c
+++ b/src/normal.c
@@ -2474,65 +2474,6 @@ nv_scroll_line(cmdarg_T *cap)
}
/*
- * Scroll "count" lines up or down, and redraw.
- */
- void
-scroll_redraw(int up, long count)
-{
- linenr_T prev_topline = curwin->w_topline;
- int prev_skipcol = curwin->w_skipcol;
-#ifdef FEAT_DIFF
- int prev_topfill = curwin->w_topfill;
-#endif
- linenr_T prev_lnum = curwin->w_cursor.lnum;
-
- if (up)
- scrollup(count, TRUE);
- else
- scrolldown(count, TRUE);
- if (get_scrolloff_value() > 0)
- {
- // Adjust the cursor position for 'scrolloff'. Mark w_topline as
- // valid, otherwise the screen jumps back at the end of the file.
- cursor_correct();
- check_cursor_moved(curwin);
- curwin->w_valid |= VALID_TOPLINE;
-
- // If moved back to where we were, at least move the cursor, otherwise
- // we get stuck at one position. Don't move the cursor up if the
- // first line of the buffer is already on the screen
- while (curwin->w_topline == prev_topline
- && curwin->w_skipcol == prev_skipcol
-#ifdef FEAT_DIFF
- && curwin->w_topfill == prev_topfill
-#endif
- )
- {
- if (up)
- {
- if (curwin->w_cursor.lnum > prev_lnum
- || cursor_down(1L, FALSE) == FAIL)
- break;
- }
- else
- {
- if (curwin->w_cursor.lnum < prev_lnum
- || prev_topline == 1L
- || cursor_up(1L, FALSE) == FAIL)
- break;
- }
- // Mark w_topline as valid, otherwise the screen jumps back at the
- // end of the file.
- check_cursor_moved(curwin);
- curwin->w_valid |= VALID_TOPLINE;
- }
- }
- if (curwin->w_cursor.lnum != prev_lnum)
- coladvance(curwin->w_curswant);
- redraw_later(UPD_VALID);
-}
-
-/*
* Get the count specified after a 'z' command. Only the 'z<CR>', 'zl', 'zh',
* 'z<Left>', and 'z<Right>' commands accept a count after 'z'.
* Returns TRUE to process the 'z' command and FALSE to skip it.
diff --git a/src/proto/move.pro b/src/proto/move.pro
index 91c8791802..ec88b9b397 100644
--- a/src/proto/move.pro
+++ b/src/proto/move.pro
@@ -37,6 +37,7 @@ void f_screenpos(typval_T *argvars, typval_T *rettv);
void f_virtcol2col(typval_T *argvars, typval_T *rettv);
void scrolldown(long line_count, int byfold);
void scrollup(long line_count, int byfold);
+void scroll_redraw(int up, long count);
void adjust_skipcol(void);
void check_topfill(win_T *wp, int down);
void scrolldown_clamp(void);
diff --git a/src/proto/normal.pro b/src/proto/normal.pro
index afa0799309..6dcbe414fc 100644
--- a/src/proto/normal.pro
+++ b/src/proto/normal.pro
@@ -25,7 +25,6 @@ int find_decl(char_u *ptr, int len, int locally, int thisblock, int flags_arg);
void nv_g_home_m_cmd(cmdarg_T *cap);
int nv_screengo(oparg_T *oap, int dir, long dist);
void nv_scroll_line(cmdarg_T *cap);
-void scroll_redraw(int up, long count);
void handle_tabmenu(void);
void do_nv_ident(int c1, int c2);
int get_visual_text(cmdarg_T *cap, char_u **pp, int *lenp);
diff --git a/src/testdir/dumps/Test_smooth_long_6.dump b/src/testdir/dumps/Test_smooth_long_6.dump
index ba48c2825f..507aa460da 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|6@1|%|
+|:|s|e|t| |s|c|r|o|l@1|o| @9|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 222e0019f5..225207ff6c 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|6@1|%|
+|:|s|e|t| |s|c|r|o|l@1|o| @9|3|,|1|7|0| @8|6@1|%|
diff --git a/src/testdir/test_diffmode.vim b/src/testdir/test_diffmode.vim
index b8d0057a13..9e973de213 100644
--- a/src/testdir/test_diffmode.vim
+++ b/src/testdir/test_diffmode.vim
@@ -2023,4 +2023,19 @@ func Test_diff_toggle_wrap_skipcol_leftcol()
bwipe!
endfunc
+" Ctrl-D reveals filler lines below the last line in the buffer.
+func Test_diff_eob_halfpage()
+ 5new
+ call setline(1, ['']->repeat(10) + ['a'])
+ diffthis
+ 5new
+ call setline(1, ['']->repeat(3) + ['a', 'b'])
+ diffthis
+ wincmd j
+ exe "norm! G\<C-D>"
+ call assert_equal(6, line('w0'))
+
+ %bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_scroll_opt.vim b/src/testdir/test_scroll_opt.vim
index 294da0d877..e09c240a09 100644
--- a/src/testdir/test_scroll_opt.vim
+++ b/src/testdir/test_scroll_opt.vim
@@ -1020,30 +1020,36 @@ func Test_smoothscroll_page()
" 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! 0\<C-D>"
+ exe "norm! 15|\<C-D>"
call assert_equal(200, winsaveview().skipcol)
- call assert_equal(204, col('.'))
+ call assert_equal(215, col('.'))
exe "norm! \<C-D>"
call assert_equal(400, winsaveview().skipcol)
- call assert_equal(404, col('.'))
+ call assert_equal(415, col('.'))
exe "norm! \<C-D>"
call assert_equal(520, winsaveview().skipcol)
- call assert_equal(601, col('.'))
+ call assert_equal(535, col('.'))
exe "norm! \<C-D>"
call assert_equal(520, winsaveview().skipcol)
- call assert_equal(801, col('.'))
- exe "norm! \<C-U>"
+ call assert_equal(735, col('.'))
+ exe "norm! \<C-D>"
call assert_equal(520, winsaveview().skipcol)
- call assert_equal(601, col('.'))
+ call assert_equal(895, col('.'))
exe "norm! \<C-U>"
- call assert_equal(400, winsaveview().skipcol)
- call assert_equal(404, col('.'))
+ call assert_equal(320, winsaveview().skipcol)
+ call assert_equal(695, col('.'))
exe "norm! \<C-U>"
- call assert_equal(200, winsaveview().skipcol)
- call assert_equal(204, col('.'))
+ call assert_equal(120, winsaveview().skipcol)
+ call assert_equal(495, col('.'))
+ exe "norm! \<C-U>"
+ call assert_equal(0, winsaveview().skipcol)
+ call assert_equal(375, col('.'))
exe "norm! \<C-U>"
call assert_equal(0, winsaveview().skipcol)
- call assert_equal(40, col('.'))
+ call assert_equal(175, col('.'))
+ exe "norm! \<C-U>"
+ call assert_equal(0, winsaveview().skipcol)
+ call assert_equal(15, col('.'))
bwipe!
endfunc
@@ -1071,6 +1077,14 @@ func Test_smoothscroll_next_topline()
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\<C-Y>"
+ redraw
+ call assert_equal(2, line('.'))
+ call assert_equal(0, winsaveview().skipcol)
+
+ set cpo-=n
bwipe!
endfunc
diff --git a/src/version.c b/src/version.c
index 6f49e116b5..5f0c909847 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 280,
+/**/
279,
/**/
278,