blob: d969de2fc030ed9a2dae225d368aa09f28c9dcf6 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9/*
10 * move.c: Functions for moving the cursor and scrolling text.
11 *
12 * There are two ways to move the cursor:
13 * 1. Move the cursor directly, the text is scrolled to keep the cursor in the
14 * window.
15 * 2. Scroll the text, the cursor is moved into the text visible in the
16 * window.
17 * The 'scrolloff' option makes this a bit complicated.
18 */
19
20#include "vim.h"
21
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010022static int scrolljump_value(void);
23static int check_top_offset(void);
24static void curs_rows(win_T *wp);
Bram Moolenaar071d4272004-06-13 20:20:40 +000025
26typedef struct
27{
Bram Moolenaar85a20022019-12-21 18:25:54 +010028 linenr_T lnum; // line number
Bram Moolenaar071d4272004-06-13 20:20:40 +000029#ifdef FEAT_DIFF
Bram Moolenaar85a20022019-12-21 18:25:54 +010030 int fill; // filler lines
Bram Moolenaar071d4272004-06-13 20:20:40 +000031#endif
Bram Moolenaar85a20022019-12-21 18:25:54 +010032 int height; // height of added line
Bram Moolenaar071d4272004-06-13 20:20:40 +000033} lineoff_T;
34
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010035static void topline_back(lineoff_T *lp);
36static void botline_forw(lineoff_T *lp);
Bram Moolenaar071d4272004-06-13 20:20:40 +000037
38/*
Bram Moolenaarf6196f42022-10-02 21:29:55 +010039 * Reduce "n" for the screen lines skipped with "wp->w_skipcol".
40 */
Luuk van Baalc8502f92023-05-06 12:40:15 +010041 int
Bram Moolenaarf6196f42022-10-02 21:29:55 +010042adjust_plines_for_skipcol(win_T *wp, int n)
43{
44 if (wp->w_skipcol == 0)
45 return n;
46
47 int off = 0;
48 int width = wp->w_width - win_col_off(wp);
49 if (wp->w_skipcol >= width)
50 {
51 ++off;
52 int skip = wp->w_skipcol - width;
Bram Moolenaarb6aab8f2022-10-03 20:01:16 +010053 width += win_col_off2(wp);
Bram Moolenaarf6196f42022-10-02 21:29:55 +010054 while (skip >= width)
55 {
56 ++off;
57 skip -= width;
58 }
59 }
60 wp->w_valid &= ~VALID_WROW;
61 return n - off;
62}
63
64/*
Bram Moolenaard5337ef2022-10-20 20:15:47 +010065 * Return how many lines "lnum" will take on the screen, taking into account
66 * whether it is the first line, whether w_skipcol is non-zero and limiting to
67 * the window height.
68 */
69 static int
70plines_correct_topline(win_T *wp, linenr_T lnum)
71{
72 int n;
73#ifdef FEAT_DIFF
74 if (lnum == wp->w_topline)
75 n = plines_win_nofill(wp, lnum, FALSE) + wp->w_topfill;
76 else
77#endif
78 n = plines_win(wp, lnum, FALSE);
79 if (lnum == wp->w_topline)
80 n = adjust_plines_for_skipcol(wp, n);
81 if (n > wp->w_height)
82 n = wp->w_height;
83 return n;
84}
85
86/*
Bram Moolenaar071d4272004-06-13 20:20:40 +000087 * Compute wp->w_botline for the current wp->w_topline. Can be called after
88 * wp->w_topline changed.
89 */
90 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +010091comp_botline(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +000092{
93 int n;
94 linenr_T lnum;
95 int done;
96#ifdef FEAT_FOLDING
97 linenr_T last;
98 int folded;
99#endif
100
101 /*
102 * If w_cline_row is valid, start there.
103 * Otherwise have to start at w_topline.
104 */
105 check_cursor_moved(wp);
106 if (wp->w_valid & VALID_CROW)
107 {
108 lnum = wp->w_cursor.lnum;
109 done = wp->w_cline_row;
110 }
111 else
112 {
113 lnum = wp->w_topline;
114 done = 0;
115 }
116
117 for ( ; lnum <= wp->w_buffer->b_ml.ml_line_count; ++lnum)
118 {
119#ifdef FEAT_FOLDING
120 last = lnum;
121 folded = FALSE;
122 if (hasFoldingWin(wp, lnum, NULL, &last, TRUE, NULL))
123 {
124 n = 1;
125 folded = TRUE;
126 }
127 else
128#endif
Bram Moolenaarf6196f42022-10-02 21:29:55 +0100129 {
Bram Moolenaard5337ef2022-10-20 20:15:47 +0100130 n = plines_correct_topline(wp, lnum);
Bram Moolenaarf6196f42022-10-02 21:29:55 +0100131 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000132 if (
133#ifdef FEAT_FOLDING
134 lnum <= wp->w_cursor.lnum && last >= wp->w_cursor.lnum
135#else
136 lnum == wp->w_cursor.lnum
137#endif
138 )
139 {
140 wp->w_cline_row = done;
141 wp->w_cline_height = n;
142#ifdef FEAT_FOLDING
143 wp->w_cline_folded = folded;
144#endif
Bram Moolenaar3d6db142014-03-28 21:49:32 +0100145 redraw_for_cursorline(wp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000146 wp->w_valid |= (VALID_CROW|VALID_CHEIGHT);
147 }
148 if (done + n > wp->w_height)
149 break;
150 done += n;
151#ifdef FEAT_FOLDING
152 lnum = last;
153#endif
154 }
155
Bram Moolenaar85a20022019-12-21 18:25:54 +0100156 // wp->w_botline is the line that is just below the window
Bram Moolenaar071d4272004-06-13 20:20:40 +0000157 wp->w_botline = lnum;
158 wp->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
159
160 set_empty_rows(wp, done);
161}
162
163/*
Bram Moolenaar3d6db142014-03-28 21:49:32 +0100164 * Redraw when w_cline_row changes and 'relativenumber' or 'cursorline' is
165 * set.
166 */
Bram Moolenaarbbb5f8d2019-01-31 13:22:32 +0100167 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100168redraw_for_cursorline(win_T *wp)
Bram Moolenaar3d6db142014-03-28 21:49:32 +0100169{
170 if ((wp->w_p_rnu
171#ifdef FEAT_SYN_HL
172 || wp->w_p_cul
173#endif
174 )
175 && (wp->w_valid & VALID_CROW) == 0
Bram Moolenaare2c453d2019-08-21 14:37:09 +0200176 && !pum_visible())
Bram Moolenaar90a99792018-09-12 21:52:18 +0200177 {
zeertzjqc20e46a2022-03-23 14:55:23 +0000178 // win_line() will redraw the number column and cursorline only.
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100179 redraw_win_later(wp, UPD_VALID);
Bram Moolenaar90a99792018-09-12 21:52:18 +0200180 }
Bram Moolenaar3d6db142014-03-28 21:49:32 +0100181}
182
zeertzjq3e559cd2022-03-27 19:26:55 +0100183#ifdef FEAT_SYN_HL
184/*
185 * Redraw when w_virtcol changes and 'cursorcolumn' is set or 'cursorlineopt'
186 * contains "screenline".
187 */
188 static void
189redraw_for_cursorcolumn(win_T *wp)
190{
191 if ((wp->w_valid & VALID_VIRTCOL) == 0 && !pum_visible())
192 {
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100193 // When 'cursorcolumn' is set need to redraw with UPD_SOME_VALID.
zeertzjq3e559cd2022-03-27 19:26:55 +0100194 if (wp->w_p_cuc)
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100195 redraw_win_later(wp, UPD_SOME_VALID);
196 // When 'cursorlineopt' contains "screenline" need to redraw with
197 // UPD_VALID.
zeertzjq3e559cd2022-03-27 19:26:55 +0100198 else if (wp->w_p_cul && (wp->w_p_culopt_flags & CULOPT_SCRLINE))
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100199 redraw_win_later(wp, UPD_VALID);
zeertzjq3e559cd2022-03-27 19:26:55 +0100200 }
201}
202#endif
203
Bram Moolenaar3d6db142014-03-28 21:49:32 +0100204/*
Luuk van Baal24b62ec2023-05-13 14:12:15 +0100205 * Calculates how much the 'listchars' "precedes" or 'smoothscroll' "<<<"
206 * marker overlaps with buffer text for window "wp".
Yee Cheng Chin81ba26e2022-11-18 12:52:50 +0000207 * Parameter "extra2" should be the padding on the 2nd line, not the first
208 * line.
209 * Returns the number of columns of overlap with buffer text, excluding the
210 * extra padding on the ledge.
211 */
Luuk van Baal24b62ec2023-05-13 14:12:15 +0100212 int
213sms_marker_overlap(win_T *wp, int extra2)
Yee Cheng Chin81ba26e2022-11-18 12:52:50 +0000214{
215#if defined(FEAT_LINEBREAK)
Luuk van Baal24b62ec2023-05-13 14:12:15 +0100216 // There is no marker overlap when in showbreak mode, thus no need to
Yee Cheng Chin81ba26e2022-11-18 12:52:50 +0000217 // account for it. See wlv_screen_line().
Luuk van Baal24b62ec2023-05-13 14:12:15 +0100218 if (*get_showbreak_value(wp) != NUL)
Yee Cheng Chin81ba26e2022-11-18 12:52:50 +0000219 return 0;
220#endif
Luuk van Baal24b62ec2023-05-13 14:12:15 +0100221 // Overlap when 'list' and 'listchars' "precedes" are set is 1.
222 if (wp->w_p_list && wp->w_lcs_chars.prec)
223 return 1;
224
Yee Cheng Chin81ba26e2022-11-18 12:52:50 +0000225 return extra2 > 3 ? 0 : 3 - extra2;
226}
227
228/*
Bram Moolenaardb4d88c2022-12-31 15:13:22 +0000229 * Calculates the skipcol offset for window "wp" given how many
230 * physical lines we want to scroll down.
231 */
232 static int
233skipcol_from_plines(win_T *wp, int plines_off)
234{
235 int width1 = wp->w_width - win_col_off(wp);
236
237 int skipcol = 0;
238 if (plines_off > 0)
239 skipcol += width1;
240 if (plines_off > 1)
241 skipcol += (width1 + win_col_off2(wp)) * (plines_off - 1);
242 return skipcol;
243}
244
245/*
Luuk van Baalc8502f92023-05-06 12:40:15 +0100246 * Set curwin->w_skipcol to zero and redraw later if needed.
Bram Moolenaarf32fb932022-11-17 11:34:38 +0000247 */
248 static void
249reset_skipcol(void)
250{
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000251 if (curwin->w_skipcol == 0)
252 return;
Bram Moolenaarf32fb932022-11-17 11:34:38 +0000253
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000254 curwin->w_skipcol = 0;
255
256 // Should use the least expensive way that displays all that changed.
257 // UPD_NOT_VALID is too expensive, UPD_REDRAW_TOP does not redraw
258 // enough when the top line gets another screen line.
259 redraw_later(UPD_SOME_VALID);
Bram Moolenaarf32fb932022-11-17 11:34:38 +0000260}
261
262/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000263 * Update curwin->w_topline and redraw if necessary.
264 * Used to update the screen before printing a message.
265 */
266 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100267update_topline_redraw(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000268{
269 update_topline();
270 if (must_redraw)
271 update_screen(0);
272}
273
274/*
275 * Update curwin->w_topline to move the cursor onto the screen.
276 */
277 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100278update_topline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000279{
280 long line_count;
281 int halfheight;
282 int n;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000283#ifdef FEAT_FOLDING
284 linenr_T lnum;
285#endif
286 int check_topline = FALSE;
287 int check_botline = FALSE;
Bram Moolenaar6ed545e2022-05-09 20:09:23 +0100288 long *so_ptr = curwin->w_p_so >= 0 ? &curwin->w_p_so : &p_so;
Bram Moolenaar375e3392019-01-31 18:26:10 +0100289 int save_so = *so_ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000290
Luuk van Baal13ece2a2022-10-03 15:28:08 +0100291 // Cursor is updated instead when this is TRUE for 'splitkeep'.
292 if (skip_update_topline)
293 return;
294
Bram Moolenaar85a20022019-12-21 18:25:54 +0100295 // If there is no valid screen and when the window height is zero just use
296 // the cursor line.
Bram Moolenaard5d37532017-03-27 23:02:07 +0200297 if (!screen_valid(TRUE) || curwin->w_height == 0)
Bram Moolenaarcfc216e2014-09-23 18:37:56 +0200298 {
Bram Moolenaar3b6d57f2020-11-01 21:56:40 +0100299 check_cursor_lnum();
Bram Moolenaarcfc216e2014-09-23 18:37:56 +0200300 curwin->w_topline = curwin->w_cursor.lnum;
301 curwin->w_botline = curwin->w_topline;
Bram Moolenaarcfc216e2014-09-23 18:37:56 +0200302 curwin->w_scbind_pos = 1;
Bram Moolenaarcfc216e2014-09-23 18:37:56 +0200303 return;
304 }
305
Bram Moolenaar071d4272004-06-13 20:20:40 +0000306 check_cursor_moved(curwin);
307 if (curwin->w_valid & VALID_TOPLINE)
308 return;
309
Bram Moolenaar85a20022019-12-21 18:25:54 +0100310 // When dragging with the mouse, don't scroll that quickly
Bram Moolenaar9964e462007-05-05 17:54:07 +0000311 if (mouse_dragging > 0)
Bram Moolenaar375e3392019-01-31 18:26:10 +0100312 *so_ptr = mouse_dragging - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000313
Luuk van Baal3ce8c382023-05-08 15:51:14 +0100314 linenr_T old_topline = curwin->w_topline;
315 colnr_T old_skipcol = curwin->w_skipcol;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000316#ifdef FEAT_DIFF
Luuk van Baal3ce8c382023-05-08 15:51:14 +0100317 int old_topfill = curwin->w_topfill;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000318#endif
319
320 /*
321 * If the buffer is empty, always set topline to 1.
322 */
Bram Moolenaar85a20022019-12-21 18:25:54 +0100323 if (BUFEMPTY()) // special case - file is empty
Bram Moolenaar071d4272004-06-13 20:20:40 +0000324 {
325 if (curwin->w_topline != 1)
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100326 redraw_later(UPD_NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000327 curwin->w_topline = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000328 curwin->w_botline = 2;
329 curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000330 curwin->w_scbind_pos = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000331 }
332
333 /*
334 * If the cursor is above or near the top of the window, scroll the window
335 * to show the line the cursor is in, with 'scrolloff' context.
336 */
337 else
338 {
Bram Moolenaar46b54742022-10-06 15:46:49 +0100339 if (curwin->w_topline > 1 || curwin->w_skipcol > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000340 {
Bram Moolenaar85a20022019-12-21 18:25:54 +0100341 // If the cursor is above topline, scrolling is always needed.
342 // If the cursor is far below topline and there is no folding,
343 // scrolling down is never needed.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000344 if (curwin->w_cursor.lnum < curwin->w_topline)
345 check_topline = TRUE;
346 else if (check_top_offset())
347 check_topline = TRUE;
Bram Moolenaar46b54742022-10-06 15:46:49 +0100348 else if (curwin->w_cursor.lnum == curwin->w_topline)
349 {
350 colnr_T vcol;
351
Yee Cheng Chin81ba26e2022-11-18 12:52:50 +0000352 // Check that the cursor position is visible. Add columns for
Luuk van Baal24b62ec2023-05-13 14:12:15 +0100353 // the marker displayed in the top-left if needed.
Bram Moolenaar46b54742022-10-06 15:46:49 +0100354 getvvcol(curwin, &curwin->w_cursor, &vcol, NULL, NULL);
Luuk van Baal24b62ec2023-05-13 14:12:15 +0100355 int overlap = sms_marker_overlap(curwin, curwin_col_off()
356 - curwin_col_off2());
357 if (curwin->w_skipcol + overlap > vcol)
Bram Moolenaar46b54742022-10-06 15:46:49 +0100358 check_topline = TRUE;
359 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000360 }
361#ifdef FEAT_DIFF
Bram Moolenaar85a20022019-12-21 18:25:54 +0100362 // Check if there are more filler lines than allowed.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000363 if (!check_topline && curwin->w_topfill > diff_check_fill(curwin,
364 curwin->w_topline))
365 check_topline = TRUE;
366#endif
367
368 if (check_topline)
369 {
370 halfheight = curwin->w_height / 2 - 1;
371 if (halfheight < 2)
372 halfheight = 2;
373
374#ifdef FEAT_FOLDING
375 if (hasAnyFolding(curwin))
376 {
Bram Moolenaar85a20022019-12-21 18:25:54 +0100377 // Count the number of logical lines between the cursor and
378 // topline + scrolloff (approximation of how much will be
379 // scrolled).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000380 n = 0;
381 for (lnum = curwin->w_cursor.lnum;
Bram Moolenaar85a20022019-12-21 18:25:54 +0100382 lnum < curwin->w_topline + *so_ptr; ++lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000383 {
384 ++n;
Bram Moolenaar85a20022019-12-21 18:25:54 +0100385 // stop at end of file or when we know we are far off
Bram Moolenaar071d4272004-06-13 20:20:40 +0000386 if (lnum >= curbuf->b_ml.ml_line_count || n >= halfheight)
387 break;
388 (void)hasFolding(lnum, NULL, &lnum);
389 }
390 }
391 else
392#endif
Bram Moolenaar375e3392019-01-31 18:26:10 +0100393 n = curwin->w_topline + *so_ptr - curwin->w_cursor.lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000394
Bram Moolenaar85a20022019-12-21 18:25:54 +0100395 // If we weren't very close to begin with, we scroll to put the
396 // cursor in the middle of the window. Otherwise put the cursor
397 // near the top of the window.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000398 if (n >= halfheight)
Bram Moolenaar1d6539c2023-02-14 17:41:20 +0000399 scroll_cursor_halfway(FALSE, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000400 else
401 {
Bram Moolenaar1e015462005-09-25 22:16:38 +0000402 scroll_cursor_top(scrolljump_value(), FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000403 check_botline = TRUE;
404 }
405 }
406
407 else
408 {
409#ifdef FEAT_FOLDING
Bram Moolenaar85a20022019-12-21 18:25:54 +0100410 // Make sure topline is the first line of a fold.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000411 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
412#endif
413 check_botline = TRUE;
414 }
415 }
416
417 /*
418 * If the cursor is below the bottom of the window, scroll the window
419 * to put the cursor on the window.
420 * When w_botline is invalid, recompute it first, to avoid a redraw later.
421 * If w_botline was approximated, we might need a redraw later in a few
422 * cases, but we don't want to spend (a lot of) time recomputing w_botline
423 * for every small change.
424 */
425 if (check_botline)
426 {
427 if (!(curwin->w_valid & VALID_BOTLINE_AP))
428 validate_botline();
429
430 if (curwin->w_botline <= curbuf->b_ml.ml_line_count)
431 {
Bram Moolenaard4153d42008-11-15 15:06:17 +0000432 if (curwin->w_cursor.lnum < curwin->w_botline)
433 {
434 if (((long)curwin->w_cursor.lnum
Bram Moolenaar8088ae92022-06-20 11:38:17 +0100435 >= (long)curwin->w_botline - *so_ptr
Bram Moolenaar071d4272004-06-13 20:20:40 +0000436#ifdef FEAT_FOLDING
437 || hasAnyFolding(curwin)
438#endif
439 ))
Bram Moolenaard4153d42008-11-15 15:06:17 +0000440 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000441 lineoff_T loff;
442
Bram Moolenaar85a20022019-12-21 18:25:54 +0100443 // Cursor is (a few lines) above botline, check if there are
444 // 'scrolloff' window lines below the cursor. If not, need to
445 // scroll.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000446 n = curwin->w_empty_rows;
447 loff.lnum = curwin->w_cursor.lnum;
448#ifdef FEAT_FOLDING
Bram Moolenaar85a20022019-12-21 18:25:54 +0100449 // In a fold go to its last line.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000450 (void)hasFolding(loff.lnum, NULL, &loff.lnum);
451#endif
452#ifdef FEAT_DIFF
453 loff.fill = 0;
454 n += curwin->w_filler_rows;
455#endif
456 loff.height = 0;
457 while (loff.lnum < curwin->w_botline
458#ifdef FEAT_DIFF
459 && (loff.lnum + 1 < curwin->w_botline || loff.fill == 0)
460#endif
461 )
462 {
463 n += loff.height;
Bram Moolenaar375e3392019-01-31 18:26:10 +0100464 if (n >= *so_ptr)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000465 break;
466 botline_forw(&loff);
467 }
Bram Moolenaar375e3392019-01-31 18:26:10 +0100468 if (n >= *so_ptr)
Bram Moolenaar85a20022019-12-21 18:25:54 +0100469 // sufficient context, no need to scroll
Bram Moolenaar071d4272004-06-13 20:20:40 +0000470 check_botline = FALSE;
Bram Moolenaard4153d42008-11-15 15:06:17 +0000471 }
472 else
Bram Moolenaar85a20022019-12-21 18:25:54 +0100473 // sufficient context, no need to scroll
Bram Moolenaard4153d42008-11-15 15:06:17 +0000474 check_botline = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000475 }
476 if (check_botline)
477 {
478#ifdef FEAT_FOLDING
479 if (hasAnyFolding(curwin))
480 {
Bram Moolenaar85a20022019-12-21 18:25:54 +0100481 // Count the number of logical lines between the cursor and
482 // botline - scrolloff (approximation of how much will be
483 // scrolled).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000484 line_count = 0;
485 for (lnum = curwin->w_cursor.lnum;
Bram Moolenaar85a20022019-12-21 18:25:54 +0100486 lnum >= curwin->w_botline - *so_ptr; --lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000487 {
488 ++line_count;
Bram Moolenaar85a20022019-12-21 18:25:54 +0100489 // stop at end of file or when we know we are far off
Bram Moolenaar071d4272004-06-13 20:20:40 +0000490 if (lnum <= 0 || line_count > curwin->w_height + 1)
491 break;
492 (void)hasFolding(lnum, &lnum, NULL);
493 }
494 }
495 else
496#endif
497 line_count = curwin->w_cursor.lnum - curwin->w_botline
Bram Moolenaar8088ae92022-06-20 11:38:17 +0100498 + 1 + *so_ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000499 if (line_count <= curwin->w_height + 1)
Bram Moolenaar1e015462005-09-25 22:16:38 +0000500 scroll_cursor_bot(scrolljump_value(), FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000501 else
Bram Moolenaar1d6539c2023-02-14 17:41:20 +0000502 scroll_cursor_halfway(FALSE, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000503 }
504 }
505 }
506 curwin->w_valid |= VALID_TOPLINE;
507
508 /*
509 * Need to redraw when topline changed.
510 */
511 if (curwin->w_topline != old_topline
512#ifdef FEAT_DIFF
513 || curwin->w_topfill != old_topfill
514#endif
515 )
516 {
Bram Moolenaar76b9b362012-02-04 23:35:00 +0100517 dollar_vcol = -1;
Bram Moolenaarf32fb932022-11-17 11:34:38 +0000518 redraw_later(UPD_VALID);
Luuk van Baal3ce8c382023-05-08 15:51:14 +0100519
520 // Only reset w_skipcol if it was not just set to make cursor visible.
521 if (curwin->w_skipcol == old_skipcol)
522 reset_skipcol();
Bram Moolenaarf32fb932022-11-17 11:34:38 +0000523
Bram Moolenaar85a20022019-12-21 18:25:54 +0100524 // May need to set w_skipcol when cursor in w_topline.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000525 if (curwin->w_cursor.lnum == curwin->w_topline)
526 validate_cursor();
527 }
528
Bram Moolenaar375e3392019-01-31 18:26:10 +0100529 *so_ptr = save_so;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000530}
531
532/*
Bram Moolenaar1e015462005-09-25 22:16:38 +0000533 * Return the scrolljump value to use for the current window.
534 * When 'scrolljump' is positive use it as-is.
535 * When 'scrolljump' is negative use it as a percentage of the window height.
536 */
537 static int
Bram Moolenaar9b578142016-01-30 19:39:49 +0100538scrolljump_value(void)
Bram Moolenaar1e015462005-09-25 22:16:38 +0000539{
540 if (p_sj >= 0)
541 return (int)p_sj;
542 return (curwin->w_height * -p_sj) / 100;
543}
544
545/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000546 * Return TRUE when there are not 'scrolloff' lines above the cursor for the
547 * current window.
548 */
549 static int
Bram Moolenaar9b578142016-01-30 19:39:49 +0100550check_top_offset(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000551{
552 lineoff_T loff;
553 int n;
Bram Moolenaar6ed545e2022-05-09 20:09:23 +0100554 long so = get_scrolloff_value();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000555
Bram Moolenaar375e3392019-01-31 18:26:10 +0100556 if (curwin->w_cursor.lnum < curwin->w_topline + so
Bram Moolenaar071d4272004-06-13 20:20:40 +0000557#ifdef FEAT_FOLDING
558 || hasAnyFolding(curwin)
559#endif
560 )
561 {
562 loff.lnum = curwin->w_cursor.lnum;
563#ifdef FEAT_DIFF
564 loff.fill = 0;
Bram Moolenaar85a20022019-12-21 18:25:54 +0100565 n = curwin->w_topfill; // always have this context
Bram Moolenaar071d4272004-06-13 20:20:40 +0000566#else
567 n = 0;
568#endif
Bram Moolenaar85a20022019-12-21 18:25:54 +0100569 // Count the visible screen lines above the cursor line.
Bram Moolenaar375e3392019-01-31 18:26:10 +0100570 while (n < so)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000571 {
572 topline_back(&loff);
Bram Moolenaar85a20022019-12-21 18:25:54 +0100573 // Stop when included a line above the window.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000574 if (loff.lnum < curwin->w_topline
575#ifdef FEAT_DIFF
576 || (loff.lnum == curwin->w_topline && loff.fill > 0)
577#endif
578 )
579 break;
580 n += loff.height;
581 }
Bram Moolenaar375e3392019-01-31 18:26:10 +0100582 if (n < so)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000583 return TRUE;
584 }
585 return FALSE;
586}
587
Bram Moolenaare24b4ab2022-09-16 20:51:14 +0100588/*
589 * Update w_curswant.
590 */
591 void
592update_curswant_force(void)
593{
594 validate_virtcol();
595 curwin->w_curswant = curwin->w_virtcol
596#ifdef FEAT_PROP_POPUP
597 - curwin->w_virtcol_first_char
598#endif
599 ;
600 curwin->w_set_curswant = FALSE;
601}
602
603/*
604 * Update w_curswant if w_set_curswant is set.
605 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000606 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100607update_curswant(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000608{
609 if (curwin->w_set_curswant)
Bram Moolenaare24b4ab2022-09-16 20:51:14 +0100610 update_curswant_force();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000611}
612
613/*
614 * Check if the cursor has moved. Set the w_valid flag accordingly.
615 */
616 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100617check_cursor_moved(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000618{
619 if (wp->w_cursor.lnum != wp->w_valid_cursor.lnum)
620 {
621 wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL
Bram Moolenaara91cb982022-05-08 19:39:31 +0100622 |VALID_CHEIGHT|VALID_CROW|VALID_TOPLINE
623 |VALID_BOTLINE|VALID_BOTLINE_AP);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000624 wp->w_valid_cursor = wp->w_cursor;
625 wp->w_valid_leftcol = wp->w_leftcol;
Bram Moolenaar2fbabd22022-10-12 19:53:38 +0100626 wp->w_valid_skipcol = wp->w_skipcol;
627 }
628 else if (wp->w_skipcol != wp->w_valid_skipcol)
629 {
630 wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL
631 |VALID_CHEIGHT|VALID_CROW
632 |VALID_BOTLINE|VALID_BOTLINE_AP);
633 wp->w_valid_cursor = wp->w_cursor;
634 wp->w_valid_leftcol = wp->w_leftcol;
635 wp->w_valid_skipcol = wp->w_skipcol;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000636 }
637 else if (wp->w_cursor.col != wp->w_valid_cursor.col
638 || wp->w_leftcol != wp->w_valid_leftcol
Bram Moolenaar29ddebe2019-01-26 17:28:26 +0100639 || wp->w_cursor.coladd != wp->w_valid_cursor.coladd)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000640 {
641 wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL);
642 wp->w_valid_cursor.col = wp->w_cursor.col;
643 wp->w_valid_leftcol = wp->w_leftcol;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000644 wp->w_valid_cursor.coladd = wp->w_cursor.coladd;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000645 }
646}
647
648/*
649 * Call this function when some window settings have changed, which require
650 * the cursor position, botline and topline to be recomputed and the window to
651 * be redrawn. E.g, when changing the 'wrap' option or folding.
652 */
653 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100654changed_window_setting(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000655{
656 changed_window_setting_win(curwin);
657}
658
659 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100660changed_window_setting_win(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000661{
662 wp->w_lines_valid = 0;
663 changed_line_abv_curs_win(wp);
664 wp->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP|VALID_TOPLINE);
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100665 redraw_win_later(wp, UPD_NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000666}
667
Dominique Pellee764d1b2023-03-12 21:20:59 +0000668#if defined(FEAT_PROP_POPUP) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000669/*
Bram Moolenaar89469d12022-12-02 20:46:26 +0000670 * Call changed_window_setting_win() for every window containing "buf".
671 */
672 void
673changed_window_setting_buf(buf_T *buf)
674{
675 tabpage_T *tp;
676 win_T *wp;
677
678 FOR_ALL_TAB_WINDOWS(tp, wp)
679 if (wp->w_buffer == buf)
680 changed_window_setting_win(wp);
681}
Dominique Pellee764d1b2023-03-12 21:20:59 +0000682#endif
Bram Moolenaar89469d12022-12-02 20:46:26 +0000683
684/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000685 * Set wp->w_topline to a certain number.
686 */
687 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100688set_topline(win_T *wp, linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000689{
Bram Moolenaar04626c22021-09-01 16:02:07 +0200690#ifdef FEAT_DIFF
691 linenr_T prev_topline = wp->w_topline;
692#endif
693
Bram Moolenaar071d4272004-06-13 20:20:40 +0000694#ifdef FEAT_FOLDING
Bram Moolenaar85a20022019-12-21 18:25:54 +0100695 // go to first of folded lines
Bram Moolenaar071d4272004-06-13 20:20:40 +0000696 (void)hasFoldingWin(wp, lnum, &lnum, NULL, TRUE, NULL);
697#endif
Bram Moolenaar85a20022019-12-21 18:25:54 +0100698 // Approximate the value of w_botline
Bram Moolenaar071d4272004-06-13 20:20:40 +0000699 wp->w_botline += lnum - wp->w_topline;
Bram Moolenaar23999d72020-12-23 14:36:00 +0100700 if (wp->w_botline > wp->w_buffer->b_ml.ml_line_count + 1)
701 wp->w_botline = wp->w_buffer->b_ml.ml_line_count + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000702 wp->w_topline = lnum;
Bram Moolenaard4153d42008-11-15 15:06:17 +0000703 wp->w_topline_was_set = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000704#ifdef FEAT_DIFF
Bram Moolenaar04626c22021-09-01 16:02:07 +0200705 if (lnum != prev_topline)
706 // Keep the filler lines when the topline didn't change.
707 wp->w_topfill = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000708#endif
709 wp->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_TOPLINE);
Bram Moolenaar85a20022019-12-21 18:25:54 +0100710 // Don't set VALID_TOPLINE here, 'scrolloff' needs to be checked.
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100711 redraw_later(UPD_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000712}
713
714/*
715 * Call this function when the length of the cursor line (in screen
716 * characters) has changed, and the change is before the cursor.
717 * Need to take care of w_botline separately!
718 */
719 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100720changed_cline_bef_curs(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000721{
722 curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL
723 |VALID_CHEIGHT|VALID_TOPLINE);
724}
725
726 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100727changed_cline_bef_curs_win(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000728{
729 wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL
730 |VALID_CHEIGHT|VALID_TOPLINE);
731}
732
Bram Moolenaar071d4272004-06-13 20:20:40 +0000733/*
734 * Call this function when the length of a line (in screen characters) above
735 * the cursor have changed.
736 * Need to take care of w_botline separately!
737 */
738 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100739changed_line_abv_curs(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000740{
741 curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL|VALID_CROW
742 |VALID_CHEIGHT|VALID_TOPLINE);
743}
744
745 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100746changed_line_abv_curs_win(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000747{
748 wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL|VALID_CROW
749 |VALID_CHEIGHT|VALID_TOPLINE);
750}
751
Dominique Pellee764d1b2023-03-12 21:20:59 +0000752#if defined(FEAT_PROP_POPUP) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000753/*
Bram Moolenaar326c5d32022-08-12 13:05:49 +0100754 * Display of line has changed for "buf", invalidate cursor position and
755 * w_botline.
756 */
757 void
758changed_line_display_buf(buf_T *buf)
759{
760 win_T *wp;
761
762 FOR_ALL_WINDOWS(wp)
763 if (wp->w_buffer == buf)
764 wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL
765 |VALID_CROW|VALID_CHEIGHT
766 |VALID_TOPLINE|VALID_BOTLINE|VALID_BOTLINE_AP);
767}
Dominique Pellee764d1b2023-03-12 21:20:59 +0000768#endif
Bram Moolenaar326c5d32022-08-12 13:05:49 +0100769
770/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000771 * Make sure the value of curwin->w_botline is valid.
772 */
773 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100774validate_botline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000775{
Bram Moolenaar23999d72020-12-23 14:36:00 +0100776 validate_botline_win(curwin);
777}
778
779/*
780 * Make sure the value of wp->w_botline is valid.
781 */
782 void
783validate_botline_win(win_T *wp)
784{
785 if (!(wp->w_valid & VALID_BOTLINE))
786 comp_botline(wp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000787}
788
789/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000790 * Mark curwin->w_botline as invalid (because of some change in the buffer).
791 */
792 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100793invalidate_botline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000794{
795 curwin->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP);
796}
797
798 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100799invalidate_botline_win(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000800{
801 wp->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP);
802}
803
Bram Moolenaar071d4272004-06-13 20:20:40 +0000804 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100805approximate_botline_win(
806 win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000807{
808 wp->w_valid &= ~VALID_BOTLINE;
809}
810
Bram Moolenaar071d4272004-06-13 20:20:40 +0000811/*
812 * Return TRUE if curwin->w_wrow and curwin->w_wcol are valid.
813 */
814 int
Bram Moolenaar9b578142016-01-30 19:39:49 +0100815cursor_valid(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000816{
817 check_cursor_moved(curwin);
818 return ((curwin->w_valid & (VALID_WROW|VALID_WCOL)) ==
819 (VALID_WROW|VALID_WCOL));
820}
821
822/*
823 * Validate cursor position. Makes sure w_wrow and w_wcol are valid.
824 * w_topline must be valid, you may need to call update_topline() first!
825 */
826 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100827validate_cursor(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000828{
Bram Moolenaard4566c12022-09-24 21:06:39 +0100829 check_cursor_lnum();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000830 check_cursor_moved(curwin);
831 if ((curwin->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW))
832 curs_columns(TRUE);
833}
834
835#if defined(FEAT_GUI) || defined(PROTO)
836/*
837 * validate w_cline_row.
838 */
839 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100840validate_cline_row(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000841{
842 /*
843 * First make sure that w_topline is valid (after moving the cursor).
844 */
845 update_topline();
846 check_cursor_moved(curwin);
847 if (!(curwin->w_valid & VALID_CROW))
Bram Moolenaar3f9be972014-12-13 21:09:57 +0100848 curs_rows(curwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000849}
850#endif
851
852/*
853 * Compute wp->w_cline_row and wp->w_cline_height, based on the current value
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200854 * of wp->w_topline.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000855 */
856 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100857curs_rows(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000858{
859 linenr_T lnum;
860 int i;
861 int all_invalid;
862 int valid;
863#ifdef FEAT_FOLDING
864 long fold_count;
865#endif
866
Bram Moolenaar85a20022019-12-21 18:25:54 +0100867 // Check if wp->w_lines[].wl_size is invalid
Bram Moolenaar071d4272004-06-13 20:20:40 +0000868 all_invalid = (!redrawing()
869 || wp->w_lines_valid == 0
870 || wp->w_lines[0].wl_lnum > wp->w_topline);
871 i = 0;
872 wp->w_cline_row = 0;
873 for (lnum = wp->w_topline; lnum < wp->w_cursor.lnum; ++i)
874 {
875 valid = FALSE;
876 if (!all_invalid && i < wp->w_lines_valid)
877 {
878 if (wp->w_lines[i].wl_lnum < lnum || !wp->w_lines[i].wl_valid)
Bram Moolenaar85a20022019-12-21 18:25:54 +0100879 continue; // skip changed or deleted lines
Bram Moolenaar071d4272004-06-13 20:20:40 +0000880 if (wp->w_lines[i].wl_lnum == lnum)
881 {
882#ifdef FEAT_FOLDING
Bram Moolenaar85a20022019-12-21 18:25:54 +0100883 // Check for newly inserted lines below this row, in which
884 // case we need to check for folded lines.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000885 if (!wp->w_buffer->b_mod_set
886 || wp->w_lines[i].wl_lastlnum < wp->w_cursor.lnum
887 || wp->w_buffer->b_mod_top
888 > wp->w_lines[i].wl_lastlnum + 1)
889#endif
890 valid = TRUE;
891 }
892 else if (wp->w_lines[i].wl_lnum > lnum)
Bram Moolenaar85a20022019-12-21 18:25:54 +0100893 --i; // hold at inserted lines
Bram Moolenaar071d4272004-06-13 20:20:40 +0000894 }
895 if (valid
896#ifdef FEAT_DIFF
897 && (lnum != wp->w_topline || !wp->w_p_diff)
898#endif
899 )
900 {
901#ifdef FEAT_FOLDING
902 lnum = wp->w_lines[i].wl_lastlnum + 1;
Bram Moolenaar85a20022019-12-21 18:25:54 +0100903 // Cursor inside folded lines, don't count this row
Bram Moolenaar071d4272004-06-13 20:20:40 +0000904 if (lnum > wp->w_cursor.lnum)
905 break;
906#else
907 ++lnum;
908#endif
909 wp->w_cline_row += wp->w_lines[i].wl_size;
910 }
911 else
912 {
913#ifdef FEAT_FOLDING
914 fold_count = foldedCount(wp, lnum, NULL);
915 if (fold_count)
916 {
917 lnum += fold_count;
918 if (lnum > wp->w_cursor.lnum)
919 break;
920 ++wp->w_cline_row;
921 }
922 else
923#endif
Bram Moolenaarf6196f42022-10-02 21:29:55 +0100924 {
Bram Moolenaard5337ef2022-10-20 20:15:47 +0100925 wp->w_cline_row += plines_correct_topline(wp, lnum);
926 ++lnum;
Bram Moolenaarf6196f42022-10-02 21:29:55 +0100927 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000928 }
929 }
930
931 check_cursor_moved(wp);
932 if (!(wp->w_valid & VALID_CHEIGHT))
933 {
934 if (all_invalid
935 || i == wp->w_lines_valid
936 || (i < wp->w_lines_valid
937 && (!wp->w_lines[i].wl_valid
938 || wp->w_lines[i].wl_lnum != wp->w_cursor.lnum)))
939 {
940#ifdef FEAT_DIFF
941 if (wp->w_cursor.lnum == wp->w_topline)
942 wp->w_cline_height = plines_win_nofill(wp, wp->w_cursor.lnum,
943 TRUE) + wp->w_topfill;
944 else
945#endif
946 wp->w_cline_height = plines_win(wp, wp->w_cursor.lnum, TRUE);
947#ifdef FEAT_FOLDING
948 wp->w_cline_folded = hasFoldingWin(wp, wp->w_cursor.lnum,
949 NULL, NULL, TRUE, NULL);
950#endif
951 }
952 else if (i > wp->w_lines_valid)
953 {
Bram Moolenaar85a20022019-12-21 18:25:54 +0100954 // a line that is too long to fit on the last screen line
Bram Moolenaar071d4272004-06-13 20:20:40 +0000955 wp->w_cline_height = 0;
956#ifdef FEAT_FOLDING
957 wp->w_cline_folded = hasFoldingWin(wp, wp->w_cursor.lnum,
958 NULL, NULL, TRUE, NULL);
959#endif
960 }
961 else
962 {
963 wp->w_cline_height = wp->w_lines[i].wl_size;
964#ifdef FEAT_FOLDING
965 wp->w_cline_folded = wp->w_lines[i].wl_folded;
966#endif
967 }
968 }
969
Bram Moolenaar3d6db142014-03-28 21:49:32 +0100970 redraw_for_cursorline(curwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000971 wp->w_valid |= VALID_CROW|VALID_CHEIGHT;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000972}
973
974/*
975 * Validate curwin->w_virtcol only.
976 */
977 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100978validate_virtcol(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000979{
980 validate_virtcol_win(curwin);
981}
982
983/*
984 * Validate wp->w_virtcol only.
985 */
986 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100987validate_virtcol_win(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000988{
989 check_cursor_moved(wp);
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000990
991 if (wp->w_valid & VALID_VIRTCOL)
992 return;
993
Bram Moolenaar04e0ed12022-09-10 20:00:56 +0100994#ifdef FEAT_PROP_POPUP
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000995 wp->w_virtcol_first_char = 0;
Bram Moolenaar04e0ed12022-09-10 20:00:56 +0100996#endif
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000997 getvvcol(wp, &wp->w_cursor, NULL, &(wp->w_virtcol), NULL);
Bram Moolenaar2b48ad52006-03-12 21:56:11 +0000998#ifdef FEAT_SYN_HL
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000999 redraw_for_cursorcolumn(wp);
Bram Moolenaar2b48ad52006-03-12 21:56:11 +00001000#endif
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001001 wp->w_valid |= VALID_VIRTCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001002}
1003
1004/*
1005 * Validate curwin->w_cline_height only.
1006 */
Bram Moolenaar09dd2bb2019-12-14 18:42:15 +01001007 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001008validate_cheight(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001009{
1010 check_cursor_moved(curwin);
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001011
1012 if (curwin->w_valid & VALID_CHEIGHT)
1013 return;
1014
Bram Moolenaar071d4272004-06-13 20:20:40 +00001015#ifdef FEAT_DIFF
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001016 if (curwin->w_cursor.lnum == curwin->w_topline)
1017 curwin->w_cline_height = plines_nofill(curwin->w_cursor.lnum)
1018 + curwin->w_topfill;
1019 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001020#endif
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001021 curwin->w_cline_height = plines(curwin->w_cursor.lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001022#ifdef FEAT_FOLDING
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001023 curwin->w_cline_folded = hasFolding(curwin->w_cursor.lnum, NULL, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001024#endif
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001025 curwin->w_valid |= VALID_CHEIGHT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001026}
1027
1028/*
Bram Moolenaarc236c162008-07-13 17:41:49 +00001029 * Validate w_wcol and w_virtcol only.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001030 */
1031 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001032validate_cursor_col(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001033{
1034 colnr_T off;
1035 colnr_T col;
Bram Moolenaar6427c602010-02-03 17:43:07 +01001036 int width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001037
1038 validate_virtcol();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001039
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001040 if (curwin->w_valid & VALID_WCOL)
1041 return;
Bram Moolenaarc236c162008-07-13 17:41:49 +00001042
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001043 col = curwin->w_virtcol;
1044 off = curwin_col_off();
1045 col += off;
1046 width = curwin->w_width - off + curwin_col_off2();
1047
1048 // long line wrapping, adjust curwin->w_wrow
1049 if (curwin->w_p_wrap
1050 && col >= (colnr_T)curwin->w_width
1051 && width > 0)
1052 // use same formula as what is used in curs_columns()
1053 col -= ((col - curwin->w_width) / width + 1) * width;
1054 if (col > (int)curwin->w_leftcol)
1055 col -= curwin->w_leftcol;
1056 else
1057 col = 0;
1058 curwin->w_wcol = col;
1059
1060 curwin->w_valid |= VALID_WCOL;
Bram Moolenaar4792a672020-11-15 21:11:18 +01001061#ifdef FEAT_PROP_POPUP
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001062 curwin->w_flags &= ~WFLAG_WCOL_OFF_ADDED;
Bram Moolenaar4792a672020-11-15 21:11:18 +01001063#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001064}
1065
1066/*
Bram Moolenaar64486672010-05-16 15:46:46 +02001067 * Compute offset of a window, occupied by absolute or relative line number,
1068 * fold column and sign column (these don't move when scrolling horizontally).
Bram Moolenaar071d4272004-06-13 20:20:40 +00001069 */
1070 int
Bram Moolenaar9b578142016-01-30 19:39:49 +01001071win_col_off(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001072{
Bram Moolenaar64486672010-05-16 15:46:46 +02001073 return (((wp->w_p_nu || wp->w_p_rnu) ? number_width(wp) + 1 : 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001074 + (cmdwin_type == 0 || wp != curwin ? 0 : 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001075#ifdef FEAT_FOLDING
1076 + wp->w_p_fdc
1077#endif
1078#ifdef FEAT_SIGNS
Bram Moolenaar95ec9d62016-08-12 18:29:59 +02001079 + (signcolumn_on(wp) ? 2 : 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001080#endif
1081 );
1082}
1083
1084 int
Bram Moolenaar9b578142016-01-30 19:39:49 +01001085curwin_col_off(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001086{
1087 return win_col_off(curwin);
1088}
1089
1090/*
1091 * Return the difference in column offset for the second screen line of a
Bram Moolenaar6b2d4ff2022-10-03 14:06:02 +01001092 * wrapped line. It's positive if 'number' or 'relativenumber' is on and 'n'
1093 * is in 'cpoptions'.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001094 */
1095 int
Bram Moolenaar9b578142016-01-30 19:39:49 +01001096win_col_off2(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001097{
Bram Moolenaar64486672010-05-16 15:46:46 +02001098 if ((wp->w_p_nu || wp->w_p_rnu) && vim_strchr(p_cpo, CPO_NUMCOL) != NULL)
Bram Moolenaar592e0a22004-07-03 16:05:59 +00001099 return number_width(wp) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001100 return 0;
1101}
1102
1103 int
Bram Moolenaar9b578142016-01-30 19:39:49 +01001104curwin_col_off2(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001105{
1106 return win_col_off2(curwin);
1107}
1108
1109/*
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01001110 * Compute curwin->w_wcol and curwin->w_virtcol.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001111 * Also updates curwin->w_wrow and curwin->w_cline_row.
1112 * Also updates curwin->w_leftcol.
1113 */
1114 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001115curs_columns(
Bram Moolenaar85a20022019-12-21 18:25:54 +01001116 int may_scroll) // when TRUE, may scroll horizontally
Bram Moolenaar071d4272004-06-13 20:20:40 +00001117{
1118 int diff;
Bram Moolenaar85a20022019-12-21 18:25:54 +01001119 int extra; // offset for first screen line
Bram Moolenaar071d4272004-06-13 20:20:40 +00001120 int off_left, off_right;
1121 int n;
1122 int p_lines;
Bram Moolenaar856c5d22022-10-13 21:54:28 +01001123 int width1; // text width for first screen line
1124 int width2 = 0; // text width for second and later screen line
Bram Moolenaar071d4272004-06-13 20:20:40 +00001125 int new_leftcol;
1126 colnr_T startcol;
1127 colnr_T endcol;
1128 colnr_T prev_skipcol;
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01001129 long so = get_scrolloff_value();
1130 long siso = get_sidescrolloff_value();
Bram Moolenaar4b6172e2022-10-13 20:23:28 +01001131 int did_sub_skipcol = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001132
1133 /*
1134 * First make sure that w_topline is valid (after moving the cursor).
1135 */
Luuk van Baal13ece2a2022-10-03 15:28:08 +01001136 update_topline();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001137
1138 /*
1139 * Next make sure that w_cline_row is valid.
1140 */
1141 if (!(curwin->w_valid & VALID_CROW))
Bram Moolenaar3f9be972014-12-13 21:09:57 +01001142 curs_rows(curwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001143
Bram Moolenaar04e0ed12022-09-10 20:00:56 +01001144#ifdef FEAT_PROP_POPUP
1145 // will be set by getvvcol() but not reset
1146 curwin->w_virtcol_first_char = 0;
1147#endif
1148
Bram Moolenaar071d4272004-06-13 20:20:40 +00001149 /*
1150 * Compute the number of virtual columns.
1151 */
1152#ifdef FEAT_FOLDING
1153 if (curwin->w_cline_folded)
Bram Moolenaar85a20022019-12-21 18:25:54 +01001154 // In a folded line the cursor is always in the first column
Bram Moolenaar071d4272004-06-13 20:20:40 +00001155 startcol = curwin->w_virtcol = endcol = curwin->w_leftcol;
1156 else
1157#endif
1158 getvvcol(curwin, &curwin->w_cursor,
1159 &startcol, &(curwin->w_virtcol), &endcol);
1160
Bram Moolenaar85a20022019-12-21 18:25:54 +01001161 // remove '$' from change command when cursor moves onto it
Bram Moolenaar071d4272004-06-13 20:20:40 +00001162 if (startcol > dollar_vcol)
Bram Moolenaar76b9b362012-02-04 23:35:00 +01001163 dollar_vcol = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001164
1165 extra = curwin_col_off();
1166 curwin->w_wcol = curwin->w_virtcol + extra;
1167 endcol += extra;
1168
1169 /*
1170 * Now compute w_wrow, counting screen lines from w_cline_row.
1171 */
1172 curwin->w_wrow = curwin->w_cline_row;
1173
Bram Moolenaar856c5d22022-10-13 21:54:28 +01001174 width1 = curwin->w_width - extra;
1175 if (width1 <= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001176 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01001177 // No room for text, put cursor in last char of window.
Bram Moolenaar30441bb2021-07-08 13:19:31 +02001178 // If not wrapping, the last non-empty line.
Bram Moolenaar02631462017-09-22 15:20:32 +02001179 curwin->w_wcol = curwin->w_width - 1;
Bram Moolenaar30441bb2021-07-08 13:19:31 +02001180 if (curwin->w_p_wrap)
1181 curwin->w_wrow = curwin->w_height - 1;
1182 else
1183 curwin->w_wrow = curwin->w_height - 1 - curwin->w_empty_rows;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001184 }
Bram Moolenaar4033c552017-09-16 20:54:51 +02001185 else if (curwin->w_p_wrap && curwin->w_width != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001186 {
Bram Moolenaar856c5d22022-10-13 21:54:28 +01001187 width2 = width1 + curwin_col_off2();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001188
Bram Moolenaar2fbabd22022-10-12 19:53:38 +01001189 // skip columns that are not visible
1190 if (curwin->w_cursor.lnum == curwin->w_topline
Bram Moolenaar856c5d22022-10-13 21:54:28 +01001191 && curwin->w_skipcol > 0
Bram Moolenaar2fbabd22022-10-12 19:53:38 +01001192 && curwin->w_wcol >= curwin->w_skipcol)
Bram Moolenaar4b6172e2022-10-13 20:23:28 +01001193 {
Yee Cheng Chin01ee52b2022-11-17 12:41:42 +00001194 // Deduct by multiples of width2. This allows the long line
1195 // wrapping formula below to correctly calculate the w_wcol value
1196 // when wrapping.
Bram Moolenaar856c5d22022-10-13 21:54:28 +01001197 if (curwin->w_skipcol <= width1)
Yee Cheng Chin01ee52b2022-11-17 12:41:42 +00001198 curwin->w_wcol -= width2;
Bram Moolenaar856c5d22022-10-13 21:54:28 +01001199 else
Yee Cheng Chin01ee52b2022-11-17 12:41:42 +00001200 curwin->w_wcol -= width2
Bram Moolenaar856c5d22022-10-13 21:54:28 +01001201 * (((curwin->w_skipcol - width1) / width2) + 1);
Yee Cheng Chin01ee52b2022-11-17 12:41:42 +00001202
Bram Moolenaar4b6172e2022-10-13 20:23:28 +01001203 did_sub_skipcol = TRUE;
1204 }
Bram Moolenaar2fbabd22022-10-12 19:53:38 +01001205
Bram Moolenaar85a20022019-12-21 18:25:54 +01001206 // long line wrapping, adjust curwin->w_wrow
Bram Moolenaar02631462017-09-22 15:20:32 +02001207 if (curwin->w_wcol >= curwin->w_width)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001208 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01001209 // this same formula is used in validate_cursor_col()
Bram Moolenaar856c5d22022-10-13 21:54:28 +01001210 n = (curwin->w_wcol - curwin->w_width) / width2 + 1;
1211 curwin->w_wcol -= n * width2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001212 curwin->w_wrow += n;
1213
1214#ifdef FEAT_LINEBREAK
Bram Moolenaar85a20022019-12-21 18:25:54 +01001215 // When cursor wraps to first char of next line in Insert
1216 // mode, the 'showbreak' string isn't shown, backup to first
1217 // column
Bram Moolenaar4b6172e2022-10-13 20:23:28 +01001218 char_u *sbr = get_showbreak_value(curwin);
Bram Moolenaaree857022019-11-09 23:26:40 +01001219 if (*sbr && *ml_get_cursor() == NUL
=?UTF-8?q?Dundar=20G=C3=B6c?=dfa5e462021-10-02 11:26:51 +01001220 && curwin->w_wcol == vim_strsize(sbr))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001221 curwin->w_wcol = 0;
1222#endif
1223 }
1224 }
1225
Bram Moolenaar85a20022019-12-21 18:25:54 +01001226 // No line wrapping: compute curwin->w_leftcol if scrolling is on and line
1227 // is not folded.
1228 // If scrolling is off, curwin->w_leftcol is assumed to be 0
Bram Moolenaar70b2a562012-01-10 22:26:17 +01001229 else if (may_scroll
Bram Moolenaar071d4272004-06-13 20:20:40 +00001230#ifdef FEAT_FOLDING
1231 && !curwin->w_cline_folded
1232#endif
1233 )
1234 {
Bram Moolenaarc9dc03f2022-09-12 17:51:07 +01001235#ifdef FEAT_PROP_POPUP
1236 if (curwin->w_virtcol_first_char > 0)
1237 {
1238 int cols = (curwin->w_width - extra);
1239 int rows = cols > 0 ? curwin->w_virtcol_first_char / cols : 1;
1240
1241 // each "above" text prop shifts the text one row down
1242 curwin->w_wrow += rows;
1243 curwin->w_wcol -= rows * cols;
1244 endcol -= rows * cols;
1245 curwin->w_cline_height = rows + 1;
1246 }
1247#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001248 /*
1249 * If Cursor is left of the screen, scroll rightwards.
1250 * If Cursor is right of the screen, scroll leftwards
1251 * If we get closer to the edge than 'sidescrolloff', scroll a little
1252 * extra
1253 */
Bram Moolenaar375e3392019-01-31 18:26:10 +01001254 off_left = (int)startcol - (int)curwin->w_leftcol - siso;
Bram Moolenaar02631462017-09-22 15:20:32 +02001255 off_right = (int)endcol - (int)(curwin->w_leftcol + curwin->w_width
Bram Moolenaar375e3392019-01-31 18:26:10 +01001256 - siso) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001257 if (off_left < 0 || off_right > 0)
1258 {
1259 if (off_left < 0)
1260 diff = -off_left;
1261 else
1262 diff = off_right;
1263
Bram Moolenaar85a20022019-12-21 18:25:54 +01001264 // When far off or not enough room on either side, put cursor in
1265 // middle of window.
Bram Moolenaar856c5d22022-10-13 21:54:28 +01001266 if (p_ss == 0 || diff >= width1 / 2 || off_right >= off_left)
1267 new_leftcol = curwin->w_wcol - extra - width1 / 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001268 else
1269 {
1270 if (diff < p_ss)
1271 diff = p_ss;
1272 if (off_left < 0)
1273 new_leftcol = curwin->w_leftcol - diff;
1274 else
1275 new_leftcol = curwin->w_leftcol + diff;
1276 }
1277 if (new_leftcol < 0)
1278 new_leftcol = 0;
1279 if (new_leftcol != (int)curwin->w_leftcol)
1280 {
1281 curwin->w_leftcol = new_leftcol;
Bram Moolenaar85a20022019-12-21 18:25:54 +01001282 // screen has to be redrawn with new curwin->w_leftcol
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001283 redraw_later(UPD_NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001284 }
1285 }
1286 curwin->w_wcol -= curwin->w_leftcol;
1287 }
1288 else if (curwin->w_wcol > (int)curwin->w_leftcol)
1289 curwin->w_wcol -= curwin->w_leftcol;
1290 else
1291 curwin->w_wcol = 0;
1292
1293#ifdef FEAT_DIFF
Bram Moolenaar85a20022019-12-21 18:25:54 +01001294 // Skip over filler lines. At the top use w_topfill, there
1295 // may be some filler lines above the window.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001296 if (curwin->w_cursor.lnum == curwin->w_topline)
1297 curwin->w_wrow += curwin->w_topfill;
1298 else
1299 curwin->w_wrow += diff_check_fill(curwin, curwin->w_cursor.lnum);
1300#endif
1301
1302 prev_skipcol = curwin->w_skipcol;
1303
1304 p_lines = 0;
Bram Moolenaar375e3392019-01-31 18:26:10 +01001305
Bram Moolenaar071d4272004-06-13 20:20:40 +00001306 if ((curwin->w_wrow >= curwin->w_height
1307 || ((prev_skipcol > 0
Bram Moolenaar375e3392019-01-31 18:26:10 +01001308 || curwin->w_wrow + so >= curwin->w_height)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001309 && (p_lines =
1310#ifdef FEAT_DIFF
1311 plines_win_nofill
1312#else
1313 plines_win
1314#endif
1315 (curwin, curwin->w_cursor.lnum, FALSE))
1316 - 1 >= curwin->w_height))
1317 && curwin->w_height != 0
1318 && curwin->w_cursor.lnum == curwin->w_topline
Bram Moolenaar856c5d22022-10-13 21:54:28 +01001319 && width2 > 0
Bram Moolenaar4033c552017-09-16 20:54:51 +02001320 && curwin->w_width != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001321 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01001322 // Cursor past end of screen. Happens with a single line that does
1323 // not fit on screen. Find a skipcol to show the text around the
1324 // cursor. Avoid scrolling all the time. compute value of "extra":
1325 // 1: Less than 'scrolloff' lines above
1326 // 2: Less than 'scrolloff' lines below
1327 // 3: both of them
Bram Moolenaar071d4272004-06-13 20:20:40 +00001328 extra = 0;
Bram Moolenaar856c5d22022-10-13 21:54:28 +01001329 if (curwin->w_skipcol + so * width2 > curwin->w_virtcol)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001330 extra = 1;
Bram Moolenaar85a20022019-12-21 18:25:54 +01001331 // Compute last display line of the buffer line that we want at the
1332 // bottom of the window.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001333 if (p_lines == 0)
1334 p_lines = plines_win(curwin, curwin->w_cursor.lnum, FALSE);
1335 --p_lines;
Bram Moolenaar375e3392019-01-31 18:26:10 +01001336 if (p_lines > curwin->w_wrow + so)
1337 n = curwin->w_wrow + so;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001338 else
1339 n = p_lines;
Bram Moolenaar856c5d22022-10-13 21:54:28 +01001340 if ((colnr_T)n >= curwin->w_height + curwin->w_skipcol / width2 - so)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001341 extra += 2;
1342
Bram Moolenaar4b6172e2022-10-13 20:23:28 +01001343 if (extra == 3 || curwin->w_height <= so * 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001344 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01001345 // not enough room for 'scrolloff', put cursor in the middle
Bram Moolenaar856c5d22022-10-13 21:54:28 +01001346 n = curwin->w_virtcol / width2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001347 if (n > curwin->w_height / 2)
1348 n -= curwin->w_height / 2;
1349 else
1350 n = 0;
Bram Moolenaar85a20022019-12-21 18:25:54 +01001351 // don't skip more than necessary
Bram Moolenaar071d4272004-06-13 20:20:40 +00001352 if (n > p_lines - curwin->w_height + 1)
1353 n = p_lines - curwin->w_height + 1;
Bram Moolenaar856c5d22022-10-13 21:54:28 +01001354 curwin->w_skipcol = n * width2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001355 }
1356 else if (extra == 1)
1357 {
Dominique Pelleaf4a61a2021-12-27 17:21:41 +00001358 // less than 'scrolloff' lines above, decrease skipcol
Bram Moolenaar856c5d22022-10-13 21:54:28 +01001359 extra = (curwin->w_skipcol + so * width2 - curwin->w_virtcol
1360 + width2 - 1) / width2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001361 if (extra > 0)
1362 {
Bram Moolenaar856c5d22022-10-13 21:54:28 +01001363 if ((colnr_T)(extra * width2) > curwin->w_skipcol)
1364 extra = curwin->w_skipcol / width2;
1365 curwin->w_skipcol -= extra * width2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001366 }
1367 }
1368 else if (extra == 2)
1369 {
Dominique Pelleaf4a61a2021-12-27 17:21:41 +00001370 // less than 'scrolloff' lines below, increase skipcol
Bram Moolenaar856c5d22022-10-13 21:54:28 +01001371 endcol = (n - curwin->w_height + 1) * width2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001372 while (endcol > curwin->w_virtcol)
Bram Moolenaar856c5d22022-10-13 21:54:28 +01001373 endcol -= width2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001374 if (endcol > curwin->w_skipcol)
1375 curwin->w_skipcol = endcol;
1376 }
1377
Bram Moolenaar4b6172e2022-10-13 20:23:28 +01001378 // adjust w_wrow for the changed w_skipcol
1379 if (did_sub_skipcol)
Bram Moolenaar856c5d22022-10-13 21:54:28 +01001380 curwin->w_wrow -= (curwin->w_skipcol - prev_skipcol) / width2;
Bram Moolenaar4b6172e2022-10-13 20:23:28 +01001381 else
Bram Moolenaar856c5d22022-10-13 21:54:28 +01001382 curwin->w_wrow -= curwin->w_skipcol / width2;
Bram Moolenaar4b6172e2022-10-13 20:23:28 +01001383
Bram Moolenaar071d4272004-06-13 20:20:40 +00001384 if (curwin->w_wrow >= curwin->w_height)
1385 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01001386 // small window, make sure cursor is in it
Bram Moolenaar071d4272004-06-13 20:20:40 +00001387 extra = curwin->w_wrow - curwin->w_height + 1;
Bram Moolenaar856c5d22022-10-13 21:54:28 +01001388 curwin->w_skipcol += extra * width2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001389 curwin->w_wrow -= extra;
1390 }
1391
Bram Moolenaar856c5d22022-10-13 21:54:28 +01001392 extra = ((int)prev_skipcol - (int)curwin->w_skipcol) / width2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001393 if (extra > 0)
1394 win_ins_lines(curwin, 0, extra, FALSE, FALSE);
1395 else if (extra < 0)
Bram Moolenaarcfce7172017-08-17 20:31:48 +02001396 win_del_lines(curwin, 0, -extra, FALSE, FALSE, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001397 }
Bram Moolenaarf6196f42022-10-02 21:29:55 +01001398 else if (!curwin->w_p_sms)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001399 curwin->w_skipcol = 0;
1400 if (prev_skipcol != curwin->w_skipcol)
Bram Moolenaarf32fb932022-11-17 11:34:38 +00001401 redraw_later(UPD_SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001402
Bram Moolenaar2b48ad52006-03-12 21:56:11 +00001403#ifdef FEAT_SYN_HL
zeertzjq3e559cd2022-03-27 19:26:55 +01001404 redraw_for_cursorcolumn(curwin);
Bram Moolenaarb6798752014-03-27 12:11:48 +01001405#endif
Bram Moolenaare52e0c82020-02-28 22:20:10 +01001406#if defined(FEAT_PROP_POPUP) && defined(FEAT_TERMINAL)
1407 if (popup_is_popup(curwin) && curbuf->b_term != NULL)
1408 {
1409 curwin->w_wrow += popup_top_extra(curwin);
1410 curwin->w_wcol += popup_left_extra(curwin);
Bram Moolenaar6a076442020-11-15 20:32:58 +01001411 curwin->w_flags |= WFLAG_WCOL_OFF_ADDED + WFLAG_WROW_OFF_ADDED;
Bram Moolenaare52e0c82020-02-28 22:20:10 +01001412 }
Bram Moolenaar6a076442020-11-15 20:32:58 +01001413 else
1414 curwin->w_flags &= ~(WFLAG_WCOL_OFF_ADDED + WFLAG_WROW_OFF_ADDED);
Bram Moolenaare52e0c82020-02-28 22:20:10 +01001415#endif
Bram Moolenaar2b48ad52006-03-12 21:56:11 +00001416
Bram Moolenaar2fbabd22022-10-12 19:53:38 +01001417 // now w_leftcol and w_skipcol are valid, avoid check_cursor_moved()
1418 // thinking otherwise
Bram Moolenaar08f23632019-11-16 14:19:33 +01001419 curwin->w_valid_leftcol = curwin->w_leftcol;
Bram Moolenaar2fbabd22022-10-12 19:53:38 +01001420 curwin->w_valid_skipcol = curwin->w_skipcol;
Bram Moolenaar08f23632019-11-16 14:19:33 +01001421
Bram Moolenaar071d4272004-06-13 20:20:40 +00001422 curwin->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL;
1423}
1424
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001425#if (defined(FEAT_EVAL) || defined(FEAT_PROP_POPUP)) || defined(PROTO)
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001426/*
1427 * Compute the screen position of text character at "pos" in window "wp"
1428 * The resulting values are one-based, zero when character is not visible.
1429 */
Bram Moolenaar12034e22019-08-25 22:25:02 +02001430 void
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001431textpos2screenpos(
1432 win_T *wp,
1433 pos_T *pos,
1434 int *rowp, // screen row
1435 int *scolp, // start screen column
1436 int *ccolp, // cursor screen column
1437 int *ecolp) // end screen column
1438{
1439 colnr_T scol = 0, ccol = 0, ecol = 0;
1440 int row = 0;
1441 int rowoff = 0;
1442 colnr_T coloff = 0;
1443
Bram Moolenaar189663b2021-07-21 18:04:56 +02001444 if (pos->lnum >= wp->w_topline && pos->lnum <= wp->w_botline)
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001445 {
Bram Moolenaar4556a2e2022-02-15 13:40:17 +00001446 colnr_T col;
1447 int width;
1448 linenr_T lnum = pos->lnum;
1449#ifdef FEAT_FOLDING
1450 int is_folded;
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001451
Bram Moolenaar4556a2e2022-02-15 13:40:17 +00001452 is_folded = hasFoldingWin(wp, lnum, &lnum, NULL, TRUE, NULL);
1453#endif
1454 row = plines_m_win(wp, wp->w_topline, lnum - 1) + 1;
Bram Moolenaar1cb16c32022-12-05 22:26:44 +00001455
1456#ifdef FEAT_DIFF
1457 // Add filler lines above this buffer line.
1458 row += diff_check_fill(wp, lnum);
1459#endif
1460
zeertzjqba2d1912022-12-18 12:28:59 +00001461 colnr_T off = win_col_off(wp);
Bram Moolenaar4556a2e2022-02-15 13:40:17 +00001462#ifdef FEAT_FOLDING
1463 if (is_folded)
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001464 {
Bram Moolenaar7924a172022-01-24 16:15:15 +00001465 row += W_WINROW(wp);
zeertzjqba2d1912022-12-18 12:28:59 +00001466 coloff = wp->w_wincol + 1 + off;
Bram Moolenaar7924a172022-01-24 16:15:15 +00001467 }
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001468 else
Bram Moolenaar4556a2e2022-02-15 13:40:17 +00001469#endif
1470 {
1471 getvcol(wp, pos, &scol, &ccol, &ecol);
1472
1473 // similar to what is done in validate_cursor_col()
1474 col = scol;
Bram Moolenaar4556a2e2022-02-15 13:40:17 +00001475 col += off;
1476 width = wp->w_width - off + win_col_off2(wp);
1477
1478 // long line wrapping, adjust row
1479 if (wp->w_p_wrap
1480 && col >= (colnr_T)wp->w_width
1481 && width > 0)
1482 {
1483 // use same formula as what is used in curs_columns()
1484 rowoff = ((col - wp->w_width) / width + 1);
1485 col -= rowoff * width;
1486 }
1487 col -= wp->w_leftcol;
1488 if (col >= wp->w_width)
1489 col = -1;
1490 if (col >= 0 && row + rowoff <= wp->w_height)
1491 {
1492 coloff = col - scol + wp->w_wincol + 1;
1493 row += W_WINROW(wp);
1494 }
1495 else
1496 // character is left, right or below of the window
1497 row = rowoff = scol = ccol = ecol = 0;
1498 }
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001499 }
Bram Moolenaar7924a172022-01-24 16:15:15 +00001500 *rowp = row + rowoff;
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001501 *scolp = scol + coloff;
1502 *ccolp = ccol + coloff;
1503 *ecolp = ecol + coloff;
1504}
Bram Moolenaar12034e22019-08-25 22:25:02 +02001505#endif
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001506
Bram Moolenaar12034e22019-08-25 22:25:02 +02001507#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001508/*
1509 * "screenpos({winid}, {lnum}, {col})" function
1510 */
1511 void
1512f_screenpos(typval_T *argvars UNUSED, typval_T *rettv)
1513{
1514 dict_T *dict;
1515 win_T *wp;
1516 pos_T pos;
1517 int row = 0;
1518 int scol = 0, ccol = 0, ecol = 0;
1519
Bram Moolenaar93a10962022-06-16 11:42:09 +01001520 if (rettv_dict_alloc(rettv) == FAIL)
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001521 return;
1522 dict = rettv->vval.v_dict;
1523
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001524 if (in_vim9script()
1525 && (check_for_number_arg(argvars, 0) == FAIL
1526 || check_for_number_arg(argvars, 1) == FAIL
1527 || check_for_number_arg(argvars, 2) == FAIL))
1528 return;
1529
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001530 wp = find_win_by_nr_or_id(&argvars[0]);
1531 if (wp == NULL)
1532 return;
1533
1534 pos.lnum = tv_get_number(&argvars[1]);
Bram Moolenaar99d19432022-12-05 16:23:24 +00001535 if (pos.lnum > wp->w_buffer->b_ml.ml_line_count)
1536 {
1537 semsg(_(e_invalid_line_number_nr), pos.lnum);
1538 return;
1539 }
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001540 pos.col = tv_get_number(&argvars[2]) - 1;
1541 pos.coladd = 0;
1542 textpos2screenpos(wp, &pos, &row, &scol, &ccol, &ecol);
1543
1544 dict_add_number(dict, "row", row);
1545 dict_add_number(dict, "col", scol);
1546 dict_add_number(dict, "curscol", ccol);
1547 dict_add_number(dict, "endcol", ecol);
1548}
Bram Moolenaar5a6ec102022-05-27 21:58:00 +01001549
1550/*
1551 * "virtcol2col({winid}, {lnum}, {col})" function
1552 */
1553 void
1554f_virtcol2col(typval_T *argvars UNUSED, typval_T *rettv)
1555{
1556 win_T *wp;
1557 linenr_T lnum;
1558 int screencol;
1559 int error = FALSE;
1560
1561 rettv->vval.v_number = -1;
1562
1563 if (check_for_number_arg(argvars, 0) == FAIL
1564 || check_for_number_arg(argvars, 1) == FAIL
1565 || check_for_number_arg(argvars, 2) == FAIL)
1566 return;
1567
1568 wp = find_win_by_nr_or_id(&argvars[0]);
1569 if (wp == NULL)
1570 return;
1571
1572 lnum = tv_get_number_chk(&argvars[1], &error);
1573 if (error || lnum < 0 || lnum > wp->w_buffer->b_ml.ml_line_count)
1574 return;
1575
1576 screencol = tv_get_number_chk(&argvars[2], &error);
1577 if (error || screencol < 0)
1578 return;
1579
1580 rettv->vval.v_number = vcol2col(wp, lnum, screencol);
1581}
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001582#endif
1583
Bram Moolenaar071d4272004-06-13 20:20:40 +00001584/*
1585 * Scroll the current window down by "line_count" logical lines. "CTRL-Y"
1586 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001587 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001588scrolldown(
1589 long line_count,
Bram Moolenaar85a20022019-12-21 18:25:54 +01001590 int byfold UNUSED) // TRUE: count a closed fold as one line
Bram Moolenaar071d4272004-06-13 20:20:40 +00001591{
Bram Moolenaar85a20022019-12-21 18:25:54 +01001592 long done = 0; // total # of physical lines done
Bram Moolenaar071d4272004-06-13 20:20:40 +00001593 int wrow;
1594 int moved = FALSE;
Bram Moolenaar1a58e1d2022-10-06 13:09:17 +01001595 int do_sms = curwin->w_p_wrap && curwin->w_p_sms;
Bram Moolenaarf6196f42022-10-02 21:29:55 +01001596 int width1 = 0;
1597 int width2 = 0;
1598
Bram Moolenaar1a58e1d2022-10-06 13:09:17 +01001599 if (do_sms)
Bram Moolenaarf6196f42022-10-02 21:29:55 +01001600 {
1601 width1 = curwin->w_width - curwin_col_off();
Bram Moolenaar6b2d4ff2022-10-03 14:06:02 +01001602 width2 = width1 + curwin_col_off2();
Bram Moolenaarf6196f42022-10-02 21:29:55 +01001603 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001604
1605#ifdef FEAT_FOLDING
1606 linenr_T first;
1607
Bram Moolenaar85a20022019-12-21 18:25:54 +01001608 // Make sure w_topline is at the first of a sequence of folded lines.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001609 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
1610#endif
Bram Moolenaar85a20022019-12-21 18:25:54 +01001611 validate_cursor(); // w_wrow needs to be valid
Bram Moolenaar1a58e1d2022-10-06 13:09:17 +01001612 for (int todo = line_count; todo > 0; --todo)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001613 {
1614#ifdef FEAT_DIFF
Bram Moolenaarfa316dd2009-11-03 15:23:14 +00001615 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline)
1616 && curwin->w_topfill < curwin->w_height - 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001617 {
1618 ++curwin->w_topfill;
1619 ++done;
1620 }
1621 else
1622#endif
1623 {
Bram Moolenaar8df97482022-10-03 12:11:13 +01001624 // break when at the very top
1625 if (curwin->w_topline == 1
Bram Moolenaar1a58e1d2022-10-06 13:09:17 +01001626 && (!do_sms || curwin->w_skipcol < width1))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001627 break;
Bram Moolenaar1a58e1d2022-10-06 13:09:17 +01001628 if (do_sms && curwin->w_skipcol >= width1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001629 {
Bram Moolenaar8df97482022-10-03 12:11:13 +01001630 // scroll a screen line down
Bram Moolenaarf6196f42022-10-02 21:29:55 +01001631 if (curwin->w_skipcol >= width1 + width2)
1632 curwin->w_skipcol -= width2;
1633 else
1634 curwin->w_skipcol -= width1;
1635 redraw_later(UPD_NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001636 ++done;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001637 }
1638 else
Bram Moolenaarf6196f42022-10-02 21:29:55 +01001639 {
Bram Moolenaar8df97482022-10-03 12:11:13 +01001640 // scroll a text line down
Bram Moolenaarf6196f42022-10-02 21:29:55 +01001641 --curwin->w_topline;
1642 curwin->w_skipcol = 0;
1643#ifdef FEAT_DIFF
1644 curwin->w_topfill = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001645#endif
Bram Moolenaarf6196f42022-10-02 21:29:55 +01001646#ifdef FEAT_FOLDING
1647 // A sequence of folded lines only counts for one logical line
1648 if (hasFolding(curwin->w_topline, &first, NULL))
1649 {
1650 ++done;
1651 if (!byfold)
Bram Moolenaar1a58e1d2022-10-06 13:09:17 +01001652 todo -= curwin->w_topline - first - 1;
Bram Moolenaarf6196f42022-10-02 21:29:55 +01001653 curwin->w_botline -= curwin->w_topline - first;
1654 curwin->w_topline = first;
1655 }
1656 else
1657#endif
Bram Moolenaar1a58e1d2022-10-06 13:09:17 +01001658 if (do_sms)
Bram Moolenaarf6196f42022-10-02 21:29:55 +01001659 {
1660 int size = win_linetabsize(curwin, curwin->w_topline,
1661 ml_get(curwin->w_topline), (colnr_T)MAXCOL);
1662 if (size > width1)
1663 {
1664 curwin->w_skipcol = width1;
1665 size -= width1;
1666 redraw_later(UPD_NOT_VALID);
1667 }
1668 while (size > width2)
1669 {
1670 curwin->w_skipcol += width2;
1671 size -= width2;
1672 }
1673 ++done;
1674 }
1675 else
1676 done += PLINES_NOFILL(curwin->w_topline);
1677 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001678 }
Bram Moolenaar85a20022019-12-21 18:25:54 +01001679 --curwin->w_botline; // approximate w_botline
Bram Moolenaar071d4272004-06-13 20:20:40 +00001680 invalidate_botline();
1681 }
Bram Moolenaar85a20022019-12-21 18:25:54 +01001682 curwin->w_wrow += done; // keep w_wrow updated
1683 curwin->w_cline_row += done; // keep w_cline_row updated
Bram Moolenaar071d4272004-06-13 20:20:40 +00001684
1685#ifdef FEAT_DIFF
1686 if (curwin->w_cursor.lnum == curwin->w_topline)
1687 curwin->w_cline_row = 0;
1688 check_topfill(curwin, TRUE);
1689#endif
1690
1691 /*
1692 * Compute the row number of the last row of the cursor line
1693 * and move the cursor onto the displayed part of the window.
1694 */
1695 wrow = curwin->w_wrow;
Bram Moolenaar4033c552017-09-16 20:54:51 +02001696 if (curwin->w_p_wrap && curwin->w_width != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001697 {
1698 validate_virtcol();
1699 validate_cheight();
1700 wrow += curwin->w_cline_height - 1 -
Bram Moolenaar02631462017-09-22 15:20:32 +02001701 curwin->w_virtcol / curwin->w_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001702 }
1703 while (wrow >= curwin->w_height && curwin->w_cursor.lnum > 1)
1704 {
1705#ifdef FEAT_FOLDING
1706 if (hasFolding(curwin->w_cursor.lnum, &first, NULL))
1707 {
1708 --wrow;
1709 if (first == 1)
1710 curwin->w_cursor.lnum = 1;
1711 else
1712 curwin->w_cursor.lnum = first - 1;
1713 }
1714 else
1715#endif
1716 wrow -= plines(curwin->w_cursor.lnum--);
1717 curwin->w_valid &=
1718 ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL);
1719 moved = TRUE;
1720 }
1721 if (moved)
1722 {
1723#ifdef FEAT_FOLDING
Bram Moolenaar85a20022019-12-21 18:25:54 +01001724 // Move cursor to first line of closed fold.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001725 foldAdjustCursor();
1726#endif
1727 coladvance(curwin->w_curswant);
1728 }
Bram Moolenaar8cf34592022-10-08 21:13:40 +01001729
1730 if (curwin->w_cursor.lnum == curwin->w_topline && do_sms)
1731 {
Bram Moolenaarc9121f72022-10-14 20:09:04 +01001732 long so = get_scrolloff_value();
Bram Moolenaar118c2352022-10-09 17:19:27 +01001733 int scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2;
1734
Bram Moolenaar8cf34592022-10-08 21:13:40 +01001735 // make sure the cursor is in the visible text
1736 validate_virtcol();
Bram Moolenaar118c2352022-10-09 17:19:27 +01001737 int col = curwin->w_virtcol - curwin->w_skipcol + scrolloff_cols;
Bram Moolenaar8cf34592022-10-08 21:13:40 +01001738 int row = 0;
1739 if (col >= width1)
1740 {
1741 col -= width1;
1742 ++row;
1743 }
Bram Moolenaare0f86912023-03-01 17:55:31 +00001744 if (col > width2 && width2 > 0)
Bram Moolenaar8cf34592022-10-08 21:13:40 +01001745 {
1746 row += col / width2;
1747 col = col % width2;
1748 }
1749 if (row >= curwin->w_height)
Bram Moolenaar118c2352022-10-09 17:19:27 +01001750 {
1751 curwin->w_curswant = curwin->w_virtcol
1752 - (row - curwin->w_height + 1) * width2;
1753 coladvance(curwin->w_curswant);
1754 }
Bram Moolenaar8cf34592022-10-08 21:13:40 +01001755 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001756}
1757
1758/*
1759 * Scroll the current window up by "line_count" logical lines. "CTRL-E"
1760 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001761 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001762scrollup(
1763 long line_count,
Bram Moolenaar85a20022019-12-21 18:25:54 +01001764 int byfold UNUSED) // TRUE: count a closed fold as one line
Bram Moolenaar071d4272004-06-13 20:20:40 +00001765{
Bram Moolenaar1a58e1d2022-10-06 13:09:17 +01001766 int do_sms = curwin->w_p_wrap && curwin->w_p_sms;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001767
Luuk van Baalaa6ba302023-05-09 16:01:17 +01001768 if (do_sms
1769# ifdef FEAT_FOLDING
1770 || (byfold && hasAnyFolding(curwin))
1771# endif
1772# ifdef FEAT_DIFF
1773 || (curwin->w_p_diff && !curwin->w_p_wrap)
1774# endif
1775 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001776 {
Bram Moolenaar6b2d4ff2022-10-03 14:06:02 +01001777 int width1 = curwin->w_width - curwin_col_off();
1778 int width2 = width1 + curwin_col_off2();
1779 int size = 0;
1780 linenr_T prev_topline = curwin->w_topline;
1781
Bram Moolenaar1a58e1d2022-10-06 13:09:17 +01001782 if (do_sms)
Bram Moolenaarc9121f72022-10-14 20:09:04 +01001783 size = linetabsize(curwin, curwin->w_topline);
Bram Moolenaar6b2d4ff2022-10-03 14:06:02 +01001784
1785 // diff mode: first consume "topfill"
1786 // 'smoothscroll': increase "w_skipcol" until it goes over the end of
1787 // the line, then advance to the next line.
1788 // folding: count each sequence of folded lines as one logical line.
1789 for (int todo = line_count; todo > 0; --todo)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001790 {
1791# ifdef FEAT_DIFF
1792 if (curwin->w_topfill > 0)
1793 --curwin->w_topfill;
1794 else
1795# endif
1796 {
Bram Moolenaar6b2d4ff2022-10-03 14:06:02 +01001797 linenr_T lnum = curwin->w_topline;
1798
Bram Moolenaar071d4272004-06-13 20:20:40 +00001799# ifdef FEAT_FOLDING
1800 if (byfold)
Bram Moolenaar6b2d4ff2022-10-03 14:06:02 +01001801 // for a closed fold: go to the last line in the fold
Bram Moolenaar071d4272004-06-13 20:20:40 +00001802 (void)hasFolding(lnum, NULL, &lnum);
1803# endif
Bram Moolenaar8cf34592022-10-08 21:13:40 +01001804 if (lnum == curwin->w_topline && do_sms)
Bram Moolenaarf6196f42022-10-02 21:29:55 +01001805 {
Bram Moolenaar6b2d4ff2022-10-03 14:06:02 +01001806 // 'smoothscroll': increase "w_skipcol" until it goes over
1807 // the end of the line, then advance to the next line.
1808 int add = curwin->w_skipcol > 0 ? width2 : width1;
1809 curwin->w_skipcol += add;
1810 if (curwin->w_skipcol >= size)
1811 {
1812 if (lnum == curbuf->b_ml.ml_line_count)
1813 {
1814 // at the last screen line, can't scroll further
1815 curwin->w_skipcol -= add;
1816 break;
1817 }
1818 ++lnum;
1819 }
Bram Moolenaarf6196f42022-10-02 21:29:55 +01001820 }
Bram Moolenaar6b2d4ff2022-10-03 14:06:02 +01001821 else
1822 {
1823 if (lnum >= curbuf->b_ml.ml_line_count)
1824 break;
1825 ++lnum;
1826 }
1827
1828 if (lnum > curwin->w_topline)
1829 {
1830 // approximate w_botline
1831 curwin->w_botline += lnum - curwin->w_topline;
1832 curwin->w_topline = lnum;
1833# ifdef FEAT_DIFF
1834 curwin->w_topfill = diff_check_fill(curwin, lnum);
1835# endif
1836 curwin->w_skipcol = 0;
Bram Moolenaar1a58e1d2022-10-06 13:09:17 +01001837 if (todo > 1 && do_sms)
Bram Moolenaarc9121f72022-10-14 20:09:04 +01001838 size = linetabsize(curwin, curwin->w_topline);
Bram Moolenaar6b2d4ff2022-10-03 14:06:02 +01001839 }
Bram Moolenaarf6196f42022-10-02 21:29:55 +01001840 }
1841 }
Bram Moolenaar6b2d4ff2022-10-03 14:06:02 +01001842
Bram Moolenaarf6196f42022-10-02 21:29:55 +01001843 if (curwin->w_topline == prev_topline)
1844 // need to redraw even though w_topline didn't change
1845 redraw_later(UPD_NOT_VALID);
1846 }
1847 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001848 {
1849 curwin->w_topline += line_count;
Bram Moolenaar85a20022019-12-21 18:25:54 +01001850 curwin->w_botline += line_count; // approximate w_botline
Bram Moolenaar071d4272004-06-13 20:20:40 +00001851 }
1852
1853 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1854 curwin->w_topline = curbuf->b_ml.ml_line_count;
1855 if (curwin->w_botline > curbuf->b_ml.ml_line_count + 1)
1856 curwin->w_botline = curbuf->b_ml.ml_line_count + 1;
1857
1858#ifdef FEAT_DIFF
1859 check_topfill(curwin, FALSE);
1860#endif
1861
1862#ifdef FEAT_FOLDING
1863 if (hasAnyFolding(curwin))
Bram Moolenaar85a20022019-12-21 18:25:54 +01001864 // Make sure w_topline is at the first of a sequence of folded lines.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001865 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
1866#endif
1867
1868 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
1869 if (curwin->w_cursor.lnum < curwin->w_topline)
1870 {
1871 curwin->w_cursor.lnum = curwin->w_topline;
1872 curwin->w_valid &=
1873 ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL);
1874 coladvance(curwin->w_curswant);
1875 }
Bram Moolenaar8cf34592022-10-08 21:13:40 +01001876 if (curwin->w_cursor.lnum == curwin->w_topline
1877 && do_sms && curwin->w_skipcol > 0)
1878 {
Yee Cheng Chin81ba26e2022-11-18 12:52:50 +00001879 int col_off = curwin_col_off();
1880 int col_off2 = curwin_col_off2();
1881
1882 int width1 = curwin->w_width - col_off;
1883 int width2 = width1 + col_off2;
1884 int extra2 = col_off - col_off2;
Bram Moolenaarc9121f72022-10-14 20:09:04 +01001885 long so = get_scrolloff_value();
Bram Moolenaar118c2352022-10-09 17:19:27 +01001886 int scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2;
Bram Moolenaarc9121f72022-10-14 20:09:04 +01001887 int space_cols = (curwin->w_height - 1) * width2;
Bram Moolenaar118c2352022-10-09 17:19:27 +01001888
Luuk van Baal24b62ec2023-05-13 14:12:15 +01001889 // If we have non-zero scrolloff, just ignore the marker as we are
Yee Cheng Chin81ba26e2022-11-18 12:52:50 +00001890 // going past it anyway.
Luuk van Baal24b62ec2023-05-13 14:12:15 +01001891 int overlap = scrolloff_cols != 0 ? 0
1892 : sms_marker_overlap(curwin, extra2);
Yee Cheng Chin81ba26e2022-11-18 12:52:50 +00001893
Bram Moolenaar118c2352022-10-09 17:19:27 +01001894 // Make sure the cursor is in a visible part of the line, taking
1895 // 'scrolloff' into account, but using screen lines.
Bram Moolenaarc9121f72022-10-14 20:09:04 +01001896 // If there are not enough screen lines put the cursor in the middle.
1897 if (scrolloff_cols > space_cols / 2)
1898 scrolloff_cols = space_cols / 2;
Bram Moolenaar8cf34592022-10-08 21:13:40 +01001899 validate_virtcol();
Luuk van Baal24b62ec2023-05-13 14:12:15 +01001900 if (curwin->w_virtcol < curwin->w_skipcol + overlap + scrolloff_cols)
Bram Moolenaar8cf34592022-10-08 21:13:40 +01001901 {
Bram Moolenaar8cf34592022-10-08 21:13:40 +01001902 colnr_T col = curwin->w_virtcol;
1903
1904 if (col < width1)
1905 col += width1;
Luuk van Baal24b62ec2023-05-13 14:12:15 +01001906 while (col < curwin->w_skipcol + overlap + scrolloff_cols)
Bram Moolenaar8cf34592022-10-08 21:13:40 +01001907 col += width2;
Bram Moolenaar118c2352022-10-09 17:19:27 +01001908 curwin->w_curswant = col;
1909 coladvance(curwin->w_curswant);
Bram Moolenaar2fbabd22022-10-12 19:53:38 +01001910
1911 // validate_virtcol() marked various things as valid, but after
1912 // moving the cursor they need to be recomputed
1913 curwin->w_valid &=
1914 ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL);
Bram Moolenaar8cf34592022-10-08 21:13:40 +01001915 }
1916 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001917}
1918
Bram Moolenaar2fbabd22022-10-12 19:53:38 +01001919/*
1920 * Called after changing the cursor column: make sure that curwin->w_skipcol is
1921 * valid for 'smoothscroll'.
1922 */
1923 void
1924adjust_skipcol(void)
1925{
1926 if (!curwin->w_p_wrap
1927 || !curwin->w_p_sms
1928 || curwin->w_cursor.lnum != curwin->w_topline)
1929 return;
1930
1931 int width1 = curwin->w_width - curwin_col_off();
Bram Moolenaar870219c2023-01-26 14:14:43 +00001932 if (width1 <= 0)
1933 return; // no text will be displayed
1934
Bram Moolenaar2fbabd22022-10-12 19:53:38 +01001935 int width2 = width1 + curwin_col_off2();
Bram Moolenaarc9121f72022-10-14 20:09:04 +01001936 long so = get_scrolloff_value();
Bram Moolenaar2fbabd22022-10-12 19:53:38 +01001937 int scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2;
1938 int scrolled = FALSE;
1939
Bram Moolenaard5337ef2022-10-20 20:15:47 +01001940 validate_cheight();
Bram Moolenaarb21b8e92022-12-03 18:35:07 +00001941 if (curwin->w_cline_height == curwin->w_height
1942 // w_cline_height may be capped at w_height, check there aren't
1943 // actually more lines.
1944 && plines_win(curwin, curwin->w_cursor.lnum, FALSE)
1945 <= curwin->w_height)
Bram Moolenaarc9121f72022-10-14 20:09:04 +01001946 {
1947 // the line just fits in the window, don't scroll
Bram Moolenaarf32fb932022-11-17 11:34:38 +00001948 reset_skipcol();
Bram Moolenaarc9121f72022-10-14 20:09:04 +01001949 return;
1950 }
1951
Bram Moolenaard5337ef2022-10-20 20:15:47 +01001952 validate_virtcol();
Luuk van Baal24b62ec2023-05-13 14:12:15 +01001953 int overlap = sms_marker_overlap(curwin,
1954 curwin_col_off() - curwin_col_off2());
Bram Moolenaar2fbabd22022-10-12 19:53:38 +01001955 while (curwin->w_skipcol > 0
Luuk van Baal24b62ec2023-05-13 14:12:15 +01001956 && curwin->w_virtcol < curwin->w_skipcol + overlap + scrolloff_cols)
Bram Moolenaar2fbabd22022-10-12 19:53:38 +01001957 {
1958 // scroll a screen line down
1959 if (curwin->w_skipcol >= width1 + width2)
1960 curwin->w_skipcol -= width2;
1961 else
1962 curwin->w_skipcol -= width1;
1963 redraw_later(UPD_NOT_VALID);
1964 scrolled = TRUE;
1965 validate_virtcol();
1966 }
1967 if (scrolled)
1968 return; // don't scroll in the other direction now
1969
1970 int col = curwin->w_virtcol - curwin->w_skipcol + scrolloff_cols;
1971 int row = 0;
1972 if (col >= width1)
1973 {
1974 col -= width1;
1975 ++row;
1976 }
1977 if (col > width2)
1978 {
1979 row += col / width2;
1980 col = col % width2;
1981 }
1982 if (row >= curwin->w_height)
1983 {
1984 if (curwin->w_skipcol == 0)
1985 {
1986 curwin->w_skipcol += width1;
1987 --row;
1988 }
1989 if (row >= curwin->w_height)
1990 curwin->w_skipcol += (row - curwin->w_height) * width2;
1991 redraw_later(UPD_NOT_VALID);
1992 }
1993}
1994
Bram Moolenaar071d4272004-06-13 20:20:40 +00001995#ifdef FEAT_DIFF
1996/*
1997 * Don't end up with too many filler lines in the window.
1998 */
1999 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002000check_topfill(
2001 win_T *wp,
Bram Moolenaar85a20022019-12-21 18:25:54 +01002002 int down) // when TRUE scroll down when not enough space
Bram Moolenaar071d4272004-06-13 20:20:40 +00002003{
2004 int n;
2005
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002006 if (wp->w_topfill <= 0)
2007 return;
2008
2009 n = plines_win_nofill(wp, wp->w_topline, TRUE);
2010 if (wp->w_topfill + n > wp->w_height)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002011 {
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002012 if (down && wp->w_topline > 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002013 {
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002014 --wp->w_topline;
2015 wp->w_topfill = 0;
2016 }
2017 else
2018 {
2019 wp->w_topfill = wp->w_height - n;
2020 if (wp->w_topfill < 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002021 wp->w_topfill = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002022 }
2023 }
2024}
2025
2026/*
2027 * Use as many filler lines as possible for w_topline. Make sure w_topline
2028 * is still visible.
2029 */
2030 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002031max_topfill(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002032{
2033 int n;
2034
2035 n = plines_nofill(curwin->w_topline);
2036 if (n >= curwin->w_height)
2037 curwin->w_topfill = 0;
2038 else
2039 {
2040 curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline);
2041 if (curwin->w_topfill + n > curwin->w_height)
2042 curwin->w_topfill = curwin->w_height - n;
2043 }
2044}
2045#endif
2046
Bram Moolenaar071d4272004-06-13 20:20:40 +00002047/*
2048 * Scroll the screen one line down, but don't do it if it would move the
2049 * cursor off the screen.
2050 */
2051 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002052scrolldown_clamp(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002053{
2054 int end_row;
2055#ifdef FEAT_DIFF
2056 int can_fill = (curwin->w_topfill
2057 < diff_check_fill(curwin, curwin->w_topline));
2058#endif
2059
2060 if (curwin->w_topline <= 1
2061#ifdef FEAT_DIFF
2062 && !can_fill
2063#endif
2064 )
2065 return;
2066
Bram Moolenaar85a20022019-12-21 18:25:54 +01002067 validate_cursor(); // w_wrow needs to be valid
Bram Moolenaar071d4272004-06-13 20:20:40 +00002068
2069 /*
2070 * Compute the row number of the last row of the cursor line
2071 * and make sure it doesn't go off the screen. Make sure the cursor
2072 * doesn't go past 'scrolloff' lines from the screen end.
2073 */
2074 end_row = curwin->w_wrow;
2075#ifdef FEAT_DIFF
2076 if (can_fill)
2077 ++end_row;
2078 else
2079 end_row += plines_nofill(curwin->w_topline - 1);
2080#else
2081 end_row += plines(curwin->w_topline - 1);
2082#endif
Bram Moolenaar4033c552017-09-16 20:54:51 +02002083 if (curwin->w_p_wrap && curwin->w_width != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002084 {
2085 validate_cheight();
2086 validate_virtcol();
2087 end_row += curwin->w_cline_height - 1 -
Bram Moolenaar02631462017-09-22 15:20:32 +02002088 curwin->w_virtcol / curwin->w_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002089 }
Bram Moolenaar375e3392019-01-31 18:26:10 +01002090 if (end_row < curwin->w_height - get_scrolloff_value())
Bram Moolenaar071d4272004-06-13 20:20:40 +00002091 {
2092#ifdef FEAT_DIFF
2093 if (can_fill)
2094 {
2095 ++curwin->w_topfill;
2096 check_topfill(curwin, TRUE);
2097 }
2098 else
2099 {
2100 --curwin->w_topline;
2101 curwin->w_topfill = 0;
2102 }
2103#else
2104 --curwin->w_topline;
2105#endif
2106#ifdef FEAT_FOLDING
Bram Moolenaarcde88542015-08-11 19:14:00 +02002107 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002108#endif
Bram Moolenaar85a20022019-12-21 18:25:54 +01002109 --curwin->w_botline; // approximate w_botline
Bram Moolenaar071d4272004-06-13 20:20:40 +00002110 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
2111 }
2112}
2113
2114/*
2115 * Scroll the screen one line up, but don't do it if it would move the cursor
2116 * off the screen.
2117 */
2118 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002119scrollup_clamp(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002120{
2121 int start_row;
2122
2123 if (curwin->w_topline == curbuf->b_ml.ml_line_count
2124#ifdef FEAT_DIFF
2125 && curwin->w_topfill == 0
2126#endif
2127 )
2128 return;
2129
Bram Moolenaar85a20022019-12-21 18:25:54 +01002130 validate_cursor(); // w_wrow needs to be valid
Bram Moolenaar071d4272004-06-13 20:20:40 +00002131
2132 /*
2133 * Compute the row number of the first row of the cursor line
2134 * and make sure it doesn't go off the screen. Make sure the cursor
2135 * doesn't go before 'scrolloff' lines from the screen start.
2136 */
2137#ifdef FEAT_DIFF
2138 start_row = curwin->w_wrow - plines_nofill(curwin->w_topline)
2139 - curwin->w_topfill;
2140#else
2141 start_row = curwin->w_wrow - plines(curwin->w_topline);
2142#endif
Bram Moolenaar4033c552017-09-16 20:54:51 +02002143 if (curwin->w_p_wrap && curwin->w_width != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002144 {
2145 validate_virtcol();
Bram Moolenaar02631462017-09-22 15:20:32 +02002146 start_row -= curwin->w_virtcol / curwin->w_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002147 }
Bram Moolenaar375e3392019-01-31 18:26:10 +01002148 if (start_row >= get_scrolloff_value())
Bram Moolenaar071d4272004-06-13 20:20:40 +00002149 {
2150#ifdef FEAT_DIFF
2151 if (curwin->w_topfill > 0)
2152 --curwin->w_topfill;
2153 else
2154#endif
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002155 {
2156#ifdef FEAT_FOLDING
2157 (void)hasFolding(curwin->w_topline, NULL, &curwin->w_topline);
2158#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002159 ++curwin->w_topline;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002160 }
Bram Moolenaar85a20022019-12-21 18:25:54 +01002161 ++curwin->w_botline; // approximate w_botline
Bram Moolenaar071d4272004-06-13 20:20:40 +00002162 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
2163 }
2164}
Bram Moolenaar071d4272004-06-13 20:20:40 +00002165
2166/*
2167 * Add one line above "lp->lnum". This can be a filler line, a closed fold or
2168 * a (wrapped) text line. Uses and sets "lp->fill".
2169 * Returns the height of the added line in "lp->height".
Bram Moolenaarbacd9da2010-02-17 18:20:37 +01002170 * Lines above the first one are incredibly high: MAXCOL.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002171 */
2172 static void
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002173topline_back_winheight(
2174 lineoff_T *lp,
2175 int winheight) // when TRUE limit to window height
Bram Moolenaar071d4272004-06-13 20:20:40 +00002176{
2177#ifdef FEAT_DIFF
2178 if (lp->fill < diff_check_fill(curwin, lp->lnum))
2179 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01002180 // Add a filler line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002181 ++lp->fill;
2182 lp->height = 1;
2183 }
2184 else
2185#endif
2186 {
2187 --lp->lnum;
2188#ifdef FEAT_DIFF
2189 lp->fill = 0;
2190#endif
2191 if (lp->lnum < 1)
2192 lp->height = MAXCOL;
2193 else
2194#ifdef FEAT_FOLDING
2195 if (hasFolding(lp->lnum, &lp->lnum, NULL))
Bram Moolenaar85a20022019-12-21 18:25:54 +01002196 // Add a closed fold
Bram Moolenaar071d4272004-06-13 20:20:40 +00002197 lp->height = 1;
2198 else
2199#endif
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002200 lp->height = PLINES_WIN_NOFILL(curwin, lp->lnum, winheight);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002201 }
2202}
2203
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002204 static void
2205topline_back(lineoff_T *lp)
2206{
2207 topline_back_winheight(lp, TRUE);
2208}
2209
2210
Bram Moolenaar071d4272004-06-13 20:20:40 +00002211/*
2212 * Add one line below "lp->lnum". This can be a filler line, a closed fold or
2213 * a (wrapped) text line. Uses and sets "lp->fill".
2214 * Returns the height of the added line in "lp->height".
2215 * Lines below the last one are incredibly high.
2216 */
2217 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002218botline_forw(lineoff_T *lp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002219{
2220#ifdef FEAT_DIFF
2221 if (lp->fill < diff_check_fill(curwin, lp->lnum + 1))
2222 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01002223 // Add a filler line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002224 ++lp->fill;
2225 lp->height = 1;
2226 }
2227 else
2228#endif
2229 {
2230 ++lp->lnum;
2231#ifdef FEAT_DIFF
2232 lp->fill = 0;
2233#endif
2234 if (lp->lnum > curbuf->b_ml.ml_line_count)
2235 lp->height = MAXCOL;
2236 else
2237#ifdef FEAT_FOLDING
2238 if (hasFolding(lp->lnum, NULL, &lp->lnum))
Bram Moolenaar85a20022019-12-21 18:25:54 +01002239 // Add a closed fold
Bram Moolenaar071d4272004-06-13 20:20:40 +00002240 lp->height = 1;
2241 else
2242#endif
Bram Moolenaar43335ea2015-09-09 20:59:37 +02002243 lp->height = PLINES_NOFILL(lp->lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002244 }
2245}
2246
2247#ifdef FEAT_DIFF
2248/*
2249 * Switch from including filler lines below lp->lnum to including filler
2250 * lines above loff.lnum + 1. This keeps pointing to the same line.
2251 * When there are no filler lines nothing changes.
2252 */
2253 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002254botline_topline(lineoff_T *lp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002255{
2256 if (lp->fill > 0)
2257 {
2258 ++lp->lnum;
2259 lp->fill = diff_check_fill(curwin, lp->lnum) - lp->fill + 1;
2260 }
2261}
2262
2263/*
2264 * Switch from including filler lines above lp->lnum to including filler
2265 * lines below loff.lnum - 1. This keeps pointing to the same line.
2266 * When there are no filler lines nothing changes.
2267 */
2268 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002269topline_botline(lineoff_T *lp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002270{
2271 if (lp->fill > 0)
2272 {
2273 lp->fill = diff_check_fill(curwin, lp->lnum) - lp->fill + 1;
2274 --lp->lnum;
2275 }
2276}
2277#endif
2278
2279/*
2280 * Recompute topline to put the cursor at the top of the window.
2281 * Scroll at least "min_scroll" lines.
2282 * If "always" is TRUE, always set topline (for "zt").
2283 */
2284 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002285scroll_cursor_top(int min_scroll, int always)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002286{
2287 int scrolled = 0;
2288 int extra = 0;
2289 int used;
2290 int i;
Bram Moolenaar85a20022019-12-21 18:25:54 +01002291 linenr_T top; // just above displayed lines
2292 linenr_T bot; // just below displayed lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00002293 linenr_T old_topline = curwin->w_topline;
Bram Moolenaar46b54742022-10-06 15:46:49 +01002294 int old_skipcol = curwin->w_skipcol;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002295#ifdef FEAT_DIFF
2296 linenr_T old_topfill = curwin->w_topfill;
2297#endif
2298 linenr_T new_topline;
Bram Moolenaar375e3392019-01-31 18:26:10 +01002299 int off = get_scrolloff_value();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002300
Bram Moolenaar071d4272004-06-13 20:20:40 +00002301 if (mouse_dragging > 0)
2302 off = mouse_dragging - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002303
2304 /*
2305 * Decrease topline until:
2306 * - it has become 1
2307 * - (part of) the cursor line is moved off the screen or
2308 * - moved at least 'scrolljump' lines and
2309 * - at least 'scrolloff' lines above and below the cursor
2310 */
2311 validate_cheight();
Bram Moolenaar85a20022019-12-21 18:25:54 +01002312 used = curwin->w_cline_height; // includes filler lines above
Bram Moolenaar071d4272004-06-13 20:20:40 +00002313 if (curwin->w_cursor.lnum < curwin->w_topline)
2314 scrolled = used;
2315
2316#ifdef FEAT_FOLDING
2317 if (hasFolding(curwin->w_cursor.lnum, &top, &bot))
2318 {
2319 --top;
2320 ++bot;
2321 }
2322 else
2323#endif
2324 {
2325 top = curwin->w_cursor.lnum - 1;
2326 bot = curwin->w_cursor.lnum + 1;
2327 }
2328 new_topline = top + 1;
2329
2330#ifdef FEAT_DIFF
Bram Moolenaar85a20022019-12-21 18:25:54 +01002331 // "used" already contains the number of filler lines above, don't add it
2332 // again.
2333 // Hide filler lines above cursor line by adding them to "extra".
Bram Moolenaara09a2c52015-09-08 17:31:59 +02002334 extra += diff_check_fill(curwin, curwin->w_cursor.lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002335#endif
2336
2337 /*
2338 * Check if the lines from "top" to "bot" fit in the window. If they do,
2339 * set new_topline and advance "top" and "bot" to include more lines.
2340 */
2341 while (top > 0)
2342 {
2343#ifdef FEAT_FOLDING
2344 if (hasFolding(top, &top, NULL))
Bram Moolenaar85a20022019-12-21 18:25:54 +01002345 // count one logical line for a sequence of folded lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00002346 i = 1;
2347 else
2348#endif
Bram Moolenaar43335ea2015-09-09 20:59:37 +02002349 i = PLINES_NOFILL(top);
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002350 if (top < curwin->w_topline)
2351 scrolled += i;
2352
2353 // If scrolling is needed, scroll at least 'sj' lines.
2354 if ((new_topline >= curwin->w_topline || scrolled > min_scroll)
2355 && extra >= off)
2356 break;
2357
Bram Moolenaar071d4272004-06-13 20:20:40 +00002358 used += i;
2359 if (extra + i <= off && bot < curbuf->b_ml.ml_line_count)
2360 {
2361#ifdef FEAT_FOLDING
2362 if (hasFolding(bot, NULL, &bot))
Bram Moolenaar85a20022019-12-21 18:25:54 +01002363 // count one logical line for a sequence of folded lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00002364 ++used;
2365 else
2366#endif
2367 used += plines(bot);
2368 }
2369 if (used > curwin->w_height)
2370 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002371
2372 extra += i;
2373 new_topline = top;
2374 --top;
2375 ++bot;
2376 }
2377
2378 /*
2379 * If we don't have enough space, put cursor in the middle.
2380 * This makes sure we get the same position when using "k" and "j"
2381 * in a small window.
2382 */
2383 if (used > curwin->w_height)
Bram Moolenaar1d6539c2023-02-14 17:41:20 +00002384 scroll_cursor_halfway(FALSE, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002385 else
2386 {
2387 /*
2388 * If "always" is FALSE, only adjust topline to a lower value, higher
Bram Moolenaar1b73edd2022-12-03 11:51:54 +00002389 * value may happen with wrapping lines.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002390 */
2391 if (new_topline < curwin->w_topline || always)
2392 curwin->w_topline = new_topline;
2393 if (curwin->w_topline > curwin->w_cursor.lnum)
2394 curwin->w_topline = curwin->w_cursor.lnum;
2395#ifdef FEAT_DIFF
2396 curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline);
2397 if (curwin->w_topfill > 0 && extra > off)
2398 {
2399 curwin->w_topfill -= extra - off;
2400 if (curwin->w_topfill < 0)
2401 curwin->w_topfill = 0;
2402 }
2403 check_topfill(curwin, FALSE);
2404#endif
Bram Moolenaar46b54742022-10-06 15:46:49 +01002405 // TODO: if the line doesn't fit may optimize w_skipcol
Bram Moolenaar1b73edd2022-12-03 11:51:54 +00002406 if (curwin->w_topline == curwin->w_cursor.lnum
2407 && curwin->w_skipcol >= curwin->w_cursor.col)
Bram Moolenaarf32fb932022-11-17 11:34:38 +00002408 reset_skipcol();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002409 if (curwin->w_topline != old_topline
Bram Moolenaar46b54742022-10-06 15:46:49 +01002410 || curwin->w_skipcol != old_skipcol
Bram Moolenaar071d4272004-06-13 20:20:40 +00002411#ifdef FEAT_DIFF
2412 || curwin->w_topfill != old_topfill
2413#endif
2414 )
2415 curwin->w_valid &=
2416 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
2417 curwin->w_valid |= VALID_TOPLINE;
2418 }
2419}
2420
2421/*
2422 * Set w_empty_rows and w_filler_rows for window "wp", having used up "used"
2423 * screen lines for text lines.
2424 */
2425 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002426set_empty_rows(win_T *wp, int used)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002427{
2428#ifdef FEAT_DIFF
2429 wp->w_filler_rows = 0;
2430#endif
2431 if (used == 0)
Bram Moolenaar85a20022019-12-21 18:25:54 +01002432 wp->w_empty_rows = 0; // single line that doesn't fit
Bram Moolenaar071d4272004-06-13 20:20:40 +00002433 else
2434 {
2435 wp->w_empty_rows = wp->w_height - used;
2436#ifdef FEAT_DIFF
2437 if (wp->w_botline <= wp->w_buffer->b_ml.ml_line_count)
2438 {
2439 wp->w_filler_rows = diff_check_fill(wp, wp->w_botline);
2440 if (wp->w_empty_rows > wp->w_filler_rows)
2441 wp->w_empty_rows -= wp->w_filler_rows;
2442 else
2443 {
2444 wp->w_filler_rows = wp->w_empty_rows;
2445 wp->w_empty_rows = 0;
2446 }
2447 }
2448#endif
2449 }
2450}
2451
2452/*
2453 * Recompute topline to put the cursor at the bottom of the window.
Bram Moolenaar8088ae92022-06-20 11:38:17 +01002454 * When scrolling scroll at least "min_scroll" lines.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002455 * If "set_topbot" is TRUE, set topline and botline first (for "zb").
2456 * This is messy stuff!!!
2457 */
2458 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002459scroll_cursor_bot(int min_scroll, int set_topbot)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002460{
2461 int used;
2462 int scrolled = 0;
2463 int extra = 0;
2464 int i;
2465 linenr_T line_count;
2466 linenr_T old_topline = curwin->w_topline;
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002467 int old_skipcol = curwin->w_skipcol;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002468 lineoff_T loff;
2469 lineoff_T boff;
2470#ifdef FEAT_DIFF
2471 int old_topfill = curwin->w_topfill;
2472 int fill_below_window;
2473#endif
2474 linenr_T old_botline = curwin->w_botline;
2475 linenr_T old_valid = curwin->w_valid;
2476 int old_empty_rows = curwin->w_empty_rows;
Bram Moolenaar85a20022019-12-21 18:25:54 +01002477 linenr_T cln; // Cursor Line Number
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002478 long so = get_scrolloff_value();
Luuk van Baalaa6ba302023-05-09 16:01:17 +01002479 int do_sms = curwin->w_p_wrap && curwin->w_p_sms;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002480
2481 cln = curwin->w_cursor.lnum;
2482 if (set_topbot)
2483 {
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002484 int set_skipcol = FALSE;
2485
Bram Moolenaar071d4272004-06-13 20:20:40 +00002486 used = 0;
2487 curwin->w_botline = cln + 1;
2488#ifdef FEAT_DIFF
2489 loff.fill = 0;
2490#endif
2491 for (curwin->w_topline = curwin->w_botline;
2492 curwin->w_topline > 1;
2493 curwin->w_topline = loff.lnum)
2494 {
2495 loff.lnum = curwin->w_topline;
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002496 topline_back_winheight(&loff, FALSE);
2497 if (loff.height == MAXCOL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002498 break;
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002499 if (used + loff.height > curwin->w_height)
2500 {
Luuk van Baalaa6ba302023-05-09 16:01:17 +01002501 if (do_sms)
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002502 {
2503 // 'smoothscroll' and 'wrap' are set. The above line is
2504 // too long to show in its entirety, so we show just a part
2505 // of it.
2506 if (used < curwin->w_height)
2507 {
2508 int plines_offset = used + loff.height
2509 - curwin->w_height;
2510 used = curwin->w_height;
2511#ifdef FEAT_DIFF
2512 curwin->w_topfill = loff.fill;
2513#endif
2514 curwin->w_topline = loff.lnum;
2515 curwin->w_skipcol = skipcol_from_plines(
2516 curwin, plines_offset);
2517 set_skipcol = TRUE;
2518 }
2519 }
2520 break;
2521 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002522 used += loff.height;
2523#ifdef FEAT_DIFF
2524 curwin->w_topfill = loff.fill;
2525#endif
2526 }
2527 set_empty_rows(curwin, used);
2528 curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
2529 if (curwin->w_topline != old_topline
2530#ifdef FEAT_DIFF
2531 || curwin->w_topfill != old_topfill
2532#endif
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002533 || set_skipcol
2534 || curwin->w_skipcol != 0)
2535 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002536 curwin->w_valid &= ~(VALID_WROW|VALID_CROW);
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002537 if (set_skipcol)
2538 redraw_later(UPD_NOT_VALID);
2539 else
2540 reset_skipcol();
2541 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002542 }
2543 else
2544 validate_botline();
2545
Bram Moolenaar85a20022019-12-21 18:25:54 +01002546 // The lines of the cursor line itself are always used.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002547#ifdef FEAT_DIFF
2548 used = plines_nofill(cln);
2549#else
2550 validate_cheight();
2551 used = curwin->w_cline_height;
2552#endif
2553
Yee Cheng Chin361895d2022-11-19 12:25:16 +00002554 // If the cursor is on or below botline, we will at least scroll by the
2555 // height of the cursor line, which is "used". Correct for empty lines,
2556 // which are really part of botline.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002557 if (cln >= curwin->w_botline)
2558 {
2559 scrolled = used;
2560 if (cln == curwin->w_botline)
2561 scrolled -= curwin->w_empty_rows;
Luuk van Baalaa6ba302023-05-09 16:01:17 +01002562 if (do_sms)
Yee Cheng Chin361895d2022-11-19 12:25:16 +00002563 {
Luuk van Baalaa6ba302023-05-09 16:01:17 +01002564 // 'smoothscroll' and 'wrap' are set.
Yee Cheng Chin361895d2022-11-19 12:25:16 +00002565 // Calculate how many screen lines the current top line of window
2566 // occupies. If it is occupying more than the entire window, we
2567 // need to scroll the additional clipped lines to scroll past the
2568 // top line before we can move on to the other lines.
2569 int top_plines =
2570#ifdef FEAT_DIFF
2571 plines_win_nofill
2572#else
2573 plines_win
2574#endif
2575 (curwin, curwin->w_topline, FALSE);
2576 int skip_lines = 0;
2577 int width1 = curwin->w_width - curwin_col_off();
2578 int width2 = width1 + curwin_col_off2();
2579 // similar formula is used in curs_columns()
2580 if (curwin->w_skipcol > width1)
2581 skip_lines += (curwin->w_skipcol - width1) / width2 + 1;
2582 else if (curwin->w_skipcol > 0)
2583 skip_lines = 1;
2584
2585 top_plines -= skip_lines;
2586 if (top_plines > curwin->w_height)
2587 {
2588 scrolled += (top_plines - curwin->w_height);
Yee Cheng Chin361895d2022-11-19 12:25:16 +00002589 }
2590 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002591 }
2592
2593 /*
2594 * Stop counting lines to scroll when
2595 * - hitting start of the file
2596 * - scrolled nothing or at least 'sj' lines
Bram Moolenaar375e3392019-01-31 18:26:10 +01002597 * - at least 'scrolloff' lines below the cursor
Bram Moolenaar071d4272004-06-13 20:20:40 +00002598 * - lines between botline and cursor have been counted
2599 */
2600#ifdef FEAT_FOLDING
2601 if (!hasFolding(curwin->w_cursor.lnum, &loff.lnum, &boff.lnum))
2602#endif
2603 {
2604 loff.lnum = cln;
2605 boff.lnum = cln;
2606 }
2607#ifdef FEAT_DIFF
2608 loff.fill = 0;
2609 boff.fill = 0;
2610 fill_below_window = diff_check_fill(curwin, curwin->w_botline)
2611 - curwin->w_filler_rows;
2612#endif
2613
2614 while (loff.lnum > 1)
2615 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01002616 // Stop when scrolled nothing or at least "min_scroll", found "extra"
2617 // context for 'scrolloff' and counted all lines below the window.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002618 if ((((scrolled <= 0 || scrolled >= min_scroll)
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02002619 && extra >= (mouse_dragging > 0 ? mouse_dragging - 1 : so))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002620 || boff.lnum + 1 > curbuf->b_ml.ml_line_count)
2621 && loff.lnum <= curwin->w_botline
2622#ifdef FEAT_DIFF
2623 && (loff.lnum < curwin->w_botline
2624 || loff.fill >= fill_below_window)
2625#endif
2626 )
2627 break;
2628
Bram Moolenaar85a20022019-12-21 18:25:54 +01002629 // Add one line above
Bram Moolenaar071d4272004-06-13 20:20:40 +00002630 topline_back(&loff);
Bram Moolenaarbacd9da2010-02-17 18:20:37 +01002631 if (loff.height == MAXCOL)
2632 used = MAXCOL;
2633 else
2634 used += loff.height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002635 if (used > curwin->w_height)
2636 break;
2637 if (loff.lnum >= curwin->w_botline
2638#ifdef FEAT_DIFF
2639 && (loff.lnum > curwin->w_botline
2640 || loff.fill <= fill_below_window)
2641#endif
2642 )
2643 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01002644 // Count screen lines that are below the window.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002645 scrolled += loff.height;
2646 if (loff.lnum == curwin->w_botline
2647#ifdef FEAT_DIFF
Bram Moolenaar4e303c82018-11-24 14:27:44 +01002648 && loff.fill == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002649#endif
2650 )
2651 scrolled -= curwin->w_empty_rows;
2652 }
2653
2654 if (boff.lnum < curbuf->b_ml.ml_line_count)
2655 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01002656 // Add one line below
Bram Moolenaar071d4272004-06-13 20:20:40 +00002657 botline_forw(&boff);
2658 used += boff.height;
2659 if (used > curwin->w_height)
2660 break;
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02002661 if (extra < ( mouse_dragging > 0 ? mouse_dragging - 1 : so)
2662 || scrolled < min_scroll)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002663 {
2664 extra += boff.height;
2665 if (boff.lnum >= curwin->w_botline
2666#ifdef FEAT_DIFF
2667 || (boff.lnum + 1 == curwin->w_botline
2668 && boff.fill > curwin->w_filler_rows)
2669#endif
2670 )
2671 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01002672 // Count screen lines that are below the window.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002673 scrolled += boff.height;
2674 if (boff.lnum == curwin->w_botline
2675#ifdef FEAT_DIFF
2676 && boff.fill == 0
2677#endif
2678 )
2679 scrolled -= curwin->w_empty_rows;
2680 }
2681 }
2682 }
2683 }
2684
Bram Moolenaar85a20022019-12-21 18:25:54 +01002685 // curwin->w_empty_rows is larger, no need to scroll
Bram Moolenaar071d4272004-06-13 20:20:40 +00002686 if (scrolled <= 0)
2687 line_count = 0;
Bram Moolenaar85a20022019-12-21 18:25:54 +01002688 // more than a screenfull, don't scroll but redraw
Bram Moolenaar071d4272004-06-13 20:20:40 +00002689 else if (used > curwin->w_height)
2690 line_count = used;
Bram Moolenaar85a20022019-12-21 18:25:54 +01002691 // scroll minimal number of lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00002692 else
2693 {
2694 line_count = 0;
2695#ifdef FEAT_DIFF
2696 boff.fill = curwin->w_topfill;
2697#endif
2698 boff.lnum = curwin->w_topline - 1;
2699 for (i = 0; i < scrolled && boff.lnum < curwin->w_botline; )
2700 {
2701 botline_forw(&boff);
2702 i += boff.height;
2703 ++line_count;
2704 }
Bram Moolenaar85a20022019-12-21 18:25:54 +01002705 if (i < scrolled) // below curwin->w_botline, don't scroll
Bram Moolenaar071d4272004-06-13 20:20:40 +00002706 line_count = 9999;
2707 }
2708
2709 /*
2710 * Scroll up if the cursor is off the bottom of the screen a bit.
2711 * Otherwise put it at 1/2 of the screen.
2712 */
2713 if (line_count >= curwin->w_height && line_count > min_scroll)
Bram Moolenaar1d6539c2023-02-14 17:41:20 +00002714 scroll_cursor_halfway(FALSE, TRUE);
Luuk van Baalaa6ba302023-05-09 16:01:17 +01002715 else if (line_count > 0)
Bram Moolenaar9bab7a02022-10-06 14:57:53 +01002716 {
Luuk van Baalaa6ba302023-05-09 16:01:17 +01002717 if (do_sms)
2718 scrollup(scrolled, TRUE); // TODO
2719 else
2720 scrollup(line_count, TRUE);
Bram Moolenaar9bab7a02022-10-06 14:57:53 +01002721 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002722
2723 /*
2724 * If topline didn't change we need to restore w_botline and w_empty_rows
2725 * (we changed them).
2726 * If topline did change, update_screen() will set botline.
2727 */
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002728 if (curwin->w_topline == old_topline
2729 && curwin->w_skipcol == old_skipcol
2730 && set_topbot)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002731 {
2732 curwin->w_botline = old_botline;
2733 curwin->w_empty_rows = old_empty_rows;
2734 curwin->w_valid = old_valid;
2735 }
2736 curwin->w_valid |= VALID_TOPLINE;
2737}
2738
2739/*
2740 * Recompute topline to put the cursor halfway the window
2741 * If "atend" is TRUE, also put it halfway at the end of the file.
2742 */
2743 void
Bram Moolenaar1d6539c2023-02-14 17:41:20 +00002744scroll_cursor_halfway(int atend, int prefer_above)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002745{
2746 int above = 0;
2747 linenr_T topline;
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002748 colnr_T skipcol = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002749#ifdef FEAT_DIFF
2750 int topfill = 0;
2751#endif
2752 int below = 0;
2753 int used;
2754 lineoff_T loff;
2755 lineoff_T boff;
Bram Moolenaarb8e23052014-02-11 18:58:09 +01002756#ifdef FEAT_DIFF
Bram Moolenaar12a0f222014-02-11 15:47:46 +01002757 linenr_T old_topline = curwin->w_topline;
Bram Moolenaarb8e23052014-02-11 18:58:09 +01002758#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002759
Bram Moolenaar3697c9b2020-09-26 22:03:00 +02002760#ifdef FEAT_PROP_POPUP
2761 // if the width changed this needs to be updated first
2762 may_update_popup_position();
2763#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002764 loff.lnum = boff.lnum = curwin->w_cursor.lnum;
2765#ifdef FEAT_FOLDING
2766 (void)hasFolding(loff.lnum, &loff.lnum, &boff.lnum);
2767#endif
2768#ifdef FEAT_DIFF
2769 used = plines_nofill(loff.lnum);
2770 loff.fill = 0;
2771 boff.fill = 0;
2772#else
2773 used = plines(loff.lnum);
2774#endif
2775 topline = loff.lnum;
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002776
Luuk van Baal3ce8c382023-05-08 15:51:14 +01002777 int want_height;
Luuk van Baal6c018682023-05-11 18:38:14 +01002778 int do_sms = curwin->w_p_wrap && curwin->w_p_sms;
2779 if (do_sms)
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002780 {
2781 // 'smoothscroll' and 'wrap' are set
Luuk van Baal3ce8c382023-05-08 15:51:14 +01002782 if (atend)
2783 {
2784 want_height = (curwin->w_height - used) / 2;
2785 used = 0;
2786 }
2787 else
2788 want_height = curwin->w_height;
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002789 }
2790
Bram Moolenaar071d4272004-06-13 20:20:40 +00002791 while (topline > 1)
2792 {
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002793 // If using smoothscroll, we can precisely scroll to the
2794 // exact point where the cursor is halfway down the screen.
Luuk van Baal6c018682023-05-11 18:38:14 +01002795 if (do_sms)
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002796 {
2797 topline_back_winheight(&loff, FALSE);
2798 if (loff.height == MAXCOL)
2799 break;
Luuk van Baal3ce8c382023-05-08 15:51:14 +01002800 used += loff.height;
2801 if (!atend && boff.lnum < curbuf->b_ml.ml_line_count)
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002802 {
Luuk van Baal3ce8c382023-05-08 15:51:14 +01002803 botline_forw(&boff);
2804 used += boff.height;
2805 }
2806 if (used > want_height)
2807 {
2808 if (used - loff.height < want_height)
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002809 {
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002810 topline = loff.lnum;
2811#ifdef FEAT_DIFF
2812 topfill = loff.fill;
2813#endif
Luuk van Baal3ce8c382023-05-08 15:51:14 +01002814 skipcol = skipcol_from_plines(curwin, used - want_height);
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002815 }
2816 break;
2817 }
2818 topline = loff.lnum;
2819#ifdef FEAT_DIFF
2820 topfill = loff.fill;
2821#endif
2822 continue;
2823 }
2824
2825 // If not using smoothscroll, we have to iteratively find how many
2826 // lines to scroll down to roughly fit the cursor.
Bram Moolenaar1d6539c2023-02-14 17:41:20 +00002827 // This may not be right in the middle if the lines'
2828 // physical height > 1 (e.g. 'wrap' is on).
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002829
Bram Moolenaar1d6539c2023-02-14 17:41:20 +00002830 // Depending on "prefer_above" we add a line above or below first.
2831 // Loop twice to avoid duplicating code.
2832 int done = FALSE;
2833 for (int round = 1; round <= 2; ++round)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002834 {
Bram Moolenaar1d6539c2023-02-14 17:41:20 +00002835 if (prefer_above ? (round == 2 && below < above)
2836 : (round == 1 && below <= above))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002837 {
Bram Moolenaar1d6539c2023-02-14 17:41:20 +00002838 // add a line below the cursor
2839 if (boff.lnum < curbuf->b_ml.ml_line_count)
2840 {
2841 botline_forw(&boff);
2842 used += boff.height;
2843 if (used > curwin->w_height)
2844 {
2845 done = TRUE;
2846 break;
2847 }
2848 below += boff.height;
2849 }
2850 else
2851 {
2852 ++below; // count a "~" line
2853 if (atend)
2854 ++used;
2855 }
2856 }
2857
2858 if (prefer_above ? (round == 1 && below >= above)
2859 : (round == 1 && below > above))
2860 {
2861 // add a line above the cursor
2862 topline_back(&loff);
2863 if (loff.height == MAXCOL)
2864 used = MAXCOL;
2865 else
2866 used += loff.height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002867 if (used > curwin->w_height)
Bram Moolenaar1d6539c2023-02-14 17:41:20 +00002868 {
2869 done = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002870 break;
Bram Moolenaar1d6539c2023-02-14 17:41:20 +00002871 }
2872 above += loff.height;
2873 topline = loff.lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002874#ifdef FEAT_DIFF
Bram Moolenaar1d6539c2023-02-14 17:41:20 +00002875 topfill = loff.fill;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002876#endif
Bram Moolenaar1d6539c2023-02-14 17:41:20 +00002877 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002878 }
Bram Moolenaar1d6539c2023-02-14 17:41:20 +00002879 if (done)
2880 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002881 }
Bram Moolenaar1d6539c2023-02-14 17:41:20 +00002882
Bram Moolenaar071d4272004-06-13 20:20:40 +00002883#ifdef FEAT_FOLDING
2884 if (!hasFolding(topline, &curwin->w_topline, NULL))
2885#endif
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002886 {
2887 if (curwin->w_topline != topline
Luuk van Baal3ce8c382023-05-08 15:51:14 +01002888 || skipcol != 0
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002889 || curwin->w_skipcol != 0)
2890 {
2891 curwin->w_topline = topline;
Luuk van Baal3ce8c382023-05-08 15:51:14 +01002892 if (skipcol != 0)
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002893 {
2894 curwin->w_skipcol = skipcol;
2895 redraw_later(UPD_NOT_VALID);
2896 }
Luuk van Baal6c018682023-05-11 18:38:14 +01002897 else if (do_sms)
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002898 reset_skipcol();
2899 }
2900 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002901#ifdef FEAT_DIFF
2902 curwin->w_topfill = topfill;
Bram Moolenaar12a0f222014-02-11 15:47:46 +01002903 if (old_topline > curwin->w_topline + curwin->w_height)
2904 curwin->w_botfill = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002905 check_topfill(curwin, FALSE);
2906#endif
2907 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
2908 curwin->w_valid |= VALID_TOPLINE;
2909}
2910
2911/*
2912 * Correct the cursor position so that it is in a part of the screen at least
Bram Moolenaar375e3392019-01-31 18:26:10 +01002913 * 'scrolloff' lines from the top and bottom, if possible.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002914 * If not possible, put it at the same position as scroll_cursor_halfway().
2915 * When called topline must be valid!
2916 */
2917 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002918cursor_correct(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002919{
Bram Moolenaar85a20022019-12-21 18:25:54 +01002920 int above = 0; // screen lines above topline
Bram Moolenaar071d4272004-06-13 20:20:40 +00002921 linenr_T topline;
Bram Moolenaar85a20022019-12-21 18:25:54 +01002922 int below = 0; // screen lines below botline
Bram Moolenaar071d4272004-06-13 20:20:40 +00002923 linenr_T botline;
2924 int above_wanted, below_wanted;
Bram Moolenaar85a20022019-12-21 18:25:54 +01002925 linenr_T cln; // Cursor Line Number
Bram Moolenaar071d4272004-06-13 20:20:40 +00002926 int max_off;
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002927 long so = get_scrolloff_value();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002928
2929 /*
2930 * How many lines we would like to have above/below the cursor depends on
2931 * whether the first/last line of the file is on screen.
2932 */
Bram Moolenaar375e3392019-01-31 18:26:10 +01002933 above_wanted = so;
2934 below_wanted = so;
Bram Moolenaar9964e462007-05-05 17:54:07 +00002935 if (mouse_dragging > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002936 {
2937 above_wanted = mouse_dragging - 1;
2938 below_wanted = mouse_dragging - 1;
2939 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002940 if (curwin->w_topline == 1)
2941 {
2942 above_wanted = 0;
2943 max_off = curwin->w_height / 2;
2944 if (below_wanted > max_off)
2945 below_wanted = max_off;
2946 }
2947 validate_botline();
2948 if (curwin->w_botline == curbuf->b_ml.ml_line_count + 1
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02002949 && mouse_dragging == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002950 {
2951 below_wanted = 0;
2952 max_off = (curwin->w_height - 1) / 2;
2953 if (above_wanted > max_off)
2954 above_wanted = max_off;
2955 }
2956
2957 /*
2958 * If there are sufficient file-lines above and below the cursor, we can
2959 * return now.
2960 */
2961 cln = curwin->w_cursor.lnum;
2962 if (cln >= curwin->w_topline + above_wanted
2963 && cln < curwin->w_botline - below_wanted
2964#ifdef FEAT_FOLDING
2965 && !hasAnyFolding(curwin)
2966#endif
2967 )
2968 return;
2969
Bram Moolenaarc9121f72022-10-14 20:09:04 +01002970 if (curwin->w_p_sms && !curwin->w_p_wrap)
2971 {
Luuk van Baalc8502f92023-05-06 12:40:15 +01002972 // 'smoothscroll' is active
Bram Moolenaarc9121f72022-10-14 20:09:04 +01002973 if (curwin->w_cline_height == curwin->w_height)
2974 {
2975 // The cursor line just fits in the window, don't scroll.
Bram Moolenaarf32fb932022-11-17 11:34:38 +00002976 reset_skipcol();
Bram Moolenaarc9121f72022-10-14 20:09:04 +01002977 return;
2978 }
2979 // TODO: If the cursor line doesn't fit in the window then only adjust
2980 // w_skipcol.
2981 }
2982
Bram Moolenaar071d4272004-06-13 20:20:40 +00002983 /*
2984 * Narrow down the area where the cursor can be put by taking lines from
2985 * the top and the bottom until:
2986 * - the desired context lines are found
2987 * - the lines from the top is past the lines from the bottom
2988 */
2989 topline = curwin->w_topline;
2990 botline = curwin->w_botline - 1;
2991#ifdef FEAT_DIFF
Bram Moolenaar85a20022019-12-21 18:25:54 +01002992 // count filler lines as context
Bram Moolenaar071d4272004-06-13 20:20:40 +00002993 above = curwin->w_topfill;
2994 below = curwin->w_filler_rows;
2995#endif
2996 while ((above < above_wanted || below < below_wanted) && topline < botline)
2997 {
2998 if (below < below_wanted && (below <= above || above >= above_wanted))
2999 {
3000#ifdef FEAT_FOLDING
3001 if (hasFolding(botline, &botline, NULL))
3002 ++below;
3003 else
3004#endif
3005 below += plines(botline);
3006 --botline;
3007 }
3008 if (above < above_wanted && (above < below || below >= below_wanted))
3009 {
3010#ifdef FEAT_FOLDING
3011 if (hasFolding(topline, NULL, &topline))
3012 ++above;
3013 else
3014#endif
Bram Moolenaar43335ea2015-09-09 20:59:37 +02003015 above += PLINES_NOFILL(topline);
3016#ifdef FEAT_DIFF
Bram Moolenaar85a20022019-12-21 18:25:54 +01003017 // Count filler lines below this line as context.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003018 if (topline < botline)
3019 above += diff_check_fill(curwin, topline + 1);
3020#endif
3021 ++topline;
3022 }
3023 }
3024 if (topline == botline || botline == 0)
3025 curwin->w_cursor.lnum = topline;
3026 else if (topline > botline)
3027 curwin->w_cursor.lnum = botline;
3028 else
3029 {
3030 if (cln < topline && curwin->w_topline > 1)
3031 {
3032 curwin->w_cursor.lnum = topline;
3033 curwin->w_valid &=
3034 ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW);
3035 }
3036 if (cln > botline && curwin->w_botline <= curbuf->b_ml.ml_line_count)
3037 {
3038 curwin->w_cursor.lnum = botline;
3039 curwin->w_valid &=
3040 ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW);
3041 }
3042 }
3043 curwin->w_valid |= VALID_TOPLINE;
3044}
3045
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +01003046static void get_scroll_overlap(lineoff_T *lp, int dir);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003047
3048/*
Bram Moolenaar88456cd2022-11-18 22:14:09 +00003049 * Move screen "count" pages up ("dir" is BACKWARD) or down ("dir" is FORWARD)
3050 * and update the screen.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003051 *
Yee Cheng Chin81ba26e2022-11-18 12:52:50 +00003052 * Return FAIL for failure, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003053 */
3054 int
Bram Moolenaar9b578142016-01-30 19:39:49 +01003055onepage(int dir, long count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003056{
3057 long n;
3058 int retval = OK;
3059 lineoff_T loff;
3060 linenr_T old_topline = curwin->w_topline;
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01003061 long so = get_scrolloff_value();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003062
Bram Moolenaar85a20022019-12-21 18:25:54 +01003063 if (curbuf->b_ml.ml_line_count == 1) // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00003064 {
3065 beep_flush();
3066 return FAIL;
3067 }
3068
3069 for ( ; count > 0; --count)
3070 {
3071 validate_botline();
3072 /*
3073 * It's an error to move a page up when the first line is already on
3074 * the screen. It's an error to move a page down when the last line
3075 * is on the screen and the topline is 'scrolloff' lines from the
3076 * last line.
3077 */
3078 if (dir == FORWARD
Bram Moolenaar375e3392019-01-31 18:26:10 +01003079 ? ((curwin->w_topline >= curbuf->b_ml.ml_line_count - so)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003080 && curwin->w_botline > curbuf->b_ml.ml_line_count)
3081 : (curwin->w_topline == 1
3082#ifdef FEAT_DIFF
3083 && curwin->w_topfill ==
3084 diff_check_fill(curwin, curwin->w_topline)
3085#endif
3086 ))
3087 {
3088 beep_flush();
3089 retval = FAIL;
3090 break;
3091 }
3092
3093#ifdef FEAT_DIFF
3094 loff.fill = 0;
3095#endif
3096 if (dir == FORWARD)
3097 {
Bram Moolenaara1f4cb92016-11-06 15:25:42 +01003098 if (ONE_WINDOW && p_window > 0 && p_window < Rows - 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003099 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003100 // Vi compatible scrolling
Bram Moolenaar4399ef42005-02-12 14:29:27 +00003101 if (p_window <= 2)
3102 ++curwin->w_topline;
3103 else
3104 curwin->w_topline += p_window - 2;
3105 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
3106 curwin->w_topline = curbuf->b_ml.ml_line_count;
3107 curwin->w_cursor.lnum = curwin->w_topline;
3108 }
3109 else if (curwin->w_botline > curbuf->b_ml.ml_line_count)
3110 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003111 // at end of file
Bram Moolenaar071d4272004-06-13 20:20:40 +00003112 curwin->w_topline = curbuf->b_ml.ml_line_count;
3113#ifdef FEAT_DIFF
3114 curwin->w_topfill = 0;
3115#endif
3116 curwin->w_valid &= ~(VALID_WROW|VALID_CROW);
3117 }
3118 else
3119 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003120 // For the overlap, start with the line just below the window
3121 // and go upwards.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003122 loff.lnum = curwin->w_botline;
3123#ifdef FEAT_DIFF
3124 loff.fill = diff_check_fill(curwin, loff.lnum)
3125 - curwin->w_filler_rows;
3126#endif
3127 get_scroll_overlap(&loff, -1);
3128 curwin->w_topline = loff.lnum;
3129#ifdef FEAT_DIFF
3130 curwin->w_topfill = loff.fill;
3131 check_topfill(curwin, FALSE);
3132#endif
3133 curwin->w_cursor.lnum = curwin->w_topline;
3134 curwin->w_valid &= ~(VALID_WCOL|VALID_CHEIGHT|VALID_WROW|
3135 VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
3136 }
3137 }
Bram Moolenaar85a20022019-12-21 18:25:54 +01003138 else // dir == BACKWARDS
Bram Moolenaar071d4272004-06-13 20:20:40 +00003139 {
3140#ifdef FEAT_DIFF
3141 if (curwin->w_topline == 1)
3142 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003143 // Include max number of filler lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00003144 max_topfill();
3145 continue;
3146 }
3147#endif
Bram Moolenaara1f4cb92016-11-06 15:25:42 +01003148 if (ONE_WINDOW && p_window > 0 && p_window < Rows - 1)
Bram Moolenaar4399ef42005-02-12 14:29:27 +00003149 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003150 // Vi compatible scrolling (sort of)
Bram Moolenaar4399ef42005-02-12 14:29:27 +00003151 if (p_window <= 2)
3152 --curwin->w_topline;
3153 else
3154 curwin->w_topline -= p_window - 2;
3155 if (curwin->w_topline < 1)
3156 curwin->w_topline = 1;
3157 curwin->w_cursor.lnum = curwin->w_topline + p_window - 1;
3158 if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
3159 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
3160 continue;
3161 }
3162
Bram Moolenaar85a20022019-12-21 18:25:54 +01003163 // Find the line at the top of the window that is going to be the
3164 // line at the bottom of the window. Make sure this results in
3165 // the same line as before doing CTRL-F.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003166 loff.lnum = curwin->w_topline - 1;
3167#ifdef FEAT_DIFF
3168 loff.fill = diff_check_fill(curwin, loff.lnum + 1)
3169 - curwin->w_topfill;
3170#endif
3171 get_scroll_overlap(&loff, 1);
3172
3173 if (loff.lnum >= curbuf->b_ml.ml_line_count)
3174 {
3175 loff.lnum = curbuf->b_ml.ml_line_count;
3176#ifdef FEAT_DIFF
3177 loff.fill = 0;
3178 }
3179 else
3180 {
3181 botline_topline(&loff);
3182#endif
3183 }
3184 curwin->w_cursor.lnum = loff.lnum;
3185
Bram Moolenaar85a20022019-12-21 18:25:54 +01003186 // Find the line just above the new topline to get the right line
3187 // at the bottom of the window.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003188 n = 0;
3189 while (n <= curwin->w_height && loff.lnum >= 1)
3190 {
3191 topline_back(&loff);
Bram Moolenaarbacd9da2010-02-17 18:20:37 +01003192 if (loff.height == MAXCOL)
3193 n = MAXCOL;
3194 else
3195 n += loff.height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003196 }
Bram Moolenaar85a20022019-12-21 18:25:54 +01003197 if (loff.lnum < 1) // at begin of file
Bram Moolenaar071d4272004-06-13 20:20:40 +00003198 {
3199 curwin->w_topline = 1;
3200#ifdef FEAT_DIFF
3201 max_topfill();
3202#endif
3203 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
3204 }
3205 else
3206 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003207 // Go two lines forward again.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003208#ifdef FEAT_DIFF
3209 topline_botline(&loff);
3210#endif
3211 botline_forw(&loff);
3212 botline_forw(&loff);
3213#ifdef FEAT_DIFF
3214 botline_topline(&loff);
3215#endif
3216#ifdef FEAT_FOLDING
Bram Moolenaar85a20022019-12-21 18:25:54 +01003217 // We're at the wrong end of a fold now.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003218 (void)hasFolding(loff.lnum, &loff.lnum, NULL);
3219#endif
3220
Bram Moolenaar85a20022019-12-21 18:25:54 +01003221 // Always scroll at least one line. Avoid getting stuck on
3222 // very long lines.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003223 if (loff.lnum >= curwin->w_topline
3224#ifdef FEAT_DIFF
3225 && (loff.lnum > curwin->w_topline
3226 || loff.fill >= curwin->w_topfill)
3227#endif
3228 )
3229 {
3230#ifdef FEAT_DIFF
Bram Moolenaar85a20022019-12-21 18:25:54 +01003231 // First try using the maximum number of filler lines. If
3232 // that's not enough, backup one line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003233 loff.fill = curwin->w_topfill;
3234 if (curwin->w_topfill < diff_check_fill(curwin,
3235 curwin->w_topline))
3236 max_topfill();
3237 if (curwin->w_topfill == loff.fill)
3238#endif
3239 {
3240 --curwin->w_topline;
3241#ifdef FEAT_DIFF
3242 curwin->w_topfill = 0;
3243#endif
3244 }
3245 comp_botline(curwin);
3246 curwin->w_cursor.lnum = curwin->w_botline - 1;
Bram Moolenaar3d6db142014-03-28 21:49:32 +01003247 curwin->w_valid &=
3248 ~(VALID_WCOL|VALID_CHEIGHT|VALID_WROW|VALID_CROW);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003249 }
3250 else
3251 {
3252 curwin->w_topline = loff.lnum;
3253#ifdef FEAT_DIFF
3254 curwin->w_topfill = loff.fill;
3255 check_topfill(curwin, FALSE);
3256#endif
3257 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
3258 }
3259 }
3260 }
3261 }
3262#ifdef FEAT_FOLDING
3263 foldAdjustCursor();
3264#endif
3265 cursor_correct();
Bram Moolenaarbc54f3f2016-09-04 14:34:28 +02003266 check_cursor_col();
Bram Moolenaar7c626922005-02-07 22:01:03 +00003267 if (retval == OK)
3268 beginline(BL_SOL | BL_FIX);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003269 curwin->w_valid &= ~(VALID_WCOL|VALID_WROW|VALID_VIRTCOL);
3270
Bram Moolenaar907dad72018-07-10 15:07:15 +02003271 if (retval == OK && dir == FORWARD)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003272 {
Bram Moolenaar907dad72018-07-10 15:07:15 +02003273 // Avoid the screen jumping up and down when 'scrolloff' is non-zero.
3274 // But make sure we scroll at least one line (happens with mix of long
3275 // wrapping lines and non-wrapping line).
3276 if (check_top_offset())
Bram Moolenaar071d4272004-06-13 20:20:40 +00003277 {
Bram Moolenaar907dad72018-07-10 15:07:15 +02003278 scroll_cursor_top(1, FALSE);
3279 if (curwin->w_topline <= old_topline
3280 && old_topline < curbuf->b_ml.ml_line_count)
3281 {
3282 curwin->w_topline = old_topline + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003283#ifdef FEAT_FOLDING
Bram Moolenaar907dad72018-07-10 15:07:15 +02003284 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
3285#endif
3286 }
3287 }
3288#ifdef FEAT_FOLDING
3289 else if (curwin->w_botline > curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003290 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
3291#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003292 }
3293
Bram Moolenaara4d158b2022-08-14 14:17:45 +01003294 redraw_later(UPD_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003295 return retval;
3296}
3297
3298/*
3299 * Decide how much overlap to use for page-up or page-down scrolling.
3300 * This is symmetric, so that doing both keeps the same lines displayed.
3301 * Three lines are examined:
3302 *
3303 * before CTRL-F after CTRL-F / before CTRL-B
3304 * etc. l1
3305 * l1 last but one line ------------
3306 * l2 last text line l2 top text line
3307 * ------------- l3 second text line
3308 * l3 etc.
3309 */
3310 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003311get_scroll_overlap(lineoff_T *lp, int dir)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003312{
3313 int h1, h2, h3, h4;
3314 int min_height = curwin->w_height - 2;
3315 lineoff_T loff0, loff1, loff2;
3316
3317#ifdef FEAT_DIFF
3318 if (lp->fill > 0)
3319 lp->height = 1;
3320 else
3321 lp->height = plines_nofill(lp->lnum);
3322#else
3323 lp->height = plines(lp->lnum);
3324#endif
3325 h1 = lp->height;
3326 if (h1 > min_height)
Bram Moolenaar85a20022019-12-21 18:25:54 +01003327 return; // no overlap
Bram Moolenaar071d4272004-06-13 20:20:40 +00003328
3329 loff0 = *lp;
3330 if (dir > 0)
3331 botline_forw(lp);
3332 else
3333 topline_back(lp);
3334 h2 = lp->height;
Bram Moolenaarf4f19562012-11-28 18:22:11 +01003335 if (h2 == MAXCOL || h2 + h1 > min_height)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003336 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003337 *lp = loff0; // no overlap
Bram Moolenaar071d4272004-06-13 20:20:40 +00003338 return;
3339 }
3340
3341 loff1 = *lp;
3342 if (dir > 0)
3343 botline_forw(lp);
3344 else
3345 topline_back(lp);
3346 h3 = lp->height;
Bram Moolenaarf4f19562012-11-28 18:22:11 +01003347 if (h3 == MAXCOL || h3 + h2 > min_height)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003348 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003349 *lp = loff0; // no overlap
Bram Moolenaar071d4272004-06-13 20:20:40 +00003350 return;
3351 }
3352
3353 loff2 = *lp;
3354 if (dir > 0)
3355 botline_forw(lp);
3356 else
3357 topline_back(lp);
3358 h4 = lp->height;
Bram Moolenaarf4f19562012-11-28 18:22:11 +01003359 if (h4 == MAXCOL || h4 + h3 + h2 > min_height || h3 + h2 + h1 > min_height)
Bram Moolenaar85a20022019-12-21 18:25:54 +01003360 *lp = loff1; // 1 line overlap
Bram Moolenaar071d4272004-06-13 20:20:40 +00003361 else
Bram Moolenaar85a20022019-12-21 18:25:54 +01003362 *lp = loff2; // 2 lines overlap
Bram Moolenaar071d4272004-06-13 20:20:40 +00003363}
3364
Bram Moolenaar071d4272004-06-13 20:20:40 +00003365/*
3366 * Scroll 'scroll' lines up or down.
3367 */
3368 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003369halfpage(int flag, linenr_T Prenum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003370{
3371 long scrolled = 0;
3372 int i;
3373 int n;
3374 int room;
3375
3376 if (Prenum)
3377 curwin->w_p_scr = (Prenum > curwin->w_height) ?
3378 curwin->w_height : Prenum;
3379 n = (curwin->w_p_scr <= curwin->w_height) ?
3380 curwin->w_p_scr : curwin->w_height;
3381
Bram Moolenaard5d37532017-03-27 23:02:07 +02003382 update_topline();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003383 validate_botline();
3384 room = curwin->w_empty_rows;
3385#ifdef FEAT_DIFF
3386 room += curwin->w_filler_rows;
3387#endif
3388 if (flag)
3389 {
3390 /*
3391 * scroll the text up
3392 */
3393 while (n > 0 && curwin->w_botline <= curbuf->b_ml.ml_line_count)
3394 {
3395#ifdef FEAT_DIFF
3396 if (curwin->w_topfill > 0)
3397 {
3398 i = 1;
Bram Moolenaar8455c5e2020-07-14 21:22:30 +02003399 --n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003400 --curwin->w_topfill;
3401 }
3402 else
3403#endif
3404 {
Bram Moolenaar43335ea2015-09-09 20:59:37 +02003405 i = PLINES_NOFILL(curwin->w_topline);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003406 n -= i;
3407 if (n < 0 && scrolled > 0)
3408 break;
3409#ifdef FEAT_FOLDING
3410 (void)hasFolding(curwin->w_topline, NULL, &curwin->w_topline);
3411#endif
3412 ++curwin->w_topline;
3413#ifdef FEAT_DIFF
3414 curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline);
3415#endif
3416
Bram Moolenaar071d4272004-06-13 20:20:40 +00003417 if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
3418 {
3419 ++curwin->w_cursor.lnum;
3420 curwin->w_valid &=
3421 ~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL);
3422 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003423 }
3424 curwin->w_valid &= ~(VALID_CROW|VALID_WROW);
3425 scrolled += i;
3426
3427 /*
3428 * Correct w_botline for changed w_topline.
3429 * Won't work when there are filler lines.
3430 */
3431#ifdef FEAT_DIFF
3432 if (curwin->w_p_diff)
3433 curwin->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP);
3434 else
3435#endif
3436 {
3437 room += i;
3438 do
3439 {
3440 i = plines(curwin->w_botline);
3441 if (i > room)
3442 break;
3443#ifdef FEAT_FOLDING
3444 (void)hasFolding(curwin->w_botline, NULL,
3445 &curwin->w_botline);
3446#endif
3447 ++curwin->w_botline;
3448 room -= i;
3449 } while (curwin->w_botline <= curbuf->b_ml.ml_line_count);
3450 }
3451 }
3452
Bram Moolenaar071d4272004-06-13 20:20:40 +00003453 /*
3454 * When hit bottom of the file: move cursor down.
3455 */
3456 if (n > 0)
3457 {
3458# ifdef FEAT_FOLDING
3459 if (hasAnyFolding(curwin))
3460 {
3461 while (--n >= 0
3462 && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
3463 {
3464 (void)hasFolding(curwin->w_cursor.lnum, NULL,
3465 &curwin->w_cursor.lnum);
3466 ++curwin->w_cursor.lnum;
3467 }
3468 }
3469 else
3470# endif
3471 curwin->w_cursor.lnum += n;
3472 check_cursor_lnum();
3473 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003474 }
3475 else
3476 {
3477 /*
3478 * scroll the text down
3479 */
3480 while (n > 0 && curwin->w_topline > 1)
3481 {
3482#ifdef FEAT_DIFF
3483 if (curwin->w_topfill < diff_check_fill(curwin, curwin->w_topline))
3484 {
3485 i = 1;
Bram Moolenaard00e0242019-03-20 21:42:20 +01003486 --n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003487 ++curwin->w_topfill;
3488 }
3489 else
3490#endif
3491 {
Bram Moolenaar43335ea2015-09-09 20:59:37 +02003492 i = PLINES_NOFILL(curwin->w_topline - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003493 n -= i;
3494 if (n < 0 && scrolled > 0)
3495 break;
3496 --curwin->w_topline;
3497#ifdef FEAT_FOLDING
3498 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
3499#endif
3500#ifdef FEAT_DIFF
3501 curwin->w_topfill = 0;
3502#endif
3503 }
3504 curwin->w_valid &= ~(VALID_CROW|VALID_WROW|
3505 VALID_BOTLINE|VALID_BOTLINE_AP);
3506 scrolled += i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003507 if (curwin->w_cursor.lnum > 1)
3508 {
3509 --curwin->w_cursor.lnum;
3510 curwin->w_valid &= ~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL);
3511 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003512 }
Bram Moolenaard00e0242019-03-20 21:42:20 +01003513
Bram Moolenaar071d4272004-06-13 20:20:40 +00003514 /*
3515 * When hit top of the file: move cursor up.
3516 */
3517 if (n > 0)
3518 {
3519 if (curwin->w_cursor.lnum <= (linenr_T)n)
3520 curwin->w_cursor.lnum = 1;
3521 else
3522# ifdef FEAT_FOLDING
3523 if (hasAnyFolding(curwin))
3524 {
3525 while (--n >= 0 && curwin->w_cursor.lnum > 1)
3526 {
3527 --curwin->w_cursor.lnum;
3528 (void)hasFolding(curwin->w_cursor.lnum,
3529 &curwin->w_cursor.lnum, NULL);
3530 }
3531 }
3532 else
3533# endif
3534 curwin->w_cursor.lnum -= n;
3535 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003536 }
3537# ifdef FEAT_FOLDING
Bram Moolenaar85a20022019-12-21 18:25:54 +01003538 // Move cursor to first line of closed fold.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003539 foldAdjustCursor();
3540# endif
3541#ifdef FEAT_DIFF
3542 check_topfill(curwin, !flag);
3543#endif
3544 cursor_correct();
3545 beginline(BL_SOL | BL_FIX);
Bram Moolenaara4d158b2022-08-14 14:17:45 +01003546 redraw_later(UPD_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003547}
Bram Moolenaar860cae12010-06-05 23:22:07 +02003548
Bram Moolenaar860cae12010-06-05 23:22:07 +02003549 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003550do_check_cursorbind(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003551{
3552 linenr_T line = curwin->w_cursor.lnum;
Bram Moolenaar1ea69b72012-03-16 19:24:26 +01003553 colnr_T col = curwin->w_cursor.col;
Bram Moolenaar1ea69b72012-03-16 19:24:26 +01003554 colnr_T coladd = curwin->w_cursor.coladd;
Bram Moolenaar524780d2012-03-28 14:19:50 +02003555 colnr_T curswant = curwin->w_curswant;
3556 int set_curswant = curwin->w_set_curswant;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003557 win_T *old_curwin = curwin;
3558 buf_T *old_curbuf = curbuf;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003559 int old_VIsual_select = VIsual_select;
3560 int old_VIsual_active = VIsual_active;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003561
3562 /*
3563 * loop through the cursorbound windows
3564 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003565 VIsual_select = VIsual_active = 0;
Bram Moolenaar29323592016-07-24 22:04:11 +02003566 FOR_ALL_WINDOWS(curwin)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003567 {
3568 curbuf = curwin->w_buffer;
Bram Moolenaar85a20022019-12-21 18:25:54 +01003569 // skip original window and windows with 'noscrollbind'
Bram Moolenaar860cae12010-06-05 23:22:07 +02003570 if (curwin != old_curwin && curwin->w_p_crb)
3571 {
3572# ifdef FEAT_DIFF
3573 if (curwin->w_p_diff)
Bram Moolenaar025e3e02016-10-18 14:50:18 +02003574 curwin->w_cursor.lnum =
3575 diff_get_corresponding_line(old_curbuf, line);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003576 else
3577# endif
3578 curwin->w_cursor.lnum = line;
3579 curwin->w_cursor.col = col;
Bram Moolenaar1ea69b72012-03-16 19:24:26 +01003580 curwin->w_cursor.coladd = coladd;
Bram Moolenaar524780d2012-03-28 14:19:50 +02003581 curwin->w_curswant = curswant;
3582 curwin->w_set_curswant = set_curswant;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003583
Bram Moolenaar85a20022019-12-21 18:25:54 +01003584 // Make sure the cursor is in a valid position. Temporarily set
3585 // "restart_edit" to allow the cursor to be beyond the EOL.
Dominique Pellee764d1b2023-03-12 21:20:59 +00003586 int restart_edit_save = restart_edit;
3587 restart_edit = 'a';
Bram Moolenaar860cae12010-06-05 23:22:07 +02003588 check_cursor();
Bram Moolenaara315ce12022-06-24 12:38:57 +01003589
3590 // Avoid a scroll here for the cursor position, 'scrollbind' is
3591 // more important.
3592 if (!curwin->w_p_scb)
3593 validate_cursor();
3594
Bram Moolenaar61452852011-02-01 18:01:11 +01003595 restart_edit = restart_edit_save;
Bram Moolenaar85a20022019-12-21 18:25:54 +01003596 // Correct cursor for multi-byte character.
Bram Moolenaar860cae12010-06-05 23:22:07 +02003597 if (has_mbyte)
3598 mb_adjust_cursor();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01003599 redraw_later(UPD_VALID);
Bram Moolenaarf3d419d2011-01-22 21:05:07 +01003600
Bram Moolenaar85a20022019-12-21 18:25:54 +01003601 // Only scroll when 'scrollbind' hasn't done this.
Bram Moolenaarf3d419d2011-01-22 21:05:07 +01003602 if (!curwin->w_p_scb)
3603 update_topline();
Bram Moolenaar860cae12010-06-05 23:22:07 +02003604 curwin->w_redr_status = TRUE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003605 }
3606 }
3607
3608 /*
3609 * reset current-window
3610 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003611 VIsual_select = old_VIsual_select;
3612 VIsual_active = old_VIsual_active;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003613 curwin = old_curwin;
3614 curbuf = old_curbuf;
3615}