changeset 31954:d8fdafc4b390 v9.0.1309

patch 9.0.1309: scrolling two lines with even line count and 'scrolloff' set Commit: https://github.com/vim/vim/commit/1d6539cf36a7b6d1afe76fb6316fe662f543bf60 Author: Bram Moolenaar <Bram@vim.org> Date: Tue Feb 14 17:41:20 2023 +0000 patch 9.0.1309: scrolling two lines with even line count and 'scrolloff' set Problem: Scrolling two lines with even line count and 'scrolloff' set. Solution: Adjust how the topline is computed. (closes https://github.com/vim/vim/issues/10545)
author Bram Moolenaar <Bram@vim.org>
date Tue, 14 Feb 2023 18:45:05 +0100
parents 80d41e6f9f2a
children 4019f3b2526c
files src/buffer.c src/move.c src/normal.c src/proto/move.pro src/testdir/dumps/Test_popupwin_previewpopup_2.dump src/testdir/dumps/Test_popupwin_previewpopup_3.dump src/testdir/dumps/Test_popupwin_previewpopup_4.dump src/testdir/dumps/Test_popupwin_previewpopup_5.dump src/testdir/test_scroll_opt.vim src/testdir/test_scrollbind.vim src/version.c
diffstat 11 files changed, 93 insertions(+), 55 deletions(-) [+]
line wrap: on
line diff
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -1953,7 +1953,7 @@ enter_buffer(buf_T *buf)
     maketitle();
 	// when autocmds didn't change it
     if (curwin->w_topline == 1 && !curwin->w_topline_was_set)
-	scroll_cursor_halfway(FALSE);	// redisplay at correct position
+	scroll_cursor_halfway(FALSE, FALSE);	// redisplay at correct position
 
 #ifdef FEAT_NETBEANS_INTG
     // Send fileOpened event because we've changed buffers.
--- a/src/move.c
+++ b/src/move.c
@@ -396,7 +396,7 @@ update_topline(void)
 	    // cursor in the middle of the window.  Otherwise put the cursor
 	    // near the top of the window.
 	    if (n >= halfheight)
-		scroll_cursor_halfway(FALSE);
+		scroll_cursor_halfway(FALSE, FALSE);
 	    else
 	    {
 		scroll_cursor_top(scrolljump_value(), FALSE);
@@ -499,7 +499,7 @@ update_topline(void)
 		if (line_count <= curwin->w_height + 1)
 		    scroll_cursor_bot(scrolljump_value(), FALSE);
 		else
-		    scroll_cursor_halfway(FALSE);
+		    scroll_cursor_halfway(FALSE, FALSE);
 	    }
 	}
     }
@@ -2383,7 +2383,7 @@ scroll_cursor_top(int min_scroll, int al
      * in a small window.
      */
     if (used > curwin->w_height)
-	scroll_cursor_halfway(FALSE);
+	scroll_cursor_halfway(FALSE, FALSE);
     else
     {
 	/*
@@ -2720,7 +2720,7 @@ scroll_cursor_bot(int min_scroll, int se
      * Otherwise put it at 1/2 of the screen.
      */
     if (line_count >= curwin->w_height && line_count > min_scroll)
-	scroll_cursor_halfway(FALSE);
+	scroll_cursor_halfway(FALSE, TRUE);
     else
     {
 	// With 'smoothscroll' scroll at least the height of the cursor line,
@@ -2760,7 +2760,7 @@ scroll_cursor_bot(int min_scroll, int se
  * If "atend" is TRUE, also put it halfway at the end of the file.
  */
     void
-scroll_cursor_halfway(int atend)
+scroll_cursor_halfway(int atend, int prefer_above)
 {
     int		above = 0;
     linenr_T	topline;
@@ -2841,43 +2841,62 @@ scroll_cursor_halfway(int atend)
 
 	// If not using smoothscroll, we have to iteratively find how many
 	// lines to scroll down to roughly fit the cursor.
-	// This may not be right in the middle if the lines' physical height >
-	// 1 (e.g. 'wrap' is on).
+	// This may not be right in the middle if the lines'
+	// physical height > 1 (e.g. 'wrap' is on).
 
-	if (below <= above)	    // add a line below the cursor first
+	// Depending on "prefer_above" we add a line above or below first.
+	// Loop twice to avoid duplicating code.
+	int done = FALSE;
+	for (int round = 1; round <= 2; ++round)
 	{
-	    if (boff.lnum < curbuf->b_ml.ml_line_count)
+	    if (prefer_above ? (round == 2 && below < above)
+			     : (round == 1 && below <= above))
 	    {
-		botline_forw(&boff);
-		used += boff.height;
-		if (used > curwin->w_height)
-		    break;
-		below += boff.height;
+		// add a line below the cursor
+		if (boff.lnum < curbuf->b_ml.ml_line_count)
+		{
+		    botline_forw(&boff);
+		    used += boff.height;
+		    if (used > curwin->w_height)
+		    {
+			done = TRUE;
+			break;
+		    }
+		    below += boff.height;
+		}
+		else
+		{
+		    ++below;	    // count a "~" line
+		    if (atend)
+			++used;
+		}
 	    }
-	    else
+
+	    if (prefer_above ? (round == 1 && below >= above)
+			     : (round == 1 && below > above))
 	    {
-		++below;	    // count a "~" line
-		if (atend)
-		    ++used;
+		// add a line above the cursor
+		topline_back(&loff);
+		if (loff.height == MAXCOL)
+		    used = MAXCOL;
+		else
+		    used += loff.height;
+		if (used > curwin->w_height)
+		{
+		    done = TRUE;
+		    break;
+		}
+		above += loff.height;
+		topline = loff.lnum;
+#ifdef FEAT_DIFF
+		topfill = loff.fill;
+#endif
 	    }
 	}
+	if (done)
+	    break;
+    }
 
-	if (below > above)	    // add a line above the cursor
-	{
-	    topline_back(&loff);
-	    if (loff.height == MAXCOL)
-		used = MAXCOL;
-	    else
-		used += loff.height;
-	    if (used > curwin->w_height)
-		break;
-	    above += loff.height;
-	    topline = loff.lnum;
-#ifdef FEAT_DIFF
-	    topfill = loff.fill;
-#endif
-	}
-    }
 #ifdef FEAT_FOLDING
     if (!hasFolding(topline, &curwin->w_topline, NULL))
 #endif
--- a/src/normal.c
+++ b/src/normal.c
@@ -2712,7 +2712,7 @@ nv_zet(cmdarg_T *cap)
     case '.':	beginline(BL_WHITE | BL_FIX);
 		// FALLTHROUGH
 
-    case 'z':	scroll_cursor_halfway(TRUE);
+    case 'z':	scroll_cursor_halfway(TRUE, FALSE);
 		redraw_later(UPD_VALID);
 		set_fraction(curwin);
 		break;
--- a/src/proto/move.pro
+++ b/src/proto/move.pro
@@ -43,7 +43,7 @@ void scrollup_clamp(void);
 void scroll_cursor_top(int min_scroll, int always);
 void set_empty_rows(win_T *wp, int used);
 void scroll_cursor_bot(int min_scroll, int set_topbot);
-void scroll_cursor_halfway(int atend);
+void scroll_cursor_halfway(int atend, int prefer_above);
 void cursor_correct(void);
 int onepage(int dir, long count);
 void halfpage(int flag, linenr_T Prenum);
--- a/src/testdir/dumps/Test_popupwin_previewpopup_2.dump
+++ b/src/testdir/dumps/Test_popupwin_previewpopup_2.dump
@@ -2,10 +2,10 @@
 |#|i|n|c|l|u|d|e| |"|X|h|e|a|d|e|r|.|h|"| @54
 |t|h|r|e@1| @69
 |f|o|u|r| @3|╔+0&#afffff255| |X|t|a|g|f|i|l|e| |═@30|X| +0&#ffffff0@23
-|f|i|v|e| @3|║+0&#afffff255|2|7| @37| +0&#a8a8a8255|║+0&#afffff255| +0&#ffffff0@23
-|s|i|x| @4|║+0&#afffff255|t|h|i|s| |i|s| |a|n|o|t|h|e|r| |p|l|a|c|e| @18| +0&#a8a8a8255|║+0&#afffff255| +0&#ffffff0@23
-|s|e|v|e|n| @2|║+0&#afffff255|2|9| @37| +0&#0000001|║+0&#afffff255| +0&#ffffff0@23
-|f|i|n|d| |t|h|e|║+0&#afffff255|3|0| @37| +0&#a8a8a8255|║+0&#afffff255| +0&#ffffff0@23
+|f|i|v|e| @3|║+0&#afffff255|2|6| @37| +0&#a8a8a8255|║+0&#afffff255| +0&#ffffff0@23
+|s|i|x| @4|║+0&#afffff255|2|7| @37| +0&#a8a8a8255|║+0&#afffff255| +0&#ffffff0@23
+|s|e|v|e|n| @2|║+0&#afffff255|t|h|i|s| |i|s| |a|n|o|t|h|e|r| |p|l|a|c|e| @18| +0&#0000001|║+0&#afffff255| +0&#ffffff0@23
+|f|i|n|d| |t|h|e|║+0&#afffff255|2|9| @37| +0&#a8a8a8255|║+0&#afffff255| +0&#ffffff0@23
 |n|i|n|e| @3|╚+0&#afffff255|═@40|⇲| +0&#ffffff0@23
 |t|h|i|s| |i|s| >a|n|o|t|h|e|r| |w|o|r|d| @54
 |v|e|r|y| |l|o|n|g| |l|i|n|e| |w|h|e|r|e| |t|h|e| |w|o|r|d| |i|s| |a|l|s|o| |a|n|o|t|h|e|r| @29
--- a/src/testdir/dumps/Test_popupwin_previewpopup_3.dump
+++ b/src/testdir/dumps/Test_popupwin_previewpopup_3.dump
@@ -2,10 +2,10 @@
 |#|i|n|c|l|u|d|e| |"|X|h|e|a|d|e|r|.|h|"| @54
 |t|h|r|e@1| @69
 |f|o|u|r| @9|╔+0&#afffff255| |X|t|a|g|f|i|l|e| |═@30|X| +0&#ffffff0@17
-|f|i|v|e| @9|║+0&#afffff255|2|7| @37| +0&#a8a8a8255|║+0&#afffff255| +0&#ffffff0@17
-|s|i|x| @10|║+0&#afffff255|t|h|i|s| |i|s| |a|n|o|t|h|e|r| |p|l|a|c|e| @18| +0&#a8a8a8255|║+0&#afffff255| +0&#ffffff0@17
-|s|e|v|e|n| @8|║+0&#afffff255|2|9| @37| +0&#0000001|║+0&#afffff255| +0&#ffffff0@17
-|f|i|n|d| |t|h|e|w|o|r|d| |s|║+0&#afffff255|3|0| @37| +0&#a8a8a8255|║+0&#afffff255| +0&#ffffff0@17
+|f|i|v|e| @9|║+0&#afffff255|2|6| @37| +0&#a8a8a8255|║+0&#afffff255| +0&#ffffff0@17
+|s|i|x| @10|║+0&#afffff255|2|7| @37| +0&#a8a8a8255|║+0&#afffff255| +0&#ffffff0@17
+|s|e|v|e|n| @8|║+0&#afffff255|t|h|i|s| |i|s| |a|n|o|t|h|e|r| |p|l|a|c|e| @18| +0&#0000001|║+0&#afffff255| +0&#ffffff0@17
+|f|i|n|d| |t|h|e|w|o|r|d| |s|║+0&#afffff255|2|9| @37| +0&#a8a8a8255|║+0&#afffff255| +0&#ffffff0@17
 |n|i|n|e| @9|╚+0&#afffff255|═@40|⇲| +0&#ffffff0@17
 |t|h|i|s| |i|s| >a|n|o|t|h|e|r| |w|o|r|d| @54
 |v|e|r|y| |l|o|n|g| |l|i|n|e| |w|h|e|r|e| |t|h|e| |w|o|r|d| |i|s| |a|l|s|o| |a|n|o|t|h|e|r| @29
--- a/src/testdir/dumps/Test_popupwin_previewpopup_4.dump
+++ b/src/testdir/dumps/Test_popupwin_previewpopup_4.dump
@@ -3,10 +3,10 @@
 |t|h|r|e@1| @69
 |f|o|u|r| @70
 |f|i|v|e| @27|╔+0&#afffff255| |X|t|a|g|f|i|l|e| |═@30|X
-|s+0&#ffffff0|i|x| @28|║+0&#afffff255|2|7| @37| +0&#a8a8a8255|║+0&#afffff255
-|s+0&#ffffff0|e|v|e|n| @26|║+0&#afffff255|t|h|i|s| |i|s| |a|n|o|t|h|e|r| |p|l|a|c|e| @18| +0&#a8a8a8255|║+0&#afffff255
-|f+0&#ffffff0|i|n|d| |t|h|e|w|o|r|d| |s|o|m|e|w|h|e|r|e| @9|║+0&#afffff255|2|9| @37| +0&#0000001|║+0&#afffff255
-|n+0&#ffffff0|i|n|e| @27|║+0&#afffff255|3|0| @37| +0&#a8a8a8255|║+0&#afffff255
+|s+0&#ffffff0|i|x| @28|║+0&#afffff255|2|6| @37| +0&#a8a8a8255|║+0&#afffff255
+|s+0&#ffffff0|e|v|e|n| @26|║+0&#afffff255|2|7| @37| +0&#a8a8a8255|║+0&#afffff255
+|f+0&#ffffff0|i|n|d| |t|h|e|w|o|r|d| |s|o|m|e|w|h|e|r|e| @9|║+0&#afffff255|t|h|i|s| |i|s| |a|n|o|t|h|e|r| |p|l|a|c|e| @18| +0&#0000001|║+0&#afffff255
+|n+0&#ffffff0|i|n|e| @27|║+0&#afffff255|2|9| @37| +0&#a8a8a8255|║+0&#afffff255
 |t+0&#ffffff0|h|i|s| |i|s| |a|n|o|t|h|e|r| |w|o|r|d| @11|╚+0&#afffff255|═@40|⇲
 |v+0&#ffffff0|e|r|y| |l|o|n|g| |l|i|n|e| |w|h|e|r|e| |t|h|e| |w|o|r|d| |i|s| |a|l|s|o| >a|n|o|t|h|e|r| @29
 |~+0#4040ff13&| @73
--- a/src/testdir/dumps/Test_popupwin_previewpopup_5.dump
+++ b/src/testdir/dumps/Test_popupwin_previewpopup_5.dump
@@ -3,10 +3,10 @@
 |t|h|r|e@1| @69
 |f|o|u|r| @70
 |f|i|v|e| @27|╔+0&#afffff255| |t|e|s|t|d|i|r|/|X|t|a|g|f|i|l|e| |═@22|X
-|s+0&#ffffff0|i|x| @28|║+0&#afffff255|2|7| @37| +0&#a8a8a8255|║+0&#afffff255
-|s+0&#ffffff0|e|v|e|n| @26|║+0&#afffff255|t|h|i|s| |i|s| |a|n|o|t|h|e|r| |p|l|a|c|e| @18| +0&#a8a8a8255|║+0&#afffff255
-|f+0&#ffffff0|i|n|d| |t|h|e|w|o|r|d| |s|o|m|e|w|h|e|r|e| @9|║+0&#afffff255|2|9| @37| +0&#0000001|║+0&#afffff255
-|n+0&#ffffff0|i|n|e| @27|║+0&#afffff255|3|0| @37| +0&#a8a8a8255|║+0&#afffff255
+|s+0&#ffffff0|i|x| @28|║+0&#afffff255|2|6| @37| +0&#a8a8a8255|║+0&#afffff255
+|s+0&#ffffff0|e|v|e|n| @26|║+0&#afffff255|2|7| @37| +0&#a8a8a8255|║+0&#afffff255
+|f+0&#ffffff0|i|n|d| |t|h|e|w|o|r|d| |s|o|m|e|w|h|e|r|e| @9|║+0&#afffff255|t|h|i|s| |i|s| |a|n|o|t|h|e|r| |p|l|a|c|e| @18| +0&#0000001|║+0&#afffff255
+|n+0&#ffffff0|i|n|e| @27|║+0&#afffff255|2|9| @37| +0&#a8a8a8255|║+0&#afffff255
 |t+0&#ffffff0|h|i|s| |i|s| |a|n|o|t|h|e|r| |w|o|r|d| @11|╚+0&#afffff255|═@40|⇲
 |v+0&#ffffff0|e|r|y| |l|o|n|g| |l|i|n|e| |w|h|e|r|e| |t|h|e| |w|o|r|d| |i|s| |a|l|s|o| >a|n|o|t|h|e|r| @29
 |~+0#4040ff13&| @73
--- a/src/testdir/test_scroll_opt.vim
+++ b/src/testdir/test_scroll_opt.vim
@@ -38,6 +38,23 @@ func Test_reset_scroll()
   quit!
 endfunc
 
+func Test_scolloff_even_line_count()
+   new
+   resize 6
+   setlocal scrolloff=3
+   call setline(1, range(20))
+   normal 2j
+   call assert_equal(1, getwininfo(win_getid())[0].topline)
+   normal j
+   call assert_equal(1, getwininfo(win_getid())[0].topline)
+   normal j
+   call assert_equal(2, getwininfo(win_getid())[0].topline)
+   normal j
+   call assert_equal(3, getwininfo(win_getid())[0].topline)
+
+   bwipe!
+endfunc
+
 func Test_CtrlE_CtrlY_stop_at_end()
   enew
   call setline(1, ['one', 'two'])
--- a/src/testdir/test_scrollbind.vim
+++ b/src/testdir/test_scrollbind.vim
@@ -218,8 +218,8 @@ end of window 2
 	      \ '7 line 02 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 02',
 	      \ '56789ABCDEFGHIJKLMNOPQRSTUVWXYZ 02',
 	      \ 'UTSRQPONMLKJIHGREDCBA9876543210 02',
-	      \ '. line 11 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 11',
-	      \ '. line 11 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 11',
+	      \ '. line 10 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 10',
+	      \ '. line 10 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 10',
 	      \ ''],  getline(1, '$'))
   enew!
 
--- a/src/version.c
+++ b/src/version.c
@@ -696,6 +696,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1309,
+/**/
     1308,
 /**/
     1307,