changeset 30535:04df44c52d65 v9.0.0603

patch 9.0.0603: with 'nosplitscroll' folds are not handled correctly Commit: https://github.com/vim/vim/commit/7c1cbb6cd437c6e0c3ccc05840cc931108b4a60a Author: Luuk van Baal <luukvbaal@gmail.com> Date: Tue Sep 27 12:31:15 2022 +0100 patch 9.0.0603: with 'nosplitscroll' folds are not handled correctly Problem: With 'nosplitscroll' folds are not handled correctly. Solution: Take care of closed folds when moving the cursor. (Luuk van Baal, closes #11234)
author Bram Moolenaar <Bram@vim.org>
date Tue, 27 Sep 2022 13:45:04 +0200
parents e2cfe68129e6
children e52d1209a9ee
files src/edit.c src/proto/edit.pro src/testdir/dumps/Test_nosplitscroll_fold_1.dump src/testdir/dumps/Test_nosplitscroll_fold_2.dump src/testdir/dumps/Test_nosplitscroll_fold_3.dump src/testdir/test_window_cmd.vim src/version.c src/window.c
diffstat 8 files changed, 206 insertions(+), 111 deletions(-) [+]
line wrap: on
line diff
--- a/src/edit.c
+++ b/src/edit.c
@@ -2749,52 +2749,62 @@ oneleft(void)
     return OK;
 }
 
+/*
+ * Move the cursor up "n" lines in window "wp".
+ * Takes care of closed folds.
+ * Returns the new cursor line or zero for failure.
+ */
+    linenr_T
+cursor_up_inner(win_T *wp, long n)
+{
+    linenr_T	lnum = wp->w_cursor.lnum;
+
+    // This fails if the cursor is already in the first line or the count is
+    // larger than the line number and '-' is in 'cpoptions'
+    if (lnum <= 1 || (n >= lnum && vim_strchr(p_cpo, CPO_MINUS) != NULL))
+	return 0;
+    if (n >= lnum)
+	lnum = 1;
+    else
+#ifdef FEAT_FOLDING
+	if (hasAnyFolding(wp))
+    {
+	/*
+	 * Count each sequence of folded lines as one logical line.
+	 */
+	// go to the start of the current fold
+	(void)hasFoldingWin(wp, lnum, &lnum, NULL, TRUE, NULL);
+
+	while (n--)
+	{
+	    // move up one line
+	    --lnum;
+	    if (lnum <= 1)
+		break;
+	    // If we entered a fold, move to the beginning, unless in
+	    // Insert mode or when 'foldopen' contains "all": it will open
+	    // in a moment.
+	    if (n > 0 || !((State & MODE_INSERT) || (fdo_flags & FDO_ALL)))
+		(void)hasFoldingWin(wp, lnum, &lnum, NULL, TRUE, NULL);
+	}
+	if (lnum < 1)
+	    lnum = 1;
+    }
+    else
+#endif
+	lnum -= n;
+
+    wp->w_cursor.lnum = lnum;
+    return lnum;
+}
+
     int
 cursor_up(
     long	n,
     int		upd_topline)	    // When TRUE: update topline
 {
-    linenr_T	lnum;
-
-    if (n > 0)
-    {
-	lnum = curwin->w_cursor.lnum;
-	// This fails if the cursor is already in the first line or the count
-	// is larger than the line number and '-' is in 'cpoptions'
-	if (lnum <= 1 || (n >= lnum && vim_strchr(p_cpo, CPO_MINUS) != NULL))
-	    return FAIL;
-	if (n >= lnum)
-	    lnum = 1;
-	else
-#ifdef FEAT_FOLDING
-	    if (hasAnyFolding(curwin))
-	{
-	    /*
-	     * Count each sequence of folded lines as one logical line.
-	     */
-	    // go to the start of the current fold
-	    (void)hasFolding(lnum, &lnum, NULL);
-
-	    while (n--)
-	    {
-		// move up one line
-		--lnum;
-		if (lnum <= 1)
-		    break;
-		// If we entered a fold, move to the beginning, unless in
-		// Insert mode or when 'foldopen' contains "all": it will open
-		// in a moment.
-		if (n > 0 || !((State & MODE_INSERT) || (fdo_flags & FDO_ALL)))
-		    (void)hasFolding(lnum, &lnum, NULL);
-	    }
-	    if (lnum < 1)
-		lnum = 1;
-	}
-	else
-#endif
-	    lnum -= n;
-	curwin->w_cursor.lnum = lnum;
-    }
+    if (n > 0 && cursor_up_inner(curwin, n) == 0)
+	return FAIL;
 
     // try to advance to the column we want to be at
     coladvance(curwin->w_curswant);
@@ -2806,6 +2816,55 @@ cursor_up(
 }
 
 /*
+ * Move the cursor down "n" lines in window "wp".
+ * Takes care of closed folds.
+ * Returns the new cursor line or zero for failure.
+ */
+    linenr_T
+cursor_down_inner(win_T *wp, long n)
+{
+    linenr_T	lnum = wp->w_cursor.lnum;
+    linenr_T	line_count = wp->w_buffer->b_ml.ml_line_count;
+
+#ifdef FEAT_FOLDING
+    // Move to last line of fold, will fail if it's the end-of-file.
+    (void)hasFoldingWin(wp, lnum, NULL, &lnum, TRUE, NULL);
+#endif
+    // This fails if the cursor is already in the last line or would move
+    // beyond the last line and '-' is in 'cpoptions'
+    if (lnum >= line_count
+	    || (lnum + n > line_count && vim_strchr(p_cpo, CPO_MINUS) != NULL))
+	return FAIL;
+    if (lnum + n >= line_count)
+	lnum = line_count;
+    else
+#ifdef FEAT_FOLDING
+	if (hasAnyFolding(wp))
+    {
+	linenr_T	last;
+
+	// count each sequence of folded lines as one logical line
+	while (n--)
+	{
+	    if (hasFoldingWin(wp, lnum, NULL, &last, TRUE, NULL))
+		lnum = last + 1;
+	    else
+		++lnum;
+	    if (lnum >= line_count)
+		break;
+	}
+	if (lnum > line_count)
+	    lnum = line_count;
+    }
+    else
+#endif
+	lnum += n;
+
+    wp->w_cursor.lnum = lnum;
+    return lnum;
+}
+
+/*
  * Cursor down a number of logical lines.
  */
     int
@@ -2813,47 +2872,8 @@ cursor_down(
     long	n,
     int		upd_topline)	    // When TRUE: update topline
 {
-    linenr_T	lnum;
-
-    if (n > 0)
-    {
-	lnum = curwin->w_cursor.lnum;
-#ifdef FEAT_FOLDING
-	// Move to last line of fold, will fail if it's the end-of-file.
-	(void)hasFolding(lnum, NULL, &lnum);
-#endif
-	// This fails if the cursor is already in the last line or would move
-	// beyond the last line and '-' is in 'cpoptions'
-	if (lnum >= curbuf->b_ml.ml_line_count
-		|| (lnum + n > curbuf->b_ml.ml_line_count
-		    && vim_strchr(p_cpo, CPO_MINUS) != NULL))
-	    return FAIL;
-	if (lnum + n >= curbuf->b_ml.ml_line_count)
-	    lnum = curbuf->b_ml.ml_line_count;
-	else
-#ifdef FEAT_FOLDING
-	if (hasAnyFolding(curwin))
-	{
-	    linenr_T	last;
-
-	    // count each sequence of folded lines as one logical line
-	    while (n--)
-	    {
-		if (hasFolding(lnum, NULL, &last))
-		    lnum = last + 1;
-		else
-		    ++lnum;
-		if (lnum >= curbuf->b_ml.ml_line_count)
-		    break;
-	    }
-	    if (lnum > curbuf->b_ml.ml_line_count)
-		lnum = curbuf->b_ml.ml_line_count;
-	}
-	else
-#endif
-	    lnum += n;
-	curwin->w_cursor.lnum = lnum;
-    }
+    if (n > 0 &&  cursor_down_inner(curwin, n) == 0)
+	return FAIL;
 
     // try to advance to the column we want to be at
     coladvance(curwin->w_curswant);
--- a/src/proto/edit.pro
+++ b/src/proto/edit.pro
@@ -19,7 +19,9 @@ char_u *add_char2buf(int c, char_u *s);
 void beginline(int flags);
 int oneright(void);
 int oneleft(void);
+linenr_T cursor_up_inner(win_T *wp, long n);
 int cursor_up(long n, int upd_topline);
+linenr_T cursor_down_inner(win_T *wp, long n);
 int cursor_down(long n, int upd_topline);
 int stuff_inserted(int c, long count, int no_esc);
 char_u *get_last_insert(void);
new file mode 100644
--- /dev/null
+++ b/src/testdir/dumps/Test_nosplitscroll_fold_1.dump
@@ -0,0 +1,10 @@
+| +0#0000e05#a8a8a8255@1|1| |+|-@1| @1|7| |l|i|n|e|s|:| |i|n|t| |F|u|n|c|N|a|m|e|(|)| |{|-@40
+| +0#af5f00255#ffffff0@1|8| >a+0#0000000&|f|t|e|r| |f|o|l|d| @60
+| +0#0000e05#a8a8a8255@1|9| |+|-@1| @1|7| |l|i|n|e|s|:| |i|n|t| |F|u|n|c|N|a|m|e|(|)| |{|-@40
+| +0#af5f00255#ffffff0|1|6| |a+0#0000000&|f|t|e|r| |f|o|l|d| @60
+|[+3&&|N|o| |N|a|m|e|]| |[|+|]| @43|8|,|1| @11|T|o|p
+| +0#af5f00255&|2|4| |a+0#0000000&|f|t|e|r| |f|o|l|d| @60
+| +0#0000e05#a8a8a8255|2|5| |+|-@1| @1|7| |l|i|n|e|s|:| |i|n|t| |F|u|n|c|N|a|m|e|(|)| |{|-@40
+| +0#af5f00255#ffffff0|3|2| |a+0#0000000&|f|t|e|r| |f|o|l|d| @60
+|[+1&&|N|o| |N|a|m|e|]| |[|+|]| @43|2|3|,|1| @10|3|2|%
+|:+0&&|w|i|n|c|m|d| |s| @65
new file mode 100644
--- /dev/null
+++ b/src/testdir/dumps/Test_nosplitscroll_fold_2.dump
@@ -0,0 +1,10 @@
+| +0#0000e05#a8a8a8255@1|1| |+|-@1| @1|7| |l|i|n|e|s|:| |i|n|t| |F|u|n|c|N|a|m|e|(|)| |{|-@40
+| +0#af5f00255#ffffff0@1|8| |a+0#0000000&|f|t|e|r| |f|o|l|d| @60
+| +0#0000e05#a8a8a8255@1|9| |+|-@1| @1|7| |l|i|n|e|s|:| |i|n|t| |F|u|n|c|N|a|m|e|(|)| |{|-@40
+| +0#af5f00255#ffffff0|1|6| |a+0#0000000&|f|t|e|r| |f|o|l|d| @60
+| +0#0000e05#a8a8a8255|1|7| >+|-@1| @1|7| |l|i|n|e|s|:| |i|n|t| |F|u|n|c|N|a|m|e|(|)| |{|-@40
+| +0#af5f00255#ffffff0|2|4| |a+0#0000000&|f|t|e|r| |f|o|l|d| @60
+| +0#0000e05#a8a8a8255|2|5| |+|-@1| @1|7| |l|i|n|e|s|:| |i|n|t| |F|u|n|c|N|a|m|e|(|)| |{|-@40
+| +0#af5f00255#ffffff0|3|2| |a+0#0000000&|f|t|e|r| |f|o|l|d| @60
+| +0#0000e05#a8a8a8255|3@1| |+|-@1| @1|7| |l|i|n|e|s|:| |i|n|t| |F|u|n|c|N|a|m|e|(|)| |{|-@40
+|:+0#0000000#ffffff0|q|u|i|t| @51|1|7|,|1| @9|T|o|p| 
new file mode 100644
--- /dev/null
+++ b/src/testdir/dumps/Test_nosplitscroll_fold_3.dump
@@ -0,0 +1,10 @@
+| +0#0000e05#a8a8a8255@1|1| |+|-@1| @1|7| |l|i|n|e|s|:| |i|n|t| |F|u|n|c|N|a|m|e|(|)| |{|-@40
+| +0#af5f00255#ffffff0@1|8| |a+0#0000000&|f|t|e|r| |f|o|l|d| @60
+| +0#0000e05#a8a8a8255@1|9| |+|-@1| @1|7| |l|i|n|e|s|:| |i|n|t| |F|u|n|c|N|a|m|e|(|)| |{|-@40
+|[+1#0000000#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @43|1|,|1| @11|T|o|p
+| +0#0000e05#a8a8a8255|1|7| |+|-@1| @1|7| |l|i|n|e|s|:| |i|n|t| |F|u|n|c|N|a|m|e|(|)| |{|-@40
+| +0#af5f00255#ffffff0|2|4| >a+0#0000000&|f|t|e|r| |f|o|l|d| @60
+| +0#0000e05#a8a8a8255|2|5| |+|-@1| @1|7| |l|i|n|e|s|:| |i|n|t| |F|u|n|c|N|a|m|e|(|)| |{|-@40
+| +0#af5f00255#ffffff0|3|2| |a+0#0000000&|f|t|e|r| |f|o|l|d| @60
+|[+3&&|N|o| |N|a|m|e|]| |[|+|]| @43|2|4|,|1| @10|2|5|%
+|:+0&&|b|e|l|o|w| |s|p|l|i|t| @62
--- a/src/testdir/test_window_cmd.vim
+++ b/src/testdir/test_window_cmd.vim
@@ -1848,4 +1848,34 @@ function Test_nosplitscroll_callback()
   call VerifyScreenDump(buf, 'Test_nosplitscroll_callback_4', {})
 endfunc
 
+function Test_nosplitscroll_fold()
+CheckScreendump
+
+let lines =<< trim END
+  set nosplitscroll
+  set foldmethod=marker
+  set number
+  let line = 1
+  for n in range(1, &lines)
+    call setline(line, ['int FuncName() {/*{{{*/', 1, 2, 3, 4, 5, '}/*}}}*/',
+          \ 'after fold'])
+    let line += 8
+  endfor
+END
+  call writefile(lines, 'XTestNosplitscrollFold', 'D')
+  let buf = RunVimInTerminal('-S XTestNosplitscrollFold', #{rows: 10})
+
+  call term_sendkeys(buf, "L:wincmd s\<CR>")
+  call VerifyScreenDump(buf, 'Test_nosplitscroll_fold_1', {})
+
+  call term_sendkeys(buf, ":quit\<CR>")
+  call VerifyScreenDump(buf, 'Test_nosplitscroll_fold_2', {})
+
+  call term_sendkeys(buf, "H:below split\<CR>")
+  call VerifyScreenDump(buf, 'Test_nosplitscroll_fold_3', {})
+
+  call term_sendkeys(buf, ":wincmd k\<CR>:quit\<CR>")
+  call VerifyScreenDump(buf, 'Test_nosplitscroll_fold_4', {})
+endfunction
+
 " vim: shiftwidth=2 sts=2 expandtab
--- a/src/version.c
+++ b/src/version.c
@@ -700,6 +700,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    603,
+/**/
     602,
 /**/
     601,
--- a/src/window.c
+++ b/src/window.c
@@ -6351,7 +6351,7 @@ set_fraction(win_T *wp)
 /*
  * Handle scroll position for 'nosplitscroll'.  Replaces scroll_to_fraction()
  * call from win_new_height().  Instead we iterate over all windows in a
- * tabpage and calculate the new scroll/cursor position.
+ * tabpage and calculate the new scroll position.
  * TODO: Ensure this also works with wrapped lines.
  * Requires topline to be able to be set to a bufferline with some
  * offset(row-wise scrolling/smoothscroll).
@@ -6359,8 +6359,9 @@ set_fraction(win_T *wp)
     static void
 win_fix_scroll(int resize)
 {
-    win_T    *wp;
-    linenr_T lnum;
+    int		diff;
+    win_T	*wp;
+    linenr_T	lnum;
 
     skip_update_topline = TRUE;  // avoid scrolling in curs_columns()
     FOR_ALL_WINDOWS(wp)
@@ -6368,13 +6369,18 @@ win_fix_scroll(int resize)
 	// Skip when window height has not changed.
 	if (wp->w_height != wp->w_prev_height)
 	{
-	    // Determine botline needed to avoid scrolling and set cursor.
+	    // If window has moved update botline to keep the same screenlines.
 	    if (wp->w_winrow != wp->w_prev_winrow)
 	    {
 		lnum = wp->w_cursor.lnum;
-		wp->w_cursor.lnum = MIN(wp->w_buffer->b_ml.ml_line_count,
-			wp->w_botline - 1 + (wp->w_winrow - wp->w_prev_winrow)
-					  + (wp->w_height - wp->w_prev_height));
+		diff = (wp->w_winrow - wp->w_prev_winrow)
+		     + (wp->w_height - wp->w_prev_height);
+		wp->w_cursor.lnum = wp->w_botline - 1;
+		//  Add difference in height and row to botline.
+		if (diff > 0)
+		    cursor_down_inner(wp, diff);
+		else
+		    cursor_up_inner(wp, -diff);
 		// Bring the new cursor position to the bottom of the screen.
 		wp->w_fraction = FRACTION_MULT;
 		scroll_to_fraction(wp, wp->w_prev_height);
@@ -6405,9 +6411,12 @@ win_fix_scroll(int resize)
     static void
 win_fix_cursor(int normal)
 {
-    win_T    *wp = curwin;
-    long     so = get_scrolloff_value();
-    linenr_T nlnum = 0;
+    long	so = get_scrolloff_value();
+    win_T	*wp = curwin;
+    linenr_T	nlnum = 0;
+    linenr_T    lnum = wp->w_cursor.lnum;
+    linenr_T    bot;
+    linenr_T    top;
 
     if (wp->w_buffer->b_ml.ml_line_count < wp->w_height)
 	return;
@@ -6415,28 +6424,30 @@ win_fix_cursor(int normal)
     if (skip_win_fix_cursor)
 	return;
 #endif
-
+    // Determine valid cursor range.
     so = MIN(wp->w_height / 2, so);
-    // Check if cursor position is above topline or below botline.
-    if (wp->w_cursor.lnum < (wp->w_topline + so) && wp->w_topline != 1)
-	nlnum = MIN(wp->w_topline + so, wp->w_buffer->b_ml.ml_line_count);
-    else if (wp->w_cursor.lnum > (wp->w_botline - so - 1)
-	    && (wp->w_botline - wp->w_buffer->b_ml.ml_line_count) != 1)
-	nlnum = MAX(wp->w_botline - so - 1, 1);
-    // If cursor was invalid scroll or change cursor.
-    if (nlnum)
-    {
-	if (normal)
-	{   // Make sure cursor is closer to topline than botline.
-	    if (so == wp->w_height / 2
-			  && nlnum - wp->w_topline > wp->w_botline - 1 - nlnum)
-		nlnum--;
-	    setmark('\'');		// save cursor position
-	    wp->w_cursor.lnum = nlnum;	// change to avoid scrolling
+    wp->w_cursor.lnum = wp->w_topline;
+    top = cursor_down_inner(wp, so);
+    wp->w_cursor.lnum = wp->w_botline - 1;
+    bot = cursor_up_inner(wp, so);
+    // Check if cursor position is above or below valid cursor range.
+    if (lnum > bot && (wp->w_botline - wp->w_buffer->b_ml.ml_line_count) != 1)
+	nlnum = bot;
+    else if (lnum < top && wp->w_topline != 1)
+	nlnum = (so == wp->w_height / 2) ? bot : top;
+
+    wp->w_cursor.lnum = lnum;
+
+    if (nlnum)  // Cursor is invalid for current scroll position.
+    {
+	if (normal)  // Save to jumplist and set cursor to avoid scrolling.
+	{
+	    setmark('\'');
+	    wp->w_cursor.lnum = nlnum;
 	    curs_columns(TRUE);		// validate w_wrow
 	}
-	else
-	{   // Ensure cursor stays visible if we are not in normal mode.
+	else  // Scroll instead when not in normal mode.
+	{
 	    wp->w_fraction = 0.5 * FRACTION_MULT;
 	    scroll_to_fraction(wp, wp->w_prev_height);
 	    validate_botline_win(curwin);