Mercurial > vim
comparison src/move.c @ 31577:3c21865e8068 v9.0.1121
patch 9.0.1121: cursor positioning and display problems with 'smoothscroll'
Commit: https://github.com/vim/vim/commit/db4d88c2adfe8f8122341ac9d6cae27ef78451c8
Author: Bram Moolenaar <Bram@vim.org>
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)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Sat, 31 Dec 2022 16:15:03 +0100 |
parents | 0c0b78aa7ae3 |
children | 238ca27dbfd2 |
comparison
equal
deleted
inserted
replaced
31576:1ae87829a0dc | 31577:3c21865e8068 |
---|---|
217 // account for it. See wlv_screen_line(). | 217 // account for it. See wlv_screen_line(). |
218 if (*get_showbreak_value(curwin) != NUL) | 218 if (*get_showbreak_value(curwin) != NUL) |
219 return 0; | 219 return 0; |
220 #endif | 220 #endif |
221 return extra2 > 3 ? 0 : 3 - extra2; | 221 return extra2 > 3 ? 0 : 3 - extra2; |
222 } | |
223 | |
224 /* | |
225 * Calculates the skipcol offset for window "wp" given how many | |
226 * physical lines we want to scroll down. | |
227 */ | |
228 static int | |
229 skipcol_from_plines(win_T *wp, int plines_off) | |
230 { | |
231 int width1 = wp->w_width - win_col_off(wp); | |
232 | |
233 int skipcol = 0; | |
234 if (plines_off > 0) | |
235 skipcol += width1; | |
236 if (plines_off > 1) | |
237 skipcol += (width1 + win_col_off2(wp)) * (plines_off - 1); | |
238 return skipcol; | |
222 } | 239 } |
223 | 240 |
224 /* | 241 /* |
225 * Set curwin->s_skipcol to zero and redraw later if needed. | 242 * Set curwin->s_skipcol to zero and redraw later if needed. |
226 */ | 243 */ |
2147 * a (wrapped) text line. Uses and sets "lp->fill". | 2164 * a (wrapped) text line. Uses and sets "lp->fill". |
2148 * Returns the height of the added line in "lp->height". | 2165 * Returns the height of the added line in "lp->height". |
2149 * Lines above the first one are incredibly high: MAXCOL. | 2166 * Lines above the first one are incredibly high: MAXCOL. |
2150 */ | 2167 */ |
2151 static void | 2168 static void |
2152 topline_back(lineoff_T *lp) | 2169 topline_back_winheight( |
2170 lineoff_T *lp, | |
2171 int winheight) // when TRUE limit to window height | |
2153 { | 2172 { |
2154 #ifdef FEAT_DIFF | 2173 #ifdef FEAT_DIFF |
2155 if (lp->fill < diff_check_fill(curwin, lp->lnum)) | 2174 if (lp->fill < diff_check_fill(curwin, lp->lnum)) |
2156 { | 2175 { |
2157 // Add a filler line. | 2176 // Add a filler line. |
2172 if (hasFolding(lp->lnum, &lp->lnum, NULL)) | 2191 if (hasFolding(lp->lnum, &lp->lnum, NULL)) |
2173 // Add a closed fold | 2192 // Add a closed fold |
2174 lp->height = 1; | 2193 lp->height = 1; |
2175 else | 2194 else |
2176 #endif | 2195 #endif |
2177 lp->height = PLINES_NOFILL(lp->lnum); | 2196 lp->height = PLINES_WIN_NOFILL(curwin, lp->lnum, winheight); |
2178 } | 2197 } |
2179 } | 2198 } |
2199 | |
2200 static void | |
2201 topline_back(lineoff_T *lp) | |
2202 { | |
2203 topline_back_winheight(lp, TRUE); | |
2204 } | |
2205 | |
2180 | 2206 |
2181 /* | 2207 /* |
2182 * Add one line below "lp->lnum". This can be a filler line, a closed fold or | 2208 * Add one line below "lp->lnum". This can be a filler line, a closed fold or |
2183 * a (wrapped) text line. Uses and sets "lp->fill". | 2209 * a (wrapped) text line. Uses and sets "lp->fill". |
2184 * Returns the height of the added line in "lp->height". | 2210 * Returns the height of the added line in "lp->height". |
2315 // count one logical line for a sequence of folded lines | 2341 // count one logical line for a sequence of folded lines |
2316 i = 1; | 2342 i = 1; |
2317 else | 2343 else |
2318 #endif | 2344 #endif |
2319 i = PLINES_NOFILL(top); | 2345 i = PLINES_NOFILL(top); |
2346 if (top < curwin->w_topline) | |
2347 scrolled += i; | |
2348 | |
2349 // If scrolling is needed, scroll at least 'sj' lines. | |
2350 if ((new_topline >= curwin->w_topline || scrolled > min_scroll) | |
2351 && extra >= off) | |
2352 break; | |
2353 | |
2320 used += i; | 2354 used += i; |
2321 if (extra + i <= off && bot < curbuf->b_ml.ml_line_count) | 2355 if (extra + i <= off && bot < curbuf->b_ml.ml_line_count) |
2322 { | 2356 { |
2323 #ifdef FEAT_FOLDING | 2357 #ifdef FEAT_FOLDING |
2324 if (hasFolding(bot, NULL, &bot)) | 2358 if (hasFolding(bot, NULL, &bot)) |
2327 else | 2361 else |
2328 #endif | 2362 #endif |
2329 used += plines(bot); | 2363 used += plines(bot); |
2330 } | 2364 } |
2331 if (used > curwin->w_height) | 2365 if (used > curwin->w_height) |
2332 break; | |
2333 if (top < curwin->w_topline) | |
2334 scrolled += i; | |
2335 | |
2336 /* | |
2337 * If scrolling is needed, scroll at least 'sj' lines. | |
2338 */ | |
2339 if ((new_topline >= curwin->w_topline || scrolled > min_scroll) | |
2340 && extra >= off) | |
2341 break; | 2366 break; |
2342 | 2367 |
2343 extra += i; | 2368 extra += i; |
2344 new_topline = top; | 2369 new_topline = top; |
2345 --top; | 2370 --top; |
2434 int min_scrolled = 1; | 2459 int min_scrolled = 1; |
2435 int extra = 0; | 2460 int extra = 0; |
2436 int i; | 2461 int i; |
2437 linenr_T line_count; | 2462 linenr_T line_count; |
2438 linenr_T old_topline = curwin->w_topline; | 2463 linenr_T old_topline = curwin->w_topline; |
2464 int old_skipcol = curwin->w_skipcol; | |
2439 lineoff_T loff; | 2465 lineoff_T loff; |
2440 lineoff_T boff; | 2466 lineoff_T boff; |
2441 #ifdef FEAT_DIFF | 2467 #ifdef FEAT_DIFF |
2442 int old_topfill = curwin->w_topfill; | 2468 int old_topfill = curwin->w_topfill; |
2443 int fill_below_window; | 2469 int fill_below_window; |
2449 long so = get_scrolloff_value(); | 2475 long so = get_scrolloff_value(); |
2450 | 2476 |
2451 cln = curwin->w_cursor.lnum; | 2477 cln = curwin->w_cursor.lnum; |
2452 if (set_topbot) | 2478 if (set_topbot) |
2453 { | 2479 { |
2480 int set_skipcol = FALSE; | |
2481 | |
2454 used = 0; | 2482 used = 0; |
2455 curwin->w_botline = cln + 1; | 2483 curwin->w_botline = cln + 1; |
2456 #ifdef FEAT_DIFF | 2484 #ifdef FEAT_DIFF |
2457 loff.fill = 0; | 2485 loff.fill = 0; |
2458 #endif | 2486 #endif |
2459 for (curwin->w_topline = curwin->w_botline; | 2487 for (curwin->w_topline = curwin->w_botline; |
2460 curwin->w_topline > 1; | 2488 curwin->w_topline > 1; |
2461 curwin->w_topline = loff.lnum) | 2489 curwin->w_topline = loff.lnum) |
2462 { | 2490 { |
2463 loff.lnum = curwin->w_topline; | 2491 loff.lnum = curwin->w_topline; |
2464 topline_back(&loff); | 2492 topline_back_winheight(&loff, FALSE); |
2465 if (loff.height == MAXCOL || used + loff.height > curwin->w_height) | 2493 if (loff.height == MAXCOL) |
2466 break; | 2494 break; |
2495 if (used + loff.height > curwin->w_height) | |
2496 { | |
2497 if (curwin->w_p_sms && curwin->w_p_wrap) | |
2498 { | |
2499 // 'smoothscroll' and 'wrap' are set. The above line is | |
2500 // too long to show in its entirety, so we show just a part | |
2501 // of it. | |
2502 if (used < curwin->w_height) | |
2503 { | |
2504 int plines_offset = used + loff.height | |
2505 - curwin->w_height; | |
2506 used = curwin->w_height; | |
2507 #ifdef FEAT_DIFF | |
2508 curwin->w_topfill = loff.fill; | |
2509 #endif | |
2510 curwin->w_topline = loff.lnum; | |
2511 curwin->w_skipcol = skipcol_from_plines( | |
2512 curwin, plines_offset); | |
2513 set_skipcol = TRUE; | |
2514 } | |
2515 } | |
2516 break; | |
2517 } | |
2467 used += loff.height; | 2518 used += loff.height; |
2468 #ifdef FEAT_DIFF | 2519 #ifdef FEAT_DIFF |
2469 curwin->w_topfill = loff.fill; | 2520 curwin->w_topfill = loff.fill; |
2470 #endif | 2521 #endif |
2471 } | 2522 } |
2473 curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP; | 2524 curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP; |
2474 if (curwin->w_topline != old_topline | 2525 if (curwin->w_topline != old_topline |
2475 #ifdef FEAT_DIFF | 2526 #ifdef FEAT_DIFF |
2476 || curwin->w_topfill != old_topfill | 2527 || curwin->w_topfill != old_topfill |
2477 #endif | 2528 #endif |
2478 ) | 2529 || set_skipcol |
2530 || curwin->w_skipcol != 0) | |
2531 { | |
2479 curwin->w_valid &= ~(VALID_WROW|VALID_CROW); | 2532 curwin->w_valid &= ~(VALID_WROW|VALID_CROW); |
2533 if (set_skipcol) | |
2534 redraw_later(UPD_NOT_VALID); | |
2535 else | |
2536 reset_skipcol(); | |
2537 } | |
2480 } | 2538 } |
2481 else | 2539 else |
2482 validate_botline(); | 2540 validate_botline(); |
2483 | 2541 |
2484 // The lines of the cursor line itself are always used. | 2542 // The lines of the cursor line itself are always used. |
2678 /* | 2736 /* |
2679 * If topline didn't change we need to restore w_botline and w_empty_rows | 2737 * If topline didn't change we need to restore w_botline and w_empty_rows |
2680 * (we changed them). | 2738 * (we changed them). |
2681 * If topline did change, update_screen() will set botline. | 2739 * If topline did change, update_screen() will set botline. |
2682 */ | 2740 */ |
2683 if (curwin->w_topline == old_topline && set_topbot) | 2741 if (curwin->w_topline == old_topline |
2742 && curwin->w_skipcol == old_skipcol | |
2743 && set_topbot) | |
2684 { | 2744 { |
2685 curwin->w_botline = old_botline; | 2745 curwin->w_botline = old_botline; |
2686 curwin->w_empty_rows = old_empty_rows; | 2746 curwin->w_empty_rows = old_empty_rows; |
2687 curwin->w_valid = old_valid; | 2747 curwin->w_valid = old_valid; |
2688 } | 2748 } |
2696 void | 2756 void |
2697 scroll_cursor_halfway(int atend) | 2757 scroll_cursor_halfway(int atend) |
2698 { | 2758 { |
2699 int above = 0; | 2759 int above = 0; |
2700 linenr_T topline; | 2760 linenr_T topline; |
2761 colnr_T skipcol = 0; | |
2762 int set_skipcol = FALSE; | |
2701 #ifdef FEAT_DIFF | 2763 #ifdef FEAT_DIFF |
2702 int topfill = 0; | 2764 int topfill = 0; |
2703 #endif | 2765 #endif |
2704 int below = 0; | 2766 int below = 0; |
2705 int used; | 2767 int used; |
2723 boff.fill = 0; | 2785 boff.fill = 0; |
2724 #else | 2786 #else |
2725 used = plines(loff.lnum); | 2787 used = plines(loff.lnum); |
2726 #endif | 2788 #endif |
2727 topline = loff.lnum; | 2789 topline = loff.lnum; |
2790 | |
2791 int half_height = 0; | |
2792 int smooth_scroll = FALSE; | |
2793 if (curwin->w_p_sms && curwin->w_p_wrap) | |
2794 { | |
2795 // 'smoothscroll' and 'wrap' are set | |
2796 smooth_scroll = TRUE; | |
2797 half_height = (curwin->w_height - used) / 2; | |
2798 used = 0; | |
2799 } | |
2800 | |
2728 while (topline > 1) | 2801 while (topline > 1) |
2729 { | 2802 { |
2803 // If using smoothscroll, we can precisely scroll to the | |
2804 // exact point where the cursor is halfway down the screen. | |
2805 if (smooth_scroll) | |
2806 { | |
2807 topline_back_winheight(&loff, FALSE); | |
2808 if (loff.height == MAXCOL) | |
2809 break; | |
2810 else | |
2811 used += loff.height; | |
2812 if (used > half_height) | |
2813 { | |
2814 if (used - loff.height < half_height) | |
2815 { | |
2816 int plines_offset = used - half_height; | |
2817 loff.height -= plines_offset; | |
2818 used = half_height; | |
2819 | |
2820 topline = loff.lnum; | |
2821 #ifdef FEAT_DIFF | |
2822 topfill = loff.fill; | |
2823 #endif | |
2824 skipcol = skipcol_from_plines(curwin, plines_offset); | |
2825 set_skipcol = TRUE; | |
2826 } | |
2827 break; | |
2828 } | |
2829 topline = loff.lnum; | |
2830 #ifdef FEAT_DIFF | |
2831 topfill = loff.fill; | |
2832 #endif | |
2833 continue; | |
2834 } | |
2835 | |
2836 // If not using smoothscroll, we have to iteratively find how many | |
2837 // lines to scroll down to roughly fit the cursor. | |
2838 // This may not be right in the middle if the lines' physical height > | |
2839 // 1 (e.g. 'wrap' is on). | |
2840 | |
2730 if (below <= above) // add a line below the cursor first | 2841 if (below <= above) // add a line below the cursor first |
2731 { | 2842 { |
2732 if (boff.lnum < curbuf->b_ml.ml_line_count) | 2843 if (boff.lnum < curbuf->b_ml.ml_line_count) |
2733 { | 2844 { |
2734 botline_forw(&boff); | 2845 botline_forw(&boff); |
2762 } | 2873 } |
2763 } | 2874 } |
2764 #ifdef FEAT_FOLDING | 2875 #ifdef FEAT_FOLDING |
2765 if (!hasFolding(topline, &curwin->w_topline, NULL)) | 2876 if (!hasFolding(topline, &curwin->w_topline, NULL)) |
2766 #endif | 2877 #endif |
2767 curwin->w_topline = topline; | 2878 { |
2879 if (curwin->w_topline != topline | |
2880 || set_skipcol | |
2881 || curwin->w_skipcol != 0) | |
2882 { | |
2883 curwin->w_topline = topline; | |
2884 if (set_skipcol) | |
2885 { | |
2886 curwin->w_skipcol = skipcol; | |
2887 redraw_later(UPD_NOT_VALID); | |
2888 } | |
2889 else | |
2890 reset_skipcol(); | |
2891 } | |
2892 } | |
2768 #ifdef FEAT_DIFF | 2893 #ifdef FEAT_DIFF |
2769 curwin->w_topfill = topfill; | 2894 curwin->w_topfill = topfill; |
2770 if (old_topline > curwin->w_topline + curwin->w_height) | 2895 if (old_topline > curwin->w_topline + curwin->w_height) |
2771 curwin->w_botfill = FALSE; | 2896 curwin->w_botfill = FALSE; |
2772 check_topfill(curwin, FALSE); | 2897 check_topfill(curwin, FALSE); |