comparison src/move.c @ 34653:8079960136db v9.1.0211

patch 9.1.0211: page-wise scrolling does not support smooth-scrolling Commit: https://github.com/vim/vim/commit/b9f5b95b7bec2414a5a96010514702d99afea18e Author: Luuk van Baal <luukvbaal@gmail.com> Date: Tue Mar 26 18:46:45 2024 +0100 patch 9.1.0211: page-wise scrolling does not support smooth-scrolling Problem: Page-wise scrolling with Ctrl-F/Ctrl-B implements it's own logic to change the topline and cursor. In doing so, skipcol is not handled properly for 'smoothscroll', and virtual lines. Solution: Re-use the logic from Ctrl-E/Ctrl-Y while staying backward compatible as much as possible. closes: #14268 Signed-off-by: Luuk van Baal <luukvbaal@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
author Christian Brabandt <cb@256bit.org>
date Tue, 26 Mar 2024 19:00:04 +0100
parents 7f84a834055a
children ca2da8e8fb53
comparison
equal deleted inserted replaced
34652:86aff5d0f82a 34653:8079960136db
2045 if (wp->w_topfill < 0) 2045 if (wp->w_topfill < 0)
2046 wp->w_topfill = 0; 2046 wp->w_topfill = 0;
2047 } 2047 }
2048 } 2048 }
2049 } 2049 }
2050
2051 /*
2052 * Use as many filler lines as possible for w_topline. Make sure w_topline
2053 * is still visible.
2054 */
2055 static void
2056 max_topfill(void)
2057 {
2058 int n;
2059
2060 n = plines_nofill(curwin->w_topline);
2061 if (n >= curwin->w_height)
2062 curwin->w_topfill = 0;
2063 else
2064 {
2065 curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline);
2066 if (curwin->w_topfill + n > curwin->w_height)
2067 curwin->w_topfill = curwin->w_height - n;
2068 }
2069 }
2070 #endif 2050 #endif
2071 2051
2072 /* 2052 /*
2073 * Scroll the screen one line down, but don't do it if it would move the 2053 * Scroll the screen one line down, but don't do it if it would move the
2074 * cursor off the screen. 2054 * cursor off the screen.
2266 else 2246 else
2267 #endif 2247 #endif
2268 lp->height = PLINES_NOFILL(lp->lnum); 2248 lp->height = PLINES_NOFILL(lp->lnum);
2269 } 2249 }
2270 } 2250 }
2271
2272 #ifdef FEAT_DIFF
2273 /*
2274 * Switch from including filler lines below lp->lnum to including filler
2275 * lines above loff.lnum + 1. This keeps pointing to the same line.
2276 * When there are no filler lines nothing changes.
2277 */
2278 static void
2279 botline_topline(lineoff_T *lp)
2280 {
2281 if (lp->fill > 0)
2282 {
2283 ++lp->lnum;
2284 lp->fill = diff_check_fill(curwin, lp->lnum) - lp->fill + 1;
2285 }
2286 }
2287
2288 /*
2289 * Switch from including filler lines above lp->lnum to including filler
2290 * lines below loff.lnum - 1. This keeps pointing to the same line.
2291 * When there are no filler lines nothing changes.
2292 */
2293 static void
2294 topline_botline(lineoff_T *lp)
2295 {
2296 if (lp->fill > 0)
2297 {
2298 lp->fill = diff_check_fill(curwin, lp->lnum) - lp->fill + 1;
2299 --lp->lnum;
2300 }
2301 }
2302 #endif
2303 2251
2304 /* 2252 /*
2305 * Recompute topline to put the cursor at the top of the window. 2253 * Recompute topline to put the cursor at the top of the window.
2306 * Scroll at least "min_scroll" lines. 2254 * Scroll at least "min_scroll" lines.
2307 * If "always" is TRUE, always set topline (for "zt"). 2255 * If "always" is TRUE, always set topline (for "zt").
3075 } 3023 }
3076 } 3024 }
3077 curwin->w_valid |= VALID_TOPLINE; 3025 curwin->w_valid |= VALID_TOPLINE;
3078 } 3026 }
3079 3027
3080 static void get_scroll_overlap(lineoff_T *lp, int dir);
3081
3082 /* 3028 /*
3083 * Move screen "count" pages up ("dir" is BACKWARD) or down ("dir" is FORWARD) 3029 * Move screen "count" pages up ("dir" is BACKWARD) or down ("dir" is FORWARD)
3084 * and update the screen. 3030 * and update the screen.
3085 * 3031 *
3086 * Return FAIL for failure, OK otherwise. 3032 * Return FAIL for failure, OK otherwise.
3087 */ 3033 */
3088 int 3034 int
3089 onepage(int dir, long count) 3035 onepage(int dir, long count)
3090 { 3036 {
3091 long n; 3037 #ifdef FEAT_DIFF
3092 int retval = OK; 3038 int prev_topfill = curwin->w_topfill;
3093 lineoff_T loff; 3039 #endif
3094 linenr_T old_topline = curwin->w_topline; 3040 linenr_T prev_topline = curwin->w_topline;
3095 long so = get_scrolloff_value(); 3041 colnr_T prev_skipcol = curwin->w_skipcol;
3096 3042
3097 if (curbuf->b_ml.ml_line_count == 1) // nothing to do 3043 // Scroll 'window' or current window height lines.
3098 { 3044 count *= ((ONE_WINDOW && p_window > 0 && p_window < Rows - 1) ?
3045 p_window : curwin->w_height) - 2;
3046
3047 if (curwin->w_p_sms)
3048 scroll_redraw(dir == FORWARD, count);
3049 else
3050 {
3051 // Scroll at least one full line without 'smoothscroll'.
3052 #ifdef FEAT_DIFF
3053 count -= plines_nofill(curwin->w_topline);
3054 #else
3055 count -= plines(curwin->w_topline);
3056 #endif
3057 scroll_redraw(dir == FORWARD, 1);
3058
3059 // Temporarily set 'smoothscroll' so that scrolling count lines
3060 // does not skip over parts of the buffer with wrapped lines.
3061 curwin->w_p_sms = TRUE;
3062 if (count > 0)
3063 scroll_redraw(dir == FORWARD, count);
3064 curwin->w_p_sms = FALSE;
3065 }
3066
3067 int nochange = curwin->w_topline == prev_topline
3068 #ifdef FEAT_DIFF
3069 && curwin->w_topfill == prev_topfill
3070 #endif
3071 && curwin->w_skipcol == prev_skipcol;
3072
3073 if (nochange)
3099 beep_flush(); 3074 beep_flush();
3100 return FAIL; 3075 else if (!curwin->w_p_sms || curwin->w_skipcol == prev_skipcol)
3101 }
3102
3103 for ( ; count > 0; --count)
3104 {
3105 validate_botline();
3106 /*
3107 * It's an error to move a page up when the first line is already on
3108 * the screen. It's an error to move a page down when the last line
3109 * is on the screen and the topline is 'scrolloff' lines from the
3110 * last line.
3111 */
3112 if (dir == FORWARD
3113 ? ((curwin->w_topline >= curbuf->b_ml.ml_line_count - so)
3114 && curwin->w_botline > curbuf->b_ml.ml_line_count)
3115 : (curwin->w_topline == 1
3116 #ifdef FEAT_DIFF
3117 && curwin->w_topfill ==
3118 diff_check_fill(curwin, curwin->w_topline)
3119 #endif
3120 ))
3121 {
3122 beep_flush();
3123 retval = FAIL;
3124 break;
3125 }
3126
3127 #ifdef FEAT_DIFF
3128 loff.fill = 0;
3129 #endif
3130 if (dir == FORWARD)
3131 {
3132 if (ONE_WINDOW && p_window > 0 && p_window < Rows - 1)
3133 {
3134 // Vi compatible scrolling
3135 if (p_window <= 2)
3136 ++curwin->w_topline;
3137 else
3138 curwin->w_topline += p_window - 2;
3139 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
3140 curwin->w_topline = curbuf->b_ml.ml_line_count;
3141 curwin->w_cursor.lnum = curwin->w_topline;
3142 }
3143 else if (curwin->w_botline > curbuf->b_ml.ml_line_count)
3144 {
3145 // at end of file
3146 curwin->w_topline = curbuf->b_ml.ml_line_count;
3147 #ifdef FEAT_DIFF
3148 curwin->w_topfill = 0;
3149 #endif
3150 curwin->w_valid &= ~(VALID_WROW|VALID_CROW);
3151 }
3152 else
3153 {
3154 // For the overlap, start with the line just below the window
3155 // and go upwards.
3156 loff.lnum = curwin->w_botline;
3157 #ifdef FEAT_DIFF
3158 loff.fill = diff_check_fill(curwin, loff.lnum)
3159 - curwin->w_filler_rows;
3160 #endif
3161 get_scroll_overlap(&loff, -1);
3162 curwin->w_topline = loff.lnum;
3163 #ifdef FEAT_DIFF
3164 curwin->w_topfill = loff.fill;
3165 check_topfill(curwin, FALSE);
3166 #endif
3167 curwin->w_cursor.lnum = curwin->w_topline;
3168 curwin->w_valid &= ~(VALID_WCOL|VALID_CHEIGHT|VALID_WROW|
3169 VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
3170 }
3171 }
3172 else // dir == BACKWARDS
3173 {
3174 #ifdef FEAT_DIFF
3175 if (curwin->w_topline == 1)
3176 {
3177 // Include max number of filler lines
3178 max_topfill();
3179 continue;
3180 }
3181 #endif
3182 if (ONE_WINDOW && p_window > 0 && p_window < Rows - 1)
3183 {
3184 // Vi compatible scrolling (sort of)
3185 if (p_window <= 2)
3186 --curwin->w_topline;
3187 else
3188 curwin->w_topline -= p_window - 2;
3189 if (curwin->w_topline < 1)
3190 curwin->w_topline = 1;
3191 curwin->w_cursor.lnum = curwin->w_topline + p_window - 1;
3192 if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
3193 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
3194 continue;
3195 }
3196
3197 // Find the line at the top of the window that is going to be the
3198 // line at the bottom of the window. Make sure this results in
3199 // the same line as before doing CTRL-F.
3200 loff.lnum = curwin->w_topline - 1;
3201 #ifdef FEAT_DIFF
3202 loff.fill = diff_check_fill(curwin, loff.lnum + 1)
3203 - curwin->w_topfill;
3204 #endif
3205 get_scroll_overlap(&loff, 1);
3206
3207 if (loff.lnum >= curbuf->b_ml.ml_line_count)
3208 {
3209 loff.lnum = curbuf->b_ml.ml_line_count;
3210 #ifdef FEAT_DIFF
3211 loff.fill = 0;
3212 }
3213 else
3214 {
3215 botline_topline(&loff);
3216 #endif
3217 }
3218 curwin->w_cursor.lnum = loff.lnum;
3219
3220 // Find the line just above the new topline to get the right line
3221 // at the bottom of the window.
3222 n = 0;
3223 while (n <= curwin->w_height && loff.lnum >= 1)
3224 {
3225 topline_back(&loff);
3226 if (loff.height == MAXCOL)
3227 n = MAXCOL;
3228 else
3229 n += loff.height;
3230 }
3231 if (loff.lnum < 1) // at begin of file
3232 {
3233 curwin->w_topline = 1;
3234 #ifdef FEAT_DIFF
3235 max_topfill();
3236 #endif
3237 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
3238 }
3239 else
3240 {
3241 // Go two lines forward again.
3242 #ifdef FEAT_DIFF
3243 topline_botline(&loff);
3244 #endif
3245 botline_forw(&loff);
3246 botline_forw(&loff);
3247 #ifdef FEAT_DIFF
3248 botline_topline(&loff);
3249 #endif
3250 #ifdef FEAT_FOLDING
3251 // We're at the wrong end of a fold now.
3252 (void)hasFolding(loff.lnum, &loff.lnum, NULL);
3253 #endif
3254
3255 // Always scroll at least one line. Avoid getting stuck on
3256 // very long lines.
3257 if (loff.lnum >= curwin->w_topline
3258 #ifdef FEAT_DIFF
3259 && (loff.lnum > curwin->w_topline
3260 || loff.fill >= curwin->w_topfill)
3261 #endif
3262 )
3263 {
3264 #ifdef FEAT_DIFF
3265 // First try using the maximum number of filler lines. If
3266 // that's not enough, backup one line.
3267 loff.fill = curwin->w_topfill;
3268 if (curwin->w_topfill < diff_check_fill(curwin,
3269 curwin->w_topline))
3270 max_topfill();
3271 if (curwin->w_topfill == loff.fill)
3272 #endif
3273 {
3274 --curwin->w_topline;
3275 #ifdef FEAT_DIFF
3276 curwin->w_topfill = 0;
3277 #endif
3278 curwin->w_valid &= ~(VALID_WROW|VALID_CROW);
3279 }
3280 comp_botline(curwin);
3281 curwin->w_cursor.lnum = curwin->w_botline - 1;
3282 curwin->w_valid &=
3283 ~(VALID_WCOL|VALID_CHEIGHT|VALID_WROW|VALID_CROW);
3284 }
3285 else
3286 {
3287 curwin->w_topline = loff.lnum;
3288 #ifdef FEAT_DIFF
3289 curwin->w_topfill = loff.fill;
3290 check_topfill(curwin, FALSE);
3291 #endif
3292 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
3293 }
3294 }
3295 }
3296 }
3297 #ifdef FEAT_FOLDING
3298 foldAdjustCursor();
3299 #endif
3300 cursor_correct();
3301 check_cursor_col();
3302 if (retval == OK)
3303 beginline(BL_SOL | BL_FIX); 3076 beginline(BL_SOL | BL_FIX);
3304 curwin->w_valid &= ~(VALID_WCOL|VALID_WROW|VALID_VIRTCOL); 3077
3305 3078 return nochange;
3306 if (retval == OK && dir == FORWARD)
3307 {
3308 // Avoid the screen jumping up and down when 'scrolloff' is non-zero.
3309 // But make sure we scroll at least one line (happens with mix of long
3310 // wrapping lines and non-wrapping line).
3311 if (check_top_offset())
3312 {
3313 scroll_cursor_top(1, FALSE);
3314 if (curwin->w_topline <= old_topline
3315 && old_topline < curbuf->b_ml.ml_line_count)
3316 {
3317 curwin->w_topline = old_topline + 1;
3318 #ifdef FEAT_FOLDING
3319 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
3320 #endif
3321 }
3322 }
3323 #ifdef FEAT_FOLDING
3324 else if (curwin->w_botline > curbuf->b_ml.ml_line_count)
3325 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
3326 #endif
3327 }
3328
3329 redraw_later(UPD_VALID);
3330 return retval;
3331 }
3332
3333 /*
3334 * Decide how much overlap to use for page-up or page-down scrolling.
3335 * This is symmetric, so that doing both keeps the same lines displayed.
3336 * Three lines are examined:
3337 *
3338 * before CTRL-F after CTRL-F / before CTRL-B
3339 * etc. l1
3340 * l1 last but one line ------------
3341 * l2 last text line l2 top text line
3342 * ------------- l3 second text line
3343 * l3 etc.
3344 */
3345 static void
3346 get_scroll_overlap(lineoff_T *lp, int dir)
3347 {
3348 int h1, h2, h3, h4;
3349 int min_height = curwin->w_height - 2;
3350 lineoff_T loff0, loff1, loff2;
3351
3352 #ifdef FEAT_DIFF
3353 if (lp->fill > 0)
3354 lp->height = 1;
3355 else
3356 lp->height = plines_nofill(lp->lnum);
3357 #else
3358 lp->height = plines(lp->lnum);
3359 #endif
3360 h1 = lp->height;
3361 if (h1 > min_height)
3362 return; // no overlap
3363
3364 loff0 = *lp;
3365 if (dir > 0)
3366 botline_forw(lp);
3367 else
3368 topline_back(lp);
3369 h2 = lp->height;
3370 if (h2 == MAXCOL || h2 + h1 > min_height)
3371 {
3372 *lp = loff0; // no overlap
3373 return;
3374 }
3375
3376 loff1 = *lp;
3377 if (dir > 0)
3378 botline_forw(lp);
3379 else
3380 topline_back(lp);
3381 h3 = lp->height;
3382 if (h3 == MAXCOL || h3 + h2 > min_height)
3383 {
3384 *lp = loff0; // no overlap
3385 return;
3386 }
3387
3388 loff2 = *lp;
3389 if (dir > 0)
3390 botline_forw(lp);
3391 else
3392 topline_back(lp);
3393 h4 = lp->height;
3394 if (h4 == MAXCOL || h4 + h3 + h2 > min_height || h3 + h2 + h1 > min_height)
3395 *lp = loff1; // 1 line overlap
3396 else
3397 *lp = loff2; // 2 lines overlap
3398 } 3079 }
3399 3080
3400 /* 3081 /*
3401 * Scroll 'scroll' lines up or down. 3082 * Scroll 'scroll' lines up or down.
3402 */ 3083 */