# HG changeset patch # User Bram Moolenaar # Date 1672499703 -3600 # Node ID 3c21865e80689ac307f5fad99b6dc41034af2c21 # Parent 1ae87829a0dc72a22788676c5dcea13a5926336f patch 9.0.1121: cursor positioning and display problems with 'smoothscroll' Commit: https://github.com/vim/vim/commit/db4d88c2adfe8f8122341ac9d6cae27ef78451c8 Author: Bram Moolenaar Date: Sat Dec 31 15:13:22 2022 +0000 patch 9.0.1121: cursor positioning and display problems with 'smoothscroll' Problem: Cursor positioning and display problems with 'smoothscroll' and using "zt", "zb" or "zz". Solution: Adjust computations and conditions. (Yee Cheng Chin, closes #11764) diff --git a/src/macros.h b/src/macros.h --- a/src/macros.h +++ b/src/macros.h @@ -253,8 +253,10 @@ #ifdef FEAT_DIFF # define PLINES_NOFILL(x) plines_nofill(x) +# define PLINES_WIN_NOFILL(w, l, h) plines_win_nofill((w), (l), (h)) #else # define PLINES_NOFILL(x) plines(x) +# define PLINES_WIN_NOFILL(w, l, h) plines_win((w), (l), (h)) #endif #if defined(FEAT_JOB_CHANNEL) || defined(FEAT_CLIENTSERVER) diff --git a/src/move.c b/src/move.c --- a/src/move.c +++ b/src/move.c @@ -222,6 +222,23 @@ smoothscroll_marker_overlap(int extra2) } /* + * Calculates the skipcol offset for window "wp" given how many + * physical lines we want to scroll down. + */ + static int +skipcol_from_plines(win_T *wp, int plines_off) +{ + int width1 = wp->w_width - win_col_off(wp); + + int skipcol = 0; + if (plines_off > 0) + skipcol += width1; + if (plines_off > 1) + skipcol += (width1 + win_col_off2(wp)) * (plines_off - 1); + return skipcol; +} + +/* * Set curwin->s_skipcol to zero and redraw later if needed. */ static void @@ -2149,7 +2166,9 @@ scrollup_clamp(void) * Lines above the first one are incredibly high: MAXCOL. */ static void -topline_back(lineoff_T *lp) +topline_back_winheight( + lineoff_T *lp, + int winheight) // when TRUE limit to window height { #ifdef FEAT_DIFF if (lp->fill < diff_check_fill(curwin, lp->lnum)) @@ -2174,10 +2193,17 @@ topline_back(lineoff_T *lp) lp->height = 1; else #endif - lp->height = PLINES_NOFILL(lp->lnum); + lp->height = PLINES_WIN_NOFILL(curwin, lp->lnum, winheight); } } + static void +topline_back(lineoff_T *lp) +{ + topline_back_winheight(lp, TRUE); +} + + /* * Add one line below "lp->lnum". This can be a filler line, a closed fold or * a (wrapped) text line. Uses and sets "lp->fill". @@ -2317,6 +2343,14 @@ scroll_cursor_top(int min_scroll, int al else #endif i = PLINES_NOFILL(top); + if (top < curwin->w_topline) + scrolled += i; + + // If scrolling is needed, scroll at least 'sj' lines. + if ((new_topline >= curwin->w_topline || scrolled > min_scroll) + && extra >= off) + break; + used += i; if (extra + i <= off && bot < curbuf->b_ml.ml_line_count) { @@ -2330,15 +2364,6 @@ scroll_cursor_top(int min_scroll, int al } if (used > curwin->w_height) break; - if (top < curwin->w_topline) - scrolled += i; - - /* - * If scrolling is needed, scroll at least 'sj' lines. - */ - if ((new_topline >= curwin->w_topline || scrolled > min_scroll) - && extra >= off) - break; extra += i; new_topline = top; @@ -2436,6 +2461,7 @@ scroll_cursor_bot(int min_scroll, int se int i; linenr_T line_count; linenr_T old_topline = curwin->w_topline; + int old_skipcol = curwin->w_skipcol; lineoff_T loff; lineoff_T boff; #ifdef FEAT_DIFF @@ -2451,6 +2477,8 @@ scroll_cursor_bot(int min_scroll, int se cln = curwin->w_cursor.lnum; if (set_topbot) { + int set_skipcol = FALSE; + used = 0; curwin->w_botline = cln + 1; #ifdef FEAT_DIFF @@ -2461,9 +2489,32 @@ scroll_cursor_bot(int min_scroll, int se curwin->w_topline = loff.lnum) { loff.lnum = curwin->w_topline; - topline_back(&loff); - if (loff.height == MAXCOL || used + loff.height > curwin->w_height) + topline_back_winheight(&loff, FALSE); + if (loff.height == MAXCOL) break; + if (used + loff.height > curwin->w_height) + { + if (curwin->w_p_sms && curwin->w_p_wrap) + { + // 'smoothscroll' and 'wrap' are set. The above line is + // too long to show in its entirety, so we show just a part + // of it. + if (used < curwin->w_height) + { + int plines_offset = used + loff.height + - curwin->w_height; + used = curwin->w_height; +#ifdef FEAT_DIFF + curwin->w_topfill = loff.fill; +#endif + curwin->w_topline = loff.lnum; + curwin->w_skipcol = skipcol_from_plines( + curwin, plines_offset); + set_skipcol = TRUE; + } + } + break; + } used += loff.height; #ifdef FEAT_DIFF curwin->w_topfill = loff.fill; @@ -2475,8 +2526,15 @@ scroll_cursor_bot(int min_scroll, int se #ifdef FEAT_DIFF || curwin->w_topfill != old_topfill #endif - ) + || set_skipcol + || curwin->w_skipcol != 0) + { curwin->w_valid &= ~(VALID_WROW|VALID_CROW); + if (set_skipcol) + redraw_later(UPD_NOT_VALID); + else + reset_skipcol(); + } } else validate_botline(); @@ -2680,7 +2738,9 @@ scroll_cursor_bot(int min_scroll, int se * (we changed them). * If topline did change, update_screen() will set botline. */ - if (curwin->w_topline == old_topline && set_topbot) + if (curwin->w_topline == old_topline + && curwin->w_skipcol == old_skipcol + && set_topbot) { curwin->w_botline = old_botline; curwin->w_empty_rows = old_empty_rows; @@ -2698,6 +2758,8 @@ scroll_cursor_halfway(int atend) { int above = 0; linenr_T topline; + colnr_T skipcol = 0; + int set_skipcol = FALSE; #ifdef FEAT_DIFF int topfill = 0; #endif @@ -2725,8 +2787,57 @@ scroll_cursor_halfway(int atend) used = plines(loff.lnum); #endif topline = loff.lnum; + + int half_height = 0; + int smooth_scroll = FALSE; + if (curwin->w_p_sms && curwin->w_p_wrap) + { + // 'smoothscroll' and 'wrap' are set + smooth_scroll = TRUE; + half_height = (curwin->w_height - used) / 2; + used = 0; + } + while (topline > 1) { + // If using smoothscroll, we can precisely scroll to the + // exact point where the cursor is halfway down the screen. + if (smooth_scroll) + { + topline_back_winheight(&loff, FALSE); + if (loff.height == MAXCOL) + break; + else + used += loff.height; + if (used > half_height) + { + if (used - loff.height < half_height) + { + int plines_offset = used - half_height; + loff.height -= plines_offset; + used = half_height; + + topline = loff.lnum; +#ifdef FEAT_DIFF + topfill = loff.fill; +#endif + skipcol = skipcol_from_plines(curwin, plines_offset); + set_skipcol = TRUE; + } + break; + } + topline = loff.lnum; +#ifdef FEAT_DIFF + topfill = loff.fill; +#endif + continue; + } + + // 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). + if (below <= above) // add a line below the cursor first { if (boff.lnum < curbuf->b_ml.ml_line_count) @@ -2764,7 +2875,21 @@ scroll_cursor_halfway(int atend) #ifdef FEAT_FOLDING if (!hasFolding(topline, &curwin->w_topline, NULL)) #endif - curwin->w_topline = topline; + { + if (curwin->w_topline != topline + || set_skipcol + || curwin->w_skipcol != 0) + { + curwin->w_topline = topline; + if (set_skipcol) + { + curwin->w_skipcol = skipcol; + redraw_later(UPD_NOT_VALID); + } + else + reset_skipcol(); + } + } #ifdef FEAT_DIFF curwin->w_topfill = topfill; if (old_topline > curwin->w_topline + curwin->w_height) diff --git a/src/testdir/dumps/Test_smooth_long_10.dump b/src/testdir/dumps/Test_smooth_long_10.dump --- a/src/testdir/dumps/Test_smooth_long_10.dump +++ b/src/testdir/dumps/Test_smooth_long_10.dump @@ -1,6 +1,6 @@ ->f+0&#ffffff0|o|u|r| @35 +|<+0#4040ff13#ffffff0@2|o+0#0000000&|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| |e|n|d| @11 +>f|o|u|r| @35 |~+0#4040ff13&| @38 |~| @38 -|~| @38 -|~| @38 |:+0#0000000&|s|e|t| |s|c|r|o|l@1|o| @9|4|,|1| @10|B|o|t| diff --git a/src/testdir/dumps/Test_smooth_long_11.dump b/src/testdir/dumps/Test_smooth_long_11.dump --- a/src/testdir/dumps/Test_smooth_long_11.dump +++ b/src/testdir/dumps/Test_smooth_long_11.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| |e|n|d| @11 >f|o|u|r| @35 -@22|4|,|1| @10|B|o|t| +|:|s|e|t| |s|c|r|o|l@1|o| @9|4|,|1| @10|B|o|t| diff --git a/src/testdir/dumps/Test_smooth_long_12.dump b/src/testdir/dumps/Test_smooth_long_12.dump --- a/src/testdir/dumps/Test_smooth_long_12.dump +++ b/src/testdir/dumps/Test_smooth_long_12.dump @@ -1,6 +1,6 @@ -|<+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 +|<+0#4040ff13#ffffff0@2|o+0#0000000&|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| |e|n|d| @11 -|f|o|u>r| @35 -@22|4|,|4| @10|B|o|t| +>f|o|u|r| @35 +|~+0#4040ff13&| @38 +|~| @38 +|:+0#0000000&|s|e|t| |s|c|r|o|l@1|o| @9|4|,|1| @10|B|o|t| diff --git a/src/testdir/dumps/Test_smooth_long_13.dump b/src/testdir/dumps/Test_smooth_long_13.dump new file mode 100644 --- /dev/null +++ b/src/testdir/dumps/Test_smooth_long_13.dump @@ -0,0 +1,6 @@ +>f+0&#ffffff0|o|u|r| @35 +|~+0#4040ff13&| @38 +|~| @38 +|~| @38 +|~| @38 +| +0#0000000&@21|4|,|1| @10|B|o|t| diff --git a/src/testdir/dumps/Test_smooth_long_14.dump b/src/testdir/dumps/Test_smooth_long_14.dump new file mode 100644 --- /dev/null +++ b/src/testdir/dumps/Test_smooth_long_14.dump @@ -0,0 +1,6 @@ +|<+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| |e|n|d| @11 +>f|o|u|r| @35 +@22|4|,|1| @10|B|o|t| diff --git a/src/testdir/dumps/Test_smooth_long_15.dump b/src/testdir/dumps/Test_smooth_long_15.dump new file mode 100644 --- /dev/null +++ b/src/testdir/dumps/Test_smooth_long_15.dump @@ -0,0 +1,6 @@ +|<+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| |e|n|d| @11 +|f|o|u>r| @35 +@22|4|,|4| @10|B|o|t| diff --git a/src/testdir/dumps/Test_smooth_long_8.dump b/src/testdir/dumps/Test_smooth_long_8.dump --- 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 -|:|s|e|t| |s|c|r|o|l@1|o| @9|3|,|1|3|0| @8|6@1|%| +| @21|3|,|1|3|0| @8|6@1|%| diff --git a/src/testdir/dumps/Test_smooth_long_9.dump b/src/testdir/dumps/Test_smooth_long_9.dump --- a/src/testdir/dumps/Test_smooth_long_9.dump +++ b/src/testdir/dumps/Test_smooth_long_9.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 -|:|s|e|t| |s|c|r|o|l@1|o| @9|3|,|1|3|0| @8|6@1|%| +| @21|3|,|1|3|0| @8|6@1|%| diff --git a/src/testdir/test_scroll_opt.vim b/src/testdir/test_scroll_opt.vim --- a/src/testdir/test_scroll_opt.vim +++ b/src/testdir/test_scroll_opt.vim @@ -296,6 +296,14 @@ func Test_smoothscroll_wrap_long_line() call term_sendkeys(buf, "0j") call VerifyScreenDump(buf, 'Test_smooth_long_10', {}) + " Test zt/zz/zb that they work properly when a long line is above it + call term_sendkeys(buf, "zb") + call VerifyScreenDump(buf, 'Test_smooth_long_11', {}) + call term_sendkeys(buf, "zz") + call VerifyScreenDump(buf, 'Test_smooth_long_12', {}) + call term_sendkeys(buf, "zt") + call VerifyScreenDump(buf, 'Test_smooth_long_13', {}) + " Repeat the step and move the cursor down again. " This time, use a shorter long line that is barely long enough to span more " than one window. Note that the cursor is at the bottom this time because @@ -303,7 +311,7 @@ func Test_smoothscroll_wrap_long_line() call term_sendkeys(buf, ":call setline(1, ['one', 'two', 'Line' .. (' with lots of text'->repeat(10)) .. ' end', 'four'])\") call term_sendkeys(buf, "3Gzt") call term_sendkeys(buf, "j") - call VerifyScreenDump(buf, 'Test_smooth_long_11', {}) + call VerifyScreenDump(buf, 'Test_smooth_long_14', {}) " Repeat the step but this time start it when the line is smooth-scrolled by " one line. This tests that the offset calculation is still correct and @@ -311,7 +319,7 @@ func Test_smoothscroll_wrap_long_line() " screen. call term_sendkeys(buf, "3Gzt") call term_sendkeys(buf, "\j") - call VerifyScreenDump(buf, 'Test_smooth_long_12', {}) + call VerifyScreenDump(buf, 'Test_smooth_long_15', {}) call StopVimInTerminal(buf) endfunc diff --git a/src/version.c b/src/version.c --- 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 */ /**/ + 1121, +/**/ 1120, /**/ 1119,