changeset 30799:ffa5492137c3 v9.0.0734

patch 9.0.0734: cursor position invalid when scrolling with 'smoothscroll' Commit: https://github.com/vim/vim/commit/2fbabd238a94022c99506e920186a5b6cdf15426 Author: Bram Moolenaar <Bram@vim.org> Date: Wed Oct 12 19:53:38 2022 +0100 patch 9.0.0734: cursor position invalid when scrolling with 'smoothscroll' Problem: Cursor position invalid when scrolling with 'smoothscroll' set. (Ernie Rael) Solution: Add w_valid_skipcol and clear flags when it changes. Adjust w_skipcol after moving the cursor.
author Bram Moolenaar <Bram@vim.org>
date Wed, 12 Oct 2022 21:00:05 +0200
parents a87c6faa01e3
children 96c0737e2595
files src/edit.c src/move.c src/normal.c src/proto/move.pro src/structs.h src/testdir/dumps/Test_smooth_long_8.dump src/testdir/dumps/Test_smooth_long_9.dump src/testdir/dumps/Test_smooth_one_long_1.dump src/testdir/dumps/Test_smooth_one_long_2.dump src/testdir/test_scroll_opt.vim src/version.c
diffstat 11 files changed, 132 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/src/edit.c
+++ b/src/edit.c
@@ -2636,6 +2636,7 @@ beginline(int flags)
 	}
 	curwin->w_set_curswant = TRUE;
     }
+    adjust_skipcol();
 }
 
 /*
@@ -2683,6 +2684,7 @@ oneright(void)
     curwin->w_cursor.col += l;
 
     curwin->w_set_curswant = TRUE;
+    adjust_skipcol();
     return OK;
 }
 
@@ -2742,6 +2744,7 @@ oneleft(void)
     // character, move to its first byte
     if (has_mbyte)
 	mb_adjust_cursor();
+    adjust_skipcol();
     return OK;
 }
 
--- a/src/move.c
+++ b/src/move.c
@@ -552,6 +552,16 @@ check_cursor_moved(win_T *wp)
 				      |VALID_BOTLINE|VALID_BOTLINE_AP);
 	wp->w_valid_cursor = wp->w_cursor;
 	wp->w_valid_leftcol = wp->w_leftcol;
+	wp->w_valid_skipcol = wp->w_skipcol;
+    }
+    else if (wp->w_skipcol != wp->w_valid_skipcol)
+    {
+	wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL
+				      |VALID_CHEIGHT|VALID_CROW
+				      |VALID_BOTLINE|VALID_BOTLINE_AP);
+	wp->w_valid_cursor = wp->w_cursor;
+	wp->w_valid_leftcol = wp->w_leftcol;
+	wp->w_valid_skipcol = wp->w_skipcol;
     }
     else if (wp->w_cursor.col != wp->w_valid_cursor.col
 	     || wp->w_leftcol != wp->w_valid_leftcol
@@ -878,7 +888,6 @@ curs_rows(win_T *wp)
 
     redraw_for_cursorline(curwin);
     wp->w_valid |= VALID_CROW|VALID_CHEIGHT;
-
 }
 
 /*
@@ -1092,6 +1101,11 @@ curs_columns(
     {
 	width = textwidth + curwin_col_off2();
 
+	// skip columns that are not visible
+	if (curwin->w_cursor.lnum == curwin->w_topline
+		&& curwin->w_wcol >= curwin->w_skipcol)
+	    curwin->w_wcol -= curwin->w_skipcol;
+
 	// long line wrapping, adjust curwin->w_wrow
 	if (curwin->w_wcol >= curwin->w_width)
 	{
@@ -1265,10 +1279,12 @@ curs_columns(
 	    while (endcol > curwin->w_virtcol)
 		endcol -= width;
 	    if (endcol > curwin->w_skipcol)
+	    {
+		curwin->w_wrow -= (endcol - curwin->w_skipcol) / width;
 		curwin->w_skipcol = endcol;
+	    }
 	}
 
-	curwin->w_wrow -= curwin->w_skipcol / width;
 	if (curwin->w_wrow >= curwin->w_height)
 	{
 	    // small window, make sure cursor is in it
@@ -1302,8 +1318,10 @@ curs_columns(
 	curwin->w_flags &= ~(WFLAG_WCOL_OFF_ADDED + WFLAG_WROW_OFF_ADDED);
 #endif
 
-    // now w_leftcol is valid, avoid check_cursor_moved() thinking otherwise
+    // now w_leftcol and w_skipcol are valid, avoid check_cursor_moved()
+    // thinking otherwise
     curwin->w_valid_leftcol = curwin->w_leftcol;
+    curwin->w_valid_skipcol = curwin->w_skipcol;
 
     curwin->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL;
 }
@@ -1772,10 +1790,74 @@ scrollup(
 		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);
 	}
     }
 }
 
+/*
+ * Called after changing the cursor column: make sure that curwin->w_skipcol is
+ * valid for 'smoothscroll'.
+ */
+    void
+adjust_skipcol(void)
+{
+    if (!curwin->w_p_wrap
+	    || !curwin->w_p_sms
+	    || curwin->w_cursor.lnum != curwin->w_topline)
+	return;
+
+    int	    width1 = curwin->w_width - curwin_col_off();
+    int	    width2 = width1 + curwin_col_off2();
+    long    so = curwin->w_p_so >= 0 ? curwin->w_p_so : p_so;
+    int	    scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2;
+    int	    scrolled = FALSE;
+
+    validate_virtcol();
+    while (curwin->w_skipcol > 0
+		 && curwin->w_virtcol < curwin->w_skipcol + 3 + scrolloff_cols)
+    {
+	// scroll a screen line down
+	if (curwin->w_skipcol >= width1 + width2)
+	    curwin->w_skipcol -= width2;
+	else
+	    curwin->w_skipcol -= width1;
+	redraw_later(UPD_NOT_VALID);
+	scrolled = TRUE;
+	validate_virtcol();
+    }
+    if (scrolled)
+	return;  // don't scroll in the other direction now
+
+    int col = curwin->w_virtcol - curwin->w_skipcol + scrolloff_cols;
+    int row = 0;
+    if (col >= width1)
+    {
+	col -= width1;
+	++row;
+    }
+    if (col > width2)
+    {
+	row += col / width2;
+	col = col % width2;
+    }
+    if (row >= curwin->w_height)
+    {
+	if (curwin->w_skipcol == 0)
+	{
+	    curwin->w_skipcol += width1;
+	    --row;
+	}
+	if (row >= curwin->w_height)
+	    curwin->w_skipcol += (row - curwin->w_height) * width2;
+	redraw_later(UPD_NOT_VALID);
+    }
+}
+
 #ifdef FEAT_DIFF
 /*
  * Don't end up with too many filler lines in the window.
--- a/src/normal.c
+++ b/src/normal.c
@@ -2448,6 +2448,7 @@ nv_screengo(oparg_T *oap, int dir, long 
 
     if (atend)
 	curwin->w_curswant = MAXCOL;	    // stick in the last column
+    adjust_skipcol();
 
     return retval;
 }
--- a/src/proto/move.pro
+++ b/src/proto/move.pro
@@ -35,6 +35,7 @@ void f_screenpos(typval_T *argvars, typv
 void f_virtcol2col(typval_T *argvars, typval_T *rettv);
 void scrolldown(long line_count, int byfold);
 void scrollup(long line_count, int byfold);
+void adjust_skipcol(void);
 void check_topfill(win_T *wp, int down);
 void scrolldown_clamp(void);
 void scrollup_clamp(void);
--- a/src/structs.h
+++ b/src/structs.h
@@ -3714,6 +3714,7 @@ struct window_S
     pos_T	w_valid_cursor;	    // last known position of w_cursor, used
 				    // to adjust w_valid
     colnr_T	w_valid_leftcol;    // last known w_leftcol
+    colnr_T	w_valid_skipcol;    // last known w_skipcol
 
     /*
      * w_cline_height is the number of physical lines taken by the buffer line
--- 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
-| @21|3|,|1|3|0| @8|B|o|t| 
+|:|s|e|t| |s|c|r|o|l@1|o| @9|3|,|1|3|0| @8|B|o|t| 
--- a/src/testdir/dumps/Test_smooth_long_9.dump
+++ b/src/testdir/dumps/Test_smooth_long_9.dump
@@ -1,6 +1,6 @@
-|<+0#4040ff13#ffffff0@2|o+0#0000000&|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
+|<+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| |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| 
+|:|s|e|t| |s|c|r|o|l@1|o| @9|3|,|1|3|0| @8|B|o|t| 
new file mode 100644
--- /dev/null
+++ b/src/testdir/dumps/Test_smooth_one_long_1.dump
@@ -0,0 +1,6 @@
+>w+0&#ffffff0|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| @34
+|~+0#4040ff13&| @38
+| +0#0000000&@21|1|,|1| @10|A|l@1| 
new file mode 100644
--- /dev/null
+++ b/src/testdir/dumps/Test_smooth_one_long_2.dump
@@ -0,0 +1,6 @@
+|<+0#4040ff13#ffffff0@2|t+0#0000000&|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| @34
+|~+0#4040ff13&| @38
+|~| @38
+| +0#0000000&@21|1|,|8|1| @9|A|l@1| 
--- a/src/testdir/test_scroll_opt.vim
+++ b/src/testdir/test_scroll_opt.vim
@@ -248,5 +248,26 @@ func Test_smoothscroll_wrap_long_line()
   call StopVimInTerminal(buf)
 endfunc
 
+func Test_smoothscroll_one_long_line()
+  CheckScreendump
+
+  let lines =<< trim END
+      vim9script
+      setline(1, 'with lots of text '->repeat(7))
+      set smoothscroll scrolloff=0
+  END
+  call writefile(lines, 'XSmoothOneLong', 'D')
+  let buf = RunVimInTerminal('-S XSmoothOneLong', #{rows: 6, cols: 40})
+  call VerifyScreenDump(buf, 'Test_smooth_one_long_1', {})
+  
+  call term_sendkeys(buf, "\<C-E>")
+  call VerifyScreenDump(buf, 'Test_smooth_one_long_2', {})
+
+  call term_sendkeys(buf, "0")
+  call VerifyScreenDump(buf, 'Test_smooth_one_long_1', {})
+
+  call StopVimInTerminal(buf)
+endfunc
+
 
 " 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 */
 /**/
+    734,
+/**/
     733,
 /**/
     732,