Mercurial > vim
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 */ |