blob: 93c101b794e91a4faa8c801082b532139428b515 [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;
Bram Moolenaar2fbabd22022-10-12 19:53:38 +01001963 scrolled = TRUE;
Bram Moolenaar2fbabd22022-10-12 19:53:38 +01001964 }
1965 if (scrolled)
Luuk van Baal798fa762023-05-15 18:17:43 +01001966 {
1967 validate_virtcol();
1968 redraw_later(UPD_NOT_VALID);
Bram Moolenaar2fbabd22022-10-12 19:53:38 +01001969 return; // don't scroll in the other direction now
Luuk van Baal798fa762023-05-15 18:17:43 +01001970 }
Bram Moolenaar2fbabd22022-10-12 19:53:38 +01001971
1972 int col = curwin->w_virtcol - curwin->w_skipcol + scrolloff_cols;
1973 int row = 0;
1974 if (col >= width1)
1975 {
1976 col -= width1;
1977 ++row;
1978 }
1979 if (col > width2)
1980 {
1981 row += col / width2;
1982 col = col % width2;
1983 }
1984 if (row >= curwin->w_height)
1985 {
1986 if (curwin->w_skipcol == 0)
1987 {
1988 curwin->w_skipcol += width1;
1989 --row;
1990 }
1991 if (row >= curwin->w_height)
1992 curwin->w_skipcol += (row - curwin->w_height) * width2;
1993 redraw_later(UPD_NOT_VALID);
1994 }
1995}
1996
Bram Moolenaar071d4272004-06-13 20:20:40 +00001997#ifdef FEAT_DIFF
1998/*
1999 * Don't end up with too many filler lines in the window.
2000 */
2001 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002002check_topfill(
2003 win_T *wp,
Bram Moolenaar85a20022019-12-21 18:25:54 +01002004 int down) // when TRUE scroll down when not enough space
Bram Moolenaar071d4272004-06-13 20:20:40 +00002005{
2006 int n;
2007
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002008 if (wp->w_topfill <= 0)
2009 return;
2010
2011 n = plines_win_nofill(wp, wp->w_topline, TRUE);
2012 if (wp->w_topfill + n > wp->w_height)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002013 {
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002014 if (down && wp->w_topline > 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002015 {
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002016 --wp->w_topline;
2017 wp->w_topfill = 0;
2018 }
2019 else
2020 {
2021 wp->w_topfill = wp->w_height - n;
2022 if (wp->w_topfill < 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002023 wp->w_topfill = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002024 }
2025 }
2026}
2027
2028/*
2029 * Use as many filler lines as possible for w_topline. Make sure w_topline
2030 * is still visible.
2031 */
2032 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002033max_topfill(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002034{
2035 int n;
2036
2037 n = plines_nofill(curwin->w_topline);
2038 if (n >= curwin->w_height)
2039 curwin->w_topfill = 0;
2040 else
2041 {
2042 curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline);
2043 if (curwin->w_topfill + n > curwin->w_height)
2044 curwin->w_topfill = curwin->w_height - n;
2045 }
2046}
2047#endif
2048
Bram Moolenaar071d4272004-06-13 20:20:40 +00002049/*
2050 * Scroll the screen one line down, but don't do it if it would move the
2051 * cursor off the screen.
2052 */
2053 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002054scrolldown_clamp(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002055{
2056 int end_row;
2057#ifdef FEAT_DIFF
2058 int can_fill = (curwin->w_topfill
2059 < diff_check_fill(curwin, curwin->w_topline));
2060#endif
2061
2062 if (curwin->w_topline <= 1
2063#ifdef FEAT_DIFF
2064 && !can_fill
2065#endif
2066 )
2067 return;
2068
Bram Moolenaar85a20022019-12-21 18:25:54 +01002069 validate_cursor(); // w_wrow needs to be valid
Bram Moolenaar071d4272004-06-13 20:20:40 +00002070
2071 /*
2072 * Compute the row number of the last row of the cursor line
2073 * and make sure it doesn't go off the screen. Make sure the cursor
2074 * doesn't go past 'scrolloff' lines from the screen end.
2075 */
2076 end_row = curwin->w_wrow;
2077#ifdef FEAT_DIFF
2078 if (can_fill)
2079 ++end_row;
2080 else
2081 end_row += plines_nofill(curwin->w_topline - 1);
2082#else
2083 end_row += plines(curwin->w_topline - 1);
2084#endif
Bram Moolenaar4033c552017-09-16 20:54:51 +02002085 if (curwin->w_p_wrap && curwin->w_width != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002086 {
2087 validate_cheight();
2088 validate_virtcol();
2089 end_row += curwin->w_cline_height - 1 -
Bram Moolenaar02631462017-09-22 15:20:32 +02002090 curwin->w_virtcol / curwin->w_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002091 }
Bram Moolenaar375e3392019-01-31 18:26:10 +01002092 if (end_row < curwin->w_height - get_scrolloff_value())
Bram Moolenaar071d4272004-06-13 20:20:40 +00002093 {
2094#ifdef FEAT_DIFF
2095 if (can_fill)
2096 {
2097 ++curwin->w_topfill;
2098 check_topfill(curwin, TRUE);
2099 }
2100 else
2101 {
2102 --curwin->w_topline;
2103 curwin->w_topfill = 0;
2104 }
2105#else
2106 --curwin->w_topline;
2107#endif
2108#ifdef FEAT_FOLDING
Bram Moolenaarcde88542015-08-11 19:14:00 +02002109 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002110#endif
Bram Moolenaar85a20022019-12-21 18:25:54 +01002111 --curwin->w_botline; // approximate w_botline
Bram Moolenaar071d4272004-06-13 20:20:40 +00002112 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
2113 }
2114}
2115
2116/*
2117 * Scroll the screen one line up, but don't do it if it would move the cursor
2118 * off the screen.
2119 */
2120 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002121scrollup_clamp(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002122{
2123 int start_row;
2124
2125 if (curwin->w_topline == curbuf->b_ml.ml_line_count
2126#ifdef FEAT_DIFF
2127 && curwin->w_topfill == 0
2128#endif
2129 )
2130 return;
2131
Bram Moolenaar85a20022019-12-21 18:25:54 +01002132 validate_cursor(); // w_wrow needs to be valid
Bram Moolenaar071d4272004-06-13 20:20:40 +00002133
2134 /*
2135 * Compute the row number of the first row of the cursor line
2136 * and make sure it doesn't go off the screen. Make sure the cursor
2137 * doesn't go before 'scrolloff' lines from the screen start.
2138 */
2139#ifdef FEAT_DIFF
2140 start_row = curwin->w_wrow - plines_nofill(curwin->w_topline)
2141 - curwin->w_topfill;
2142#else
2143 start_row = curwin->w_wrow - plines(curwin->w_topline);
2144#endif
Bram Moolenaar4033c552017-09-16 20:54:51 +02002145 if (curwin->w_p_wrap && curwin->w_width != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002146 {
2147 validate_virtcol();
Bram Moolenaar02631462017-09-22 15:20:32 +02002148 start_row -= curwin->w_virtcol / curwin->w_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002149 }
Bram Moolenaar375e3392019-01-31 18:26:10 +01002150 if (start_row >= get_scrolloff_value())
Bram Moolenaar071d4272004-06-13 20:20:40 +00002151 {
2152#ifdef FEAT_DIFF
2153 if (curwin->w_topfill > 0)
2154 --curwin->w_topfill;
2155 else
2156#endif
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002157 {
2158#ifdef FEAT_FOLDING
2159 (void)hasFolding(curwin->w_topline, NULL, &curwin->w_topline);
2160#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002161 ++curwin->w_topline;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002162 }
Bram Moolenaar85a20022019-12-21 18:25:54 +01002163 ++curwin->w_botline; // approximate w_botline
Bram Moolenaar071d4272004-06-13 20:20:40 +00002164 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
2165 }
2166}
Bram Moolenaar071d4272004-06-13 20:20:40 +00002167
2168/*
2169 * Add one line above "lp->lnum". This can be a filler line, a closed fold or
2170 * a (wrapped) text line. Uses and sets "lp->fill".
2171 * Returns the height of the added line in "lp->height".
Bram Moolenaarbacd9da2010-02-17 18:20:37 +01002172 * Lines above the first one are incredibly high: MAXCOL.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002173 */
2174 static void
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002175topline_back_winheight(
2176 lineoff_T *lp,
2177 int winheight) // when TRUE limit to window height
Bram Moolenaar071d4272004-06-13 20:20:40 +00002178{
2179#ifdef FEAT_DIFF
2180 if (lp->fill < diff_check_fill(curwin, lp->lnum))
2181 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01002182 // Add a filler line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002183 ++lp->fill;
2184 lp->height = 1;
2185 }
2186 else
2187#endif
2188 {
2189 --lp->lnum;
2190#ifdef FEAT_DIFF
2191 lp->fill = 0;
2192#endif
2193 if (lp->lnum < 1)
2194 lp->height = MAXCOL;
2195 else
2196#ifdef FEAT_FOLDING
2197 if (hasFolding(lp->lnum, &lp->lnum, NULL))
Bram Moolenaar85a20022019-12-21 18:25:54 +01002198 // Add a closed fold
Bram Moolenaar071d4272004-06-13 20:20:40 +00002199 lp->height = 1;
2200 else
2201#endif
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002202 lp->height = PLINES_WIN_NOFILL(curwin, lp->lnum, winheight);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002203 }
2204}
2205
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002206 static void
2207topline_back(lineoff_T *lp)
2208{
2209 topline_back_winheight(lp, TRUE);
2210}
2211
2212
Bram Moolenaar071d4272004-06-13 20:20:40 +00002213/*
2214 * Add one line below "lp->lnum". This can be a filler line, a closed fold or
2215 * a (wrapped) text line. Uses and sets "lp->fill".
2216 * Returns the height of the added line in "lp->height".
2217 * Lines below the last one are incredibly high.
2218 */
2219 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002220botline_forw(lineoff_T *lp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002221{
2222#ifdef FEAT_DIFF
2223 if (lp->fill < diff_check_fill(curwin, lp->lnum + 1))
2224 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01002225 // Add a filler line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002226 ++lp->fill;
2227 lp->height = 1;
2228 }
2229 else
2230#endif
2231 {
2232 ++lp->lnum;
2233#ifdef FEAT_DIFF
2234 lp->fill = 0;
2235#endif
2236 if (lp->lnum > curbuf->b_ml.ml_line_count)
2237 lp->height = MAXCOL;
2238 else
2239#ifdef FEAT_FOLDING
2240 if (hasFolding(lp->lnum, NULL, &lp->lnum))
Bram Moolenaar85a20022019-12-21 18:25:54 +01002241 // Add a closed fold
Bram Moolenaar071d4272004-06-13 20:20:40 +00002242 lp->height = 1;
2243 else
2244#endif
Bram Moolenaar43335ea2015-09-09 20:59:37 +02002245 lp->height = PLINES_NOFILL(lp->lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002246 }
2247}
2248
2249#ifdef FEAT_DIFF
2250/*
2251 * Switch from including filler lines below lp->lnum to including filler
2252 * lines above loff.lnum + 1. This keeps pointing to the same line.
2253 * When there are no filler lines nothing changes.
2254 */
2255 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002256botline_topline(lineoff_T *lp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002257{
2258 if (lp->fill > 0)
2259 {
2260 ++lp->lnum;
2261 lp->fill = diff_check_fill(curwin, lp->lnum) - lp->fill + 1;
2262 }
2263}
2264
2265/*
2266 * Switch from including filler lines above lp->lnum to including filler
2267 * lines below loff.lnum - 1. This keeps pointing to the same line.
2268 * When there are no filler lines nothing changes.
2269 */
2270 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002271topline_botline(lineoff_T *lp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002272{
2273 if (lp->fill > 0)
2274 {
2275 lp->fill = diff_check_fill(curwin, lp->lnum) - lp->fill + 1;
2276 --lp->lnum;
2277 }
2278}
2279#endif
2280
2281/*
2282 * Recompute topline to put the cursor at the top of the window.
2283 * Scroll at least "min_scroll" lines.
2284 * If "always" is TRUE, always set topline (for "zt").
2285 */
2286 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002287scroll_cursor_top(int min_scroll, int always)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002288{
2289 int scrolled = 0;
2290 int extra = 0;
2291 int used;
2292 int i;
Bram Moolenaar85a20022019-12-21 18:25:54 +01002293 linenr_T top; // just above displayed lines
2294 linenr_T bot; // just below displayed lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00002295 linenr_T old_topline = curwin->w_topline;
Bram Moolenaar46b54742022-10-06 15:46:49 +01002296 int old_skipcol = curwin->w_skipcol;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002297#ifdef FEAT_DIFF
2298 linenr_T old_topfill = curwin->w_topfill;
2299#endif
2300 linenr_T new_topline;
Bram Moolenaar375e3392019-01-31 18:26:10 +01002301 int off = get_scrolloff_value();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002302
Bram Moolenaar071d4272004-06-13 20:20:40 +00002303 if (mouse_dragging > 0)
2304 off = mouse_dragging - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002305
2306 /*
2307 * Decrease topline until:
2308 * - it has become 1
2309 * - (part of) the cursor line is moved off the screen or
2310 * - moved at least 'scrolljump' lines and
2311 * - at least 'scrolloff' lines above and below the cursor
2312 */
2313 validate_cheight();
Bram Moolenaar85a20022019-12-21 18:25:54 +01002314 used = curwin->w_cline_height; // includes filler lines above
Bram Moolenaar071d4272004-06-13 20:20:40 +00002315 if (curwin->w_cursor.lnum < curwin->w_topline)
2316 scrolled = used;
2317
2318#ifdef FEAT_FOLDING
2319 if (hasFolding(curwin->w_cursor.lnum, &top, &bot))
2320 {
2321 --top;
2322 ++bot;
2323 }
2324 else
2325#endif
2326 {
2327 top = curwin->w_cursor.lnum - 1;
2328 bot = curwin->w_cursor.lnum + 1;
2329 }
2330 new_topline = top + 1;
2331
2332#ifdef FEAT_DIFF
Bram Moolenaar85a20022019-12-21 18:25:54 +01002333 // "used" already contains the number of filler lines above, don't add it
2334 // again.
2335 // Hide filler lines above cursor line by adding them to "extra".
Bram Moolenaara09a2c52015-09-08 17:31:59 +02002336 extra += diff_check_fill(curwin, curwin->w_cursor.lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002337#endif
2338
2339 /*
2340 * Check if the lines from "top" to "bot" fit in the window. If they do,
2341 * set new_topline and advance "top" and "bot" to include more lines.
2342 */
2343 while (top > 0)
2344 {
2345#ifdef FEAT_FOLDING
2346 if (hasFolding(top, &top, NULL))
Bram Moolenaar85a20022019-12-21 18:25:54 +01002347 // count one logical line for a sequence of folded lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00002348 i = 1;
2349 else
2350#endif
Bram Moolenaar43335ea2015-09-09 20:59:37 +02002351 i = PLINES_NOFILL(top);
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002352 if (top < curwin->w_topline)
2353 scrolled += i;
2354
2355 // If scrolling is needed, scroll at least 'sj' lines.
2356 if ((new_topline >= curwin->w_topline || scrolled > min_scroll)
2357 && extra >= off)
2358 break;
2359
Bram Moolenaar071d4272004-06-13 20:20:40 +00002360 used += i;
2361 if (extra + i <= off && bot < curbuf->b_ml.ml_line_count)
2362 {
2363#ifdef FEAT_FOLDING
2364 if (hasFolding(bot, NULL, &bot))
Bram Moolenaar85a20022019-12-21 18:25:54 +01002365 // count one logical line for a sequence of folded lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00002366 ++used;
2367 else
2368#endif
2369 used += plines(bot);
2370 }
2371 if (used > curwin->w_height)
2372 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002373
2374 extra += i;
2375 new_topline = top;
2376 --top;
2377 ++bot;
2378 }
2379
2380 /*
2381 * If we don't have enough space, put cursor in the middle.
2382 * This makes sure we get the same position when using "k" and "j"
2383 * in a small window.
2384 */
2385 if (used > curwin->w_height)
Bram Moolenaar1d6539c2023-02-14 17:41:20 +00002386 scroll_cursor_halfway(FALSE, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002387 else
2388 {
2389 /*
2390 * If "always" is FALSE, only adjust topline to a lower value, higher
Bram Moolenaar1b73edd2022-12-03 11:51:54 +00002391 * value may happen with wrapping lines.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002392 */
2393 if (new_topline < curwin->w_topline || always)
2394 curwin->w_topline = new_topline;
2395 if (curwin->w_topline > curwin->w_cursor.lnum)
2396 curwin->w_topline = curwin->w_cursor.lnum;
2397#ifdef FEAT_DIFF
2398 curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline);
2399 if (curwin->w_topfill > 0 && extra > off)
2400 {
2401 curwin->w_topfill -= extra - off;
2402 if (curwin->w_topfill < 0)
2403 curwin->w_topfill = 0;
2404 }
2405 check_topfill(curwin, FALSE);
2406#endif
Bram Moolenaar46b54742022-10-06 15:46:49 +01002407 // TODO: if the line doesn't fit may optimize w_skipcol
Bram Moolenaar1b73edd2022-12-03 11:51:54 +00002408 if (curwin->w_topline == curwin->w_cursor.lnum
2409 && curwin->w_skipcol >= curwin->w_cursor.col)
Bram Moolenaarf32fb932022-11-17 11:34:38 +00002410 reset_skipcol();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002411 if (curwin->w_topline != old_topline
Bram Moolenaar46b54742022-10-06 15:46:49 +01002412 || curwin->w_skipcol != old_skipcol
Bram Moolenaar071d4272004-06-13 20:20:40 +00002413#ifdef FEAT_DIFF
2414 || curwin->w_topfill != old_topfill
2415#endif
2416 )
2417 curwin->w_valid &=
2418 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
2419 curwin->w_valid |= VALID_TOPLINE;
2420 }
2421}
2422
2423/*
2424 * Set w_empty_rows and w_filler_rows for window "wp", having used up "used"
2425 * screen lines for text lines.
2426 */
2427 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002428set_empty_rows(win_T *wp, int used)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002429{
2430#ifdef FEAT_DIFF
2431 wp->w_filler_rows = 0;
2432#endif
2433 if (used == 0)
Bram Moolenaar85a20022019-12-21 18:25:54 +01002434 wp->w_empty_rows = 0; // single line that doesn't fit
Bram Moolenaar071d4272004-06-13 20:20:40 +00002435 else
2436 {
2437 wp->w_empty_rows = wp->w_height - used;
2438#ifdef FEAT_DIFF
2439 if (wp->w_botline <= wp->w_buffer->b_ml.ml_line_count)
2440 {
2441 wp->w_filler_rows = diff_check_fill(wp, wp->w_botline);
2442 if (wp->w_empty_rows > wp->w_filler_rows)
2443 wp->w_empty_rows -= wp->w_filler_rows;
2444 else
2445 {
2446 wp->w_filler_rows = wp->w_empty_rows;
2447 wp->w_empty_rows = 0;
2448 }
2449 }
2450#endif
2451 }
2452}
2453
2454/*
2455 * Recompute topline to put the cursor at the bottom of the window.
Bram Moolenaar8088ae92022-06-20 11:38:17 +01002456 * When scrolling scroll at least "min_scroll" lines.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002457 * If "set_topbot" is TRUE, set topline and botline first (for "zb").
2458 * This is messy stuff!!!
2459 */
2460 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002461scroll_cursor_bot(int min_scroll, int set_topbot)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002462{
2463 int used;
2464 int scrolled = 0;
2465 int extra = 0;
2466 int i;
2467 linenr_T line_count;
2468 linenr_T old_topline = curwin->w_topline;
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002469 int old_skipcol = curwin->w_skipcol;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002470 lineoff_T loff;
2471 lineoff_T boff;
2472#ifdef FEAT_DIFF
2473 int old_topfill = curwin->w_topfill;
2474 int fill_below_window;
2475#endif
2476 linenr_T old_botline = curwin->w_botline;
2477 linenr_T old_valid = curwin->w_valid;
2478 int old_empty_rows = curwin->w_empty_rows;
Bram Moolenaar85a20022019-12-21 18:25:54 +01002479 linenr_T cln; // Cursor Line Number
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002480 long so = get_scrolloff_value();
Luuk van Baalaa6ba302023-05-09 16:01:17 +01002481 int do_sms = curwin->w_p_wrap && curwin->w_p_sms;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002482
2483 cln = curwin->w_cursor.lnum;
2484 if (set_topbot)
2485 {
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002486 int set_skipcol = FALSE;
2487
Bram Moolenaar071d4272004-06-13 20:20:40 +00002488 used = 0;
2489 curwin->w_botline = cln + 1;
2490#ifdef FEAT_DIFF
2491 loff.fill = 0;
2492#endif
2493 for (curwin->w_topline = curwin->w_botline;
2494 curwin->w_topline > 1;
2495 curwin->w_topline = loff.lnum)
2496 {
2497 loff.lnum = curwin->w_topline;
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002498 topline_back_winheight(&loff, FALSE);
2499 if (loff.height == MAXCOL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002500 break;
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002501 if (used + loff.height > curwin->w_height)
2502 {
Luuk van Baalaa6ba302023-05-09 16:01:17 +01002503 if (do_sms)
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002504 {
2505 // 'smoothscroll' and 'wrap' are set. The above line is
2506 // too long to show in its entirety, so we show just a part
2507 // of it.
2508 if (used < curwin->w_height)
2509 {
2510 int plines_offset = used + loff.height
2511 - curwin->w_height;
2512 used = curwin->w_height;
2513#ifdef FEAT_DIFF
2514 curwin->w_topfill = loff.fill;
2515#endif
2516 curwin->w_topline = loff.lnum;
2517 curwin->w_skipcol = skipcol_from_plines(
2518 curwin, plines_offset);
2519 set_skipcol = TRUE;
2520 }
2521 }
2522 break;
2523 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002524 used += loff.height;
2525#ifdef FEAT_DIFF
2526 curwin->w_topfill = loff.fill;
2527#endif
2528 }
2529 set_empty_rows(curwin, used);
2530 curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
2531 if (curwin->w_topline != old_topline
2532#ifdef FEAT_DIFF
2533 || curwin->w_topfill != old_topfill
2534#endif
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002535 || set_skipcol
2536 || curwin->w_skipcol != 0)
2537 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002538 curwin->w_valid &= ~(VALID_WROW|VALID_CROW);
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002539 if (set_skipcol)
2540 redraw_later(UPD_NOT_VALID);
2541 else
2542 reset_skipcol();
2543 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002544 }
2545 else
2546 validate_botline();
2547
Bram Moolenaar85a20022019-12-21 18:25:54 +01002548 // The lines of the cursor line itself are always used.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002549#ifdef FEAT_DIFF
2550 used = plines_nofill(cln);
2551#else
2552 validate_cheight();
2553 used = curwin->w_cline_height;
2554#endif
2555
Yee Cheng Chin361895d2022-11-19 12:25:16 +00002556 // If the cursor is on or below botline, we will at least scroll by the
2557 // height of the cursor line, which is "used". Correct for empty lines,
2558 // which are really part of botline.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002559 if (cln >= curwin->w_botline)
2560 {
2561 scrolled = used;
2562 if (cln == curwin->w_botline)
2563 scrolled -= curwin->w_empty_rows;
Luuk van Baalaa6ba302023-05-09 16:01:17 +01002564 if (do_sms)
Yee Cheng Chin361895d2022-11-19 12:25:16 +00002565 {
Luuk van Baalaa6ba302023-05-09 16:01:17 +01002566 // 'smoothscroll' and 'wrap' are set.
Yee Cheng Chin361895d2022-11-19 12:25:16 +00002567 // Calculate how many screen lines the current top line of window
2568 // occupies. If it is occupying more than the entire window, we
2569 // need to scroll the additional clipped lines to scroll past the
2570 // top line before we can move on to the other lines.
2571 int top_plines =
2572#ifdef FEAT_DIFF
2573 plines_win_nofill
2574#else
2575 plines_win
2576#endif
2577 (curwin, curwin->w_topline, FALSE);
2578 int skip_lines = 0;
2579 int width1 = curwin->w_width - curwin_col_off();
2580 int width2 = width1 + curwin_col_off2();
2581 // similar formula is used in curs_columns()
2582 if (curwin->w_skipcol > width1)
2583 skip_lines += (curwin->w_skipcol - width1) / width2 + 1;
2584 else if (curwin->w_skipcol > 0)
2585 skip_lines = 1;
2586
2587 top_plines -= skip_lines;
2588 if (top_plines > curwin->w_height)
2589 {
2590 scrolled += (top_plines - curwin->w_height);
Yee Cheng Chin361895d2022-11-19 12:25:16 +00002591 }
2592 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002593 }
2594
2595 /*
2596 * Stop counting lines to scroll when
2597 * - hitting start of the file
2598 * - scrolled nothing or at least 'sj' lines
Bram Moolenaar375e3392019-01-31 18:26:10 +01002599 * - at least 'scrolloff' lines below the cursor
Bram Moolenaar071d4272004-06-13 20:20:40 +00002600 * - lines between botline and cursor have been counted
2601 */
2602#ifdef FEAT_FOLDING
2603 if (!hasFolding(curwin->w_cursor.lnum, &loff.lnum, &boff.lnum))
2604#endif
2605 {
2606 loff.lnum = cln;
2607 boff.lnum = cln;
2608 }
2609#ifdef FEAT_DIFF
2610 loff.fill = 0;
2611 boff.fill = 0;
2612 fill_below_window = diff_check_fill(curwin, curwin->w_botline)
2613 - curwin->w_filler_rows;
2614#endif
2615
2616 while (loff.lnum > 1)
2617 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01002618 // Stop when scrolled nothing or at least "min_scroll", found "extra"
2619 // context for 'scrolloff' and counted all lines below the window.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002620 if ((((scrolled <= 0 || scrolled >= min_scroll)
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02002621 && extra >= (mouse_dragging > 0 ? mouse_dragging - 1 : so))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002622 || boff.lnum + 1 > curbuf->b_ml.ml_line_count)
2623 && loff.lnum <= curwin->w_botline
2624#ifdef FEAT_DIFF
2625 && (loff.lnum < curwin->w_botline
2626 || loff.fill >= fill_below_window)
2627#endif
2628 )
2629 break;
2630
Bram Moolenaar85a20022019-12-21 18:25:54 +01002631 // Add one line above
Bram Moolenaar071d4272004-06-13 20:20:40 +00002632 topline_back(&loff);
Bram Moolenaarbacd9da2010-02-17 18:20:37 +01002633 if (loff.height == MAXCOL)
2634 used = MAXCOL;
2635 else
2636 used += loff.height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002637 if (used > curwin->w_height)
2638 break;
2639 if (loff.lnum >= curwin->w_botline
2640#ifdef FEAT_DIFF
2641 && (loff.lnum > curwin->w_botline
2642 || loff.fill <= fill_below_window)
2643#endif
2644 )
2645 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01002646 // Count screen lines that are below the window.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002647 scrolled += loff.height;
2648 if (loff.lnum == curwin->w_botline
2649#ifdef FEAT_DIFF
Bram Moolenaar4e303c82018-11-24 14:27:44 +01002650 && loff.fill == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002651#endif
2652 )
2653 scrolled -= curwin->w_empty_rows;
2654 }
2655
2656 if (boff.lnum < curbuf->b_ml.ml_line_count)
2657 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01002658 // Add one line below
Bram Moolenaar071d4272004-06-13 20:20:40 +00002659 botline_forw(&boff);
2660 used += boff.height;
2661 if (used > curwin->w_height)
2662 break;
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02002663 if (extra < ( mouse_dragging > 0 ? mouse_dragging - 1 : so)
2664 || scrolled < min_scroll)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002665 {
2666 extra += boff.height;
2667 if (boff.lnum >= curwin->w_botline
2668#ifdef FEAT_DIFF
2669 || (boff.lnum + 1 == curwin->w_botline
2670 && boff.fill > curwin->w_filler_rows)
2671#endif
2672 )
2673 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01002674 // Count screen lines that are below the window.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002675 scrolled += boff.height;
2676 if (boff.lnum == curwin->w_botline
2677#ifdef FEAT_DIFF
2678 && boff.fill == 0
2679#endif
2680 )
2681 scrolled -= curwin->w_empty_rows;
2682 }
2683 }
2684 }
2685 }
2686
Bram Moolenaar85a20022019-12-21 18:25:54 +01002687 // curwin->w_empty_rows is larger, no need to scroll
Bram Moolenaar071d4272004-06-13 20:20:40 +00002688 if (scrolled <= 0)
2689 line_count = 0;
Bram Moolenaar85a20022019-12-21 18:25:54 +01002690 // more than a screenfull, don't scroll but redraw
Bram Moolenaar071d4272004-06-13 20:20:40 +00002691 else if (used > curwin->w_height)
2692 line_count = used;
Bram Moolenaar85a20022019-12-21 18:25:54 +01002693 // scroll minimal number of lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00002694 else
2695 {
2696 line_count = 0;
2697#ifdef FEAT_DIFF
2698 boff.fill = curwin->w_topfill;
2699#endif
2700 boff.lnum = curwin->w_topline - 1;
2701 for (i = 0; i < scrolled && boff.lnum < curwin->w_botline; )
2702 {
2703 botline_forw(&boff);
2704 i += boff.height;
2705 ++line_count;
2706 }
Bram Moolenaar85a20022019-12-21 18:25:54 +01002707 if (i < scrolled) // below curwin->w_botline, don't scroll
Bram Moolenaar071d4272004-06-13 20:20:40 +00002708 line_count = 9999;
2709 }
2710
2711 /*
2712 * Scroll up if the cursor is off the bottom of the screen a bit.
2713 * Otherwise put it at 1/2 of the screen.
2714 */
2715 if (line_count >= curwin->w_height && line_count > min_scroll)
Bram Moolenaar1d6539c2023-02-14 17:41:20 +00002716 scroll_cursor_halfway(FALSE, TRUE);
Luuk van Baalaa6ba302023-05-09 16:01:17 +01002717 else if (line_count > 0)
Bram Moolenaar9bab7a02022-10-06 14:57:53 +01002718 {
Luuk van Baalaa6ba302023-05-09 16:01:17 +01002719 if (do_sms)
2720 scrollup(scrolled, TRUE); // TODO
2721 else
2722 scrollup(line_count, TRUE);
Bram Moolenaar9bab7a02022-10-06 14:57:53 +01002723 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002724
2725 /*
2726 * If topline didn't change we need to restore w_botline and w_empty_rows
2727 * (we changed them).
2728 * If topline did change, update_screen() will set botline.
2729 */
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002730 if (curwin->w_topline == old_topline
2731 && curwin->w_skipcol == old_skipcol
2732 && set_topbot)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002733 {
2734 curwin->w_botline = old_botline;
2735 curwin->w_empty_rows = old_empty_rows;
2736 curwin->w_valid = old_valid;
2737 }
2738 curwin->w_valid |= VALID_TOPLINE;
2739}
2740
2741/*
2742 * Recompute topline to put the cursor halfway the window
2743 * If "atend" is TRUE, also put it halfway at the end of the file.
2744 */
2745 void
Bram Moolenaar1d6539c2023-02-14 17:41:20 +00002746scroll_cursor_halfway(int atend, int prefer_above)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002747{
2748 int above = 0;
2749 linenr_T topline;
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002750 colnr_T skipcol = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002751#ifdef FEAT_DIFF
2752 int topfill = 0;
2753#endif
2754 int below = 0;
2755 int used;
2756 lineoff_T loff;
2757 lineoff_T boff;
Bram Moolenaarb8e23052014-02-11 18:58:09 +01002758#ifdef FEAT_DIFF
Bram Moolenaar12a0f222014-02-11 15:47:46 +01002759 linenr_T old_topline = curwin->w_topline;
Bram Moolenaarb8e23052014-02-11 18:58:09 +01002760#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002761
Bram Moolenaar3697c9b2020-09-26 22:03:00 +02002762#ifdef FEAT_PROP_POPUP
2763 // if the width changed this needs to be updated first
2764 may_update_popup_position();
2765#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002766 loff.lnum = boff.lnum = curwin->w_cursor.lnum;
2767#ifdef FEAT_FOLDING
2768 (void)hasFolding(loff.lnum, &loff.lnum, &boff.lnum);
2769#endif
2770#ifdef FEAT_DIFF
2771 used = plines_nofill(loff.lnum);
2772 loff.fill = 0;
2773 boff.fill = 0;
2774#else
2775 used = plines(loff.lnum);
2776#endif
2777 topline = loff.lnum;
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002778
Luuk van Baal3ce8c382023-05-08 15:51:14 +01002779 int want_height;
Luuk van Baal6c018682023-05-11 18:38:14 +01002780 int do_sms = curwin->w_p_wrap && curwin->w_p_sms;
2781 if (do_sms)
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002782 {
2783 // 'smoothscroll' and 'wrap' are set
Luuk van Baal3ce8c382023-05-08 15:51:14 +01002784 if (atend)
2785 {
2786 want_height = (curwin->w_height - used) / 2;
2787 used = 0;
2788 }
2789 else
2790 want_height = curwin->w_height;
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002791 }
2792
Bram Moolenaar071d4272004-06-13 20:20:40 +00002793 while (topline > 1)
2794 {
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002795 // If using smoothscroll, we can precisely scroll to the
2796 // exact point where the cursor is halfway down the screen.
Luuk van Baal6c018682023-05-11 18:38:14 +01002797 if (do_sms)
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002798 {
2799 topline_back_winheight(&loff, FALSE);
2800 if (loff.height == MAXCOL)
2801 break;
Luuk van Baal3ce8c382023-05-08 15:51:14 +01002802 used += loff.height;
2803 if (!atend && boff.lnum < curbuf->b_ml.ml_line_count)
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002804 {
Luuk van Baal3ce8c382023-05-08 15:51:14 +01002805 botline_forw(&boff);
2806 used += boff.height;
2807 }
2808 if (used > want_height)
2809 {
2810 if (used - loff.height < want_height)
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002811 {
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002812 topline = loff.lnum;
2813#ifdef FEAT_DIFF
2814 topfill = loff.fill;
2815#endif
Luuk van Baal3ce8c382023-05-08 15:51:14 +01002816 skipcol = skipcol_from_plines(curwin, used - want_height);
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002817 }
2818 break;
2819 }
2820 topline = loff.lnum;
2821#ifdef FEAT_DIFF
2822 topfill = loff.fill;
2823#endif
2824 continue;
2825 }
2826
2827 // If not using smoothscroll, we have to iteratively find how many
2828 // lines to scroll down to roughly fit the cursor.
Bram Moolenaar1d6539c2023-02-14 17:41:20 +00002829 // This may not be right in the middle if the lines'
2830 // physical height > 1 (e.g. 'wrap' is on).
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002831
Bram Moolenaar1d6539c2023-02-14 17:41:20 +00002832 // Depending on "prefer_above" we add a line above or below first.
2833 // Loop twice to avoid duplicating code.
2834 int done = FALSE;
2835 for (int round = 1; round <= 2; ++round)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002836 {
Bram Moolenaar1d6539c2023-02-14 17:41:20 +00002837 if (prefer_above ? (round == 2 && below < above)
2838 : (round == 1 && below <= above))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002839 {
Bram Moolenaar1d6539c2023-02-14 17:41:20 +00002840 // add a line below the cursor
2841 if (boff.lnum < curbuf->b_ml.ml_line_count)
2842 {
2843 botline_forw(&boff);
2844 used += boff.height;
2845 if (used > curwin->w_height)
2846 {
2847 done = TRUE;
2848 break;
2849 }
2850 below += boff.height;
2851 }
2852 else
2853 {
2854 ++below; // count a "~" line
2855 if (atend)
2856 ++used;
2857 }
2858 }
2859
2860 if (prefer_above ? (round == 1 && below >= above)
2861 : (round == 1 && below > above))
2862 {
2863 // add a line above the cursor
2864 topline_back(&loff);
2865 if (loff.height == MAXCOL)
2866 used = MAXCOL;
2867 else
2868 used += loff.height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002869 if (used > curwin->w_height)
Bram Moolenaar1d6539c2023-02-14 17:41:20 +00002870 {
2871 done = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002872 break;
Bram Moolenaar1d6539c2023-02-14 17:41:20 +00002873 }
2874 above += loff.height;
2875 topline = loff.lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002876#ifdef FEAT_DIFF
Bram Moolenaar1d6539c2023-02-14 17:41:20 +00002877 topfill = loff.fill;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002878#endif
Bram Moolenaar1d6539c2023-02-14 17:41:20 +00002879 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002880 }
Bram Moolenaar1d6539c2023-02-14 17:41:20 +00002881 if (done)
2882 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002883 }
Bram Moolenaar1d6539c2023-02-14 17:41:20 +00002884
Bram Moolenaar071d4272004-06-13 20:20:40 +00002885#ifdef FEAT_FOLDING
2886 if (!hasFolding(topline, &curwin->w_topline, NULL))
2887#endif
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002888 {
2889 if (curwin->w_topline != topline
Luuk van Baal3ce8c382023-05-08 15:51:14 +01002890 || skipcol != 0
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002891 || curwin->w_skipcol != 0)
2892 {
2893 curwin->w_topline = topline;
Luuk van Baal3ce8c382023-05-08 15:51:14 +01002894 if (skipcol != 0)
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002895 {
2896 curwin->w_skipcol = skipcol;
2897 redraw_later(UPD_NOT_VALID);
2898 }
Luuk van Baal6c018682023-05-11 18:38:14 +01002899 else if (do_sms)
Bram Moolenaardb4d88c2022-12-31 15:13:22 +00002900 reset_skipcol();
2901 }
2902 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002903#ifdef FEAT_DIFF
2904 curwin->w_topfill = topfill;
Bram Moolenaar12a0f222014-02-11 15:47:46 +01002905 if (old_topline > curwin->w_topline + curwin->w_height)
2906 curwin->w_botfill = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002907 check_topfill(curwin, FALSE);
2908#endif
2909 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
2910 curwin->w_valid |= VALID_TOPLINE;
2911}
2912
2913/*
2914 * Correct the cursor position so that it is in a part of the screen at least
Bram Moolenaar375e3392019-01-31 18:26:10 +01002915 * 'scrolloff' lines from the top and bottom, if possible.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002916 * If not possible, put it at the same position as scroll_cursor_halfway().
2917 * When called topline must be valid!
2918 */
2919 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002920cursor_correct(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002921{
Bram Moolenaar85a20022019-12-21 18:25:54 +01002922 int above = 0; // screen lines above topline
Bram Moolenaar071d4272004-06-13 20:20:40 +00002923 linenr_T topline;
Bram Moolenaar85a20022019-12-21 18:25:54 +01002924 int below = 0; // screen lines below botline
Bram Moolenaar071d4272004-06-13 20:20:40 +00002925 linenr_T botline;
2926 int above_wanted, below_wanted;
Bram Moolenaar85a20022019-12-21 18:25:54 +01002927 linenr_T cln; // Cursor Line Number
Bram Moolenaar071d4272004-06-13 20:20:40 +00002928 int max_off;
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002929 long so = get_scrolloff_value();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002930
2931 /*
2932 * How many lines we would like to have above/below the cursor depends on
2933 * whether the first/last line of the file is on screen.
2934 */
Bram Moolenaar375e3392019-01-31 18:26:10 +01002935 above_wanted = so;
2936 below_wanted = so;
Bram Moolenaar9964e462007-05-05 17:54:07 +00002937 if (mouse_dragging > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002938 {
2939 above_wanted = mouse_dragging - 1;
2940 below_wanted = mouse_dragging - 1;
2941 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002942 if (curwin->w_topline == 1)
2943 {
2944 above_wanted = 0;
2945 max_off = curwin->w_height / 2;
2946 if (below_wanted > max_off)
2947 below_wanted = max_off;
2948 }
2949 validate_botline();
2950 if (curwin->w_botline == curbuf->b_ml.ml_line_count + 1
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02002951 && mouse_dragging == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002952 {
2953 below_wanted = 0;
2954 max_off = (curwin->w_height - 1) / 2;
2955 if (above_wanted > max_off)
2956 above_wanted = max_off;
2957 }
2958
2959 /*
2960 * If there are sufficient file-lines above and below the cursor, we can
2961 * return now.
2962 */
2963 cln = curwin->w_cursor.lnum;
2964 if (cln >= curwin->w_topline + above_wanted
2965 && cln < curwin->w_botline - below_wanted
2966#ifdef FEAT_FOLDING
2967 && !hasAnyFolding(curwin)
2968#endif
2969 )
2970 return;
2971
Bram Moolenaarc9121f72022-10-14 20:09:04 +01002972 if (curwin->w_p_sms && !curwin->w_p_wrap)
2973 {
Luuk van Baalc8502f92023-05-06 12:40:15 +01002974 // 'smoothscroll' is active
Bram Moolenaarc9121f72022-10-14 20:09:04 +01002975 if (curwin->w_cline_height == curwin->w_height)
2976 {
2977 // The cursor line just fits in the window, don't scroll.
Bram Moolenaarf32fb932022-11-17 11:34:38 +00002978 reset_skipcol();
Bram Moolenaarc9121f72022-10-14 20:09:04 +01002979 return;
2980 }
2981 // TODO: If the cursor line doesn't fit in the window then only adjust
2982 // w_skipcol.
2983 }
2984
Bram Moolenaar071d4272004-06-13 20:20:40 +00002985 /*
2986 * Narrow down the area where the cursor can be put by taking lines from
2987 * the top and the bottom until:
2988 * - the desired context lines are found
2989 * - the lines from the top is past the lines from the bottom
2990 */
2991 topline = curwin->w_topline;
2992 botline = curwin->w_botline - 1;
2993#ifdef FEAT_DIFF
Bram Moolenaar85a20022019-12-21 18:25:54 +01002994 // count filler lines as context
Bram Moolenaar071d4272004-06-13 20:20:40 +00002995 above = curwin->w_topfill;
2996 below = curwin->w_filler_rows;
2997#endif
2998 while ((above < above_wanted || below < below_wanted) && topline < botline)
2999 {
3000 if (below < below_wanted && (below <= above || above >= above_wanted))
3001 {
3002#ifdef FEAT_FOLDING
3003 if (hasFolding(botline, &botline, NULL))
3004 ++below;
3005 else
3006#endif
3007 below += plines(botline);
3008 --botline;
3009 }
3010 if (above < above_wanted && (above < below || below >= below_wanted))
3011 {
3012#ifdef FEAT_FOLDING
3013 if (hasFolding(topline, NULL, &topline))
3014 ++above;
3015 else
3016#endif
Bram Moolenaar43335ea2015-09-09 20:59:37 +02003017 above += PLINES_NOFILL(topline);
3018#ifdef FEAT_DIFF
Bram Moolenaar85a20022019-12-21 18:25:54 +01003019 // Count filler lines below this line as context.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003020 if (topline < botline)
3021 above += diff_check_fill(curwin, topline + 1);
3022#endif
3023 ++topline;
3024 }
3025 }
3026 if (topline == botline || botline == 0)
3027 curwin->w_cursor.lnum = topline;
3028 else if (topline > botline)
3029 curwin->w_cursor.lnum = botline;
3030 else
3031 {
3032 if (cln < topline && curwin->w_topline > 1)
3033 {
3034 curwin->w_cursor.lnum = topline;
3035 curwin->w_valid &=
3036 ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW);
3037 }
3038 if (cln > botline && curwin->w_botline <= curbuf->b_ml.ml_line_count)
3039 {
3040 curwin->w_cursor.lnum = botline;
3041 curwin->w_valid &=
3042 ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW);
3043 }
3044 }
3045 curwin->w_valid |= VALID_TOPLINE;
3046}
3047
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +01003048static void get_scroll_overlap(lineoff_T *lp, int dir);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003049
3050/*
Bram Moolenaar88456cd2022-11-18 22:14:09 +00003051 * Move screen "count" pages up ("dir" is BACKWARD) or down ("dir" is FORWARD)
3052 * and update the screen.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003053 *
Yee Cheng Chin81ba26e2022-11-18 12:52:50 +00003054 * Return FAIL for failure, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003055 */
3056 int
Bram Moolenaar9b578142016-01-30 19:39:49 +01003057onepage(int dir, long count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003058{
3059 long n;
3060 int retval = OK;
3061 lineoff_T loff;
3062 linenr_T old_topline = curwin->w_topline;
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01003063 long so = get_scrolloff_value();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003064
Bram Moolenaar85a20022019-12-21 18:25:54 +01003065 if (curbuf->b_ml.ml_line_count == 1) // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00003066 {
3067 beep_flush();
3068 return FAIL;
3069 }
3070
3071 for ( ; count > 0; --count)
3072 {
3073 validate_botline();
3074 /*
3075 * It's an error to move a page up when the first line is already on
3076 * the screen. It's an error to move a page down when the last line
3077 * is on the screen and the topline is 'scrolloff' lines from the
3078 * last line.
3079 */
3080 if (dir == FORWARD
Bram Moolenaar375e3392019-01-31 18:26:10 +01003081 ? ((curwin->w_topline >= curbuf->b_ml.ml_line_count - so)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003082 && curwin->w_botline > curbuf->b_ml.ml_line_count)
3083 : (curwin->w_topline == 1
3084#ifdef FEAT_DIFF
3085 && curwin->w_topfill ==
3086 diff_check_fill(curwin, curwin->w_topline)
3087#endif
3088 ))
3089 {
3090 beep_flush();
3091 retval = FAIL;
3092 break;
3093 }
3094
3095#ifdef FEAT_DIFF
3096 loff.fill = 0;
3097#endif
3098 if (dir == FORWARD)
3099 {
Bram Moolenaara1f4cb92016-11-06 15:25:42 +01003100 if (ONE_WINDOW && p_window > 0 && p_window < Rows - 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003101 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003102 // Vi compatible scrolling
Bram Moolenaar4399ef42005-02-12 14:29:27 +00003103 if (p_window <= 2)
3104 ++curwin->w_topline;
3105 else
3106 curwin->w_topline += p_window - 2;
3107 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
3108 curwin->w_topline = curbuf->b_ml.ml_line_count;
3109 curwin->w_cursor.lnum = curwin->w_topline;
3110 }
3111 else if (curwin->w_botline > curbuf->b_ml.ml_line_count)
3112 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003113 // at end of file
Bram Moolenaar071d4272004-06-13 20:20:40 +00003114 curwin->w_topline = curbuf->b_ml.ml_line_count;
3115#ifdef FEAT_DIFF
3116 curwin->w_topfill = 0;
3117#endif
3118 curwin->w_valid &= ~(VALID_WROW|VALID_CROW);
3119 }
3120 else
3121 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003122 // For the overlap, start with the line just below the window
3123 // and go upwards.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003124 loff.lnum = curwin->w_botline;
3125#ifdef FEAT_DIFF
3126 loff.fill = diff_check_fill(curwin, loff.lnum)
3127 - curwin->w_filler_rows;
3128#endif
3129 get_scroll_overlap(&loff, -1);
3130 curwin->w_topline = loff.lnum;
3131#ifdef FEAT_DIFF
3132 curwin->w_topfill = loff.fill;
3133 check_topfill(curwin, FALSE);
3134#endif
3135 curwin->w_cursor.lnum = curwin->w_topline;
3136 curwin->w_valid &= ~(VALID_WCOL|VALID_CHEIGHT|VALID_WROW|
3137 VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
3138 }
3139 }
Bram Moolenaar85a20022019-12-21 18:25:54 +01003140 else // dir == BACKWARDS
Bram Moolenaar071d4272004-06-13 20:20:40 +00003141 {
3142#ifdef FEAT_DIFF
3143 if (curwin->w_topline == 1)
3144 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003145 // Include max number of filler lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00003146 max_topfill();
3147 continue;
3148 }
3149#endif
Bram Moolenaara1f4cb92016-11-06 15:25:42 +01003150 if (ONE_WINDOW && p_window > 0 && p_window < Rows - 1)
Bram Moolenaar4399ef42005-02-12 14:29:27 +00003151 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003152 // Vi compatible scrolling (sort of)
Bram Moolenaar4399ef42005-02-12 14:29:27 +00003153 if (p_window <= 2)
3154 --curwin->w_topline;
3155 else
3156 curwin->w_topline -= p_window - 2;
3157 if (curwin->w_topline < 1)
3158 curwin->w_topline = 1;
3159 curwin->w_cursor.lnum = curwin->w_topline + p_window - 1;
3160 if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
3161 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
3162 continue;
3163 }
3164
Bram Moolenaar85a20022019-12-21 18:25:54 +01003165 // Find the line at the top of the window that is going to be the
3166 // line at the bottom of the window. Make sure this results in
3167 // the same line as before doing CTRL-F.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003168 loff.lnum = curwin->w_topline - 1;
3169#ifdef FEAT_DIFF
3170 loff.fill = diff_check_fill(curwin, loff.lnum + 1)
3171 - curwin->w_topfill;
3172#endif
3173 get_scroll_overlap(&loff, 1);
3174
3175 if (loff.lnum >= curbuf->b_ml.ml_line_count)
3176 {
3177 loff.lnum = curbuf->b_ml.ml_line_count;
3178#ifdef FEAT_DIFF
3179 loff.fill = 0;
3180 }
3181 else
3182 {
3183 botline_topline(&loff);
3184#endif
3185 }
3186 curwin->w_cursor.lnum = loff.lnum;
3187
Bram Moolenaar85a20022019-12-21 18:25:54 +01003188 // Find the line just above the new topline to get the right line
3189 // at the bottom of the window.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003190 n = 0;
3191 while (n <= curwin->w_height && loff.lnum >= 1)
3192 {
3193 topline_back(&loff);
Bram Moolenaarbacd9da2010-02-17 18:20:37 +01003194 if (loff.height == MAXCOL)
3195 n = MAXCOL;
3196 else
3197 n += loff.height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003198 }
Bram Moolenaar85a20022019-12-21 18:25:54 +01003199 if (loff.lnum < 1) // at begin of file
Bram Moolenaar071d4272004-06-13 20:20:40 +00003200 {
3201 curwin->w_topline = 1;
3202#ifdef FEAT_DIFF
3203 max_topfill();
3204#endif
3205 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
3206 }
3207 else
3208 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003209 // Go two lines forward again.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003210#ifdef FEAT_DIFF
3211 topline_botline(&loff);
3212#endif
3213 botline_forw(&loff);
3214 botline_forw(&loff);
3215#ifdef FEAT_DIFF
3216 botline_topline(&loff);
3217#endif
3218#ifdef FEAT_FOLDING
Bram Moolenaar85a20022019-12-21 18:25:54 +01003219 // We're at the wrong end of a fold now.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003220 (void)hasFolding(loff.lnum, &loff.lnum, NULL);
3221#endif
3222
Bram Moolenaar85a20022019-12-21 18:25:54 +01003223 // Always scroll at least one line. Avoid getting stuck on
3224 // very long lines.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003225 if (loff.lnum >= curwin->w_topline
3226#ifdef FEAT_DIFF
3227 && (loff.lnum > curwin->w_topline
3228 || loff.fill >= curwin->w_topfill)
3229#endif
3230 )
3231 {
3232#ifdef FEAT_DIFF
Bram Moolenaar85a20022019-12-21 18:25:54 +01003233 // First try using the maximum number of filler lines. If
3234 // that's not enough, backup one line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003235 loff.fill = curwin->w_topfill;
3236 if (curwin->w_topfill < diff_check_fill(curwin,
3237 curwin->w_topline))
3238 max_topfill();
3239 if (curwin->w_topfill == loff.fill)
3240#endif
3241 {
3242 --curwin->w_topline;
3243#ifdef FEAT_DIFF
3244 curwin->w_topfill = 0;
3245#endif
3246 }
3247 comp_botline(curwin);
3248 curwin->w_cursor.lnum = curwin->w_botline - 1;
Bram Moolenaar3d6db142014-03-28 21:49:32 +01003249 curwin->w_valid &=
3250 ~(VALID_WCOL|VALID_CHEIGHT|VALID_WROW|VALID_CROW);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003251 }
3252 else
3253 {
3254 curwin->w_topline = loff.lnum;
3255#ifdef FEAT_DIFF
3256 curwin->w_topfill = loff.fill;
3257 check_topfill(curwin, FALSE);
3258#endif
3259 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
3260 }
3261 }
3262 }
3263 }
3264#ifdef FEAT_FOLDING
3265 foldAdjustCursor();
3266#endif
3267 cursor_correct();
Bram Moolenaarbc54f3f2016-09-04 14:34:28 +02003268 check_cursor_col();
Bram Moolenaar7c626922005-02-07 22:01:03 +00003269 if (retval == OK)
3270 beginline(BL_SOL | BL_FIX);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003271 curwin->w_valid &= ~(VALID_WCOL|VALID_WROW|VALID_VIRTCOL);
3272
Bram Moolenaar907dad72018-07-10 15:07:15 +02003273 if (retval == OK && dir == FORWARD)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003274 {
Bram Moolenaar907dad72018-07-10 15:07:15 +02003275 // Avoid the screen jumping up and down when 'scrolloff' is non-zero.
3276 // But make sure we scroll at least one line (happens with mix of long
3277 // wrapping lines and non-wrapping line).
3278 if (check_top_offset())
Bram Moolenaar071d4272004-06-13 20:20:40 +00003279 {
Bram Moolenaar907dad72018-07-10 15:07:15 +02003280 scroll_cursor_top(1, FALSE);
3281 if (curwin->w_topline <= old_topline
3282 && old_topline < curbuf->b_ml.ml_line_count)
3283 {
3284 curwin->w_topline = old_topline + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003285#ifdef FEAT_FOLDING
Bram Moolenaar907dad72018-07-10 15:07:15 +02003286 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
3287#endif
3288 }
3289 }
3290#ifdef FEAT_FOLDING
3291 else if (curwin->w_botline > curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003292 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
3293#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003294 }
3295
Bram Moolenaara4d158b2022-08-14 14:17:45 +01003296 redraw_later(UPD_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003297 return retval;
3298}
3299
3300/*
3301 * Decide how much overlap to use for page-up or page-down scrolling.
3302 * This is symmetric, so that doing both keeps the same lines displayed.
3303 * Three lines are examined:
3304 *
3305 * before CTRL-F after CTRL-F / before CTRL-B
3306 * etc. l1
3307 * l1 last but one line ------------
3308 * l2 last text line l2 top text line
3309 * ------------- l3 second text line
3310 * l3 etc.
3311 */
3312 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003313get_scroll_overlap(lineoff_T *lp, int dir)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003314{
3315 int h1, h2, h3, h4;
3316 int min_height = curwin->w_height - 2;
3317 lineoff_T loff0, loff1, loff2;
3318
3319#ifdef FEAT_DIFF
3320 if (lp->fill > 0)
3321 lp->height = 1;
3322 else
3323 lp->height = plines_nofill(lp->lnum);
3324#else
3325 lp->height = plines(lp->lnum);
3326#endif
3327 h1 = lp->height;
3328 if (h1 > min_height)
Bram Moolenaar85a20022019-12-21 18:25:54 +01003329 return; // no overlap
Bram Moolenaar071d4272004-06-13 20:20:40 +00003330
3331 loff0 = *lp;
3332 if (dir > 0)
3333 botline_forw(lp);
3334 else
3335 topline_back(lp);
3336 h2 = lp->height;
Bram Moolenaarf4f19562012-11-28 18:22:11 +01003337 if (h2 == MAXCOL || h2 + h1 > min_height)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003338 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003339 *lp = loff0; // no overlap
Bram Moolenaar071d4272004-06-13 20:20:40 +00003340 return;
3341 }
3342
3343 loff1 = *lp;
3344 if (dir > 0)
3345 botline_forw(lp);
3346 else
3347 topline_back(lp);
3348 h3 = lp->height;
Bram Moolenaarf4f19562012-11-28 18:22:11 +01003349 if (h3 == MAXCOL || h3 + h2 > min_height)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003350 {
Bram Moolenaar85a20022019-12-21 18:25:54 +01003351 *lp = loff0; // no overlap
Bram Moolenaar071d4272004-06-13 20:20:40 +00003352 return;
3353 }
3354
3355 loff2 = *lp;
3356 if (dir > 0)
3357 botline_forw(lp);
3358 else
3359 topline_back(lp);
3360 h4 = lp->height;
Bram Moolenaarf4f19562012-11-28 18:22:11 +01003361 if (h4 == MAXCOL || h4 + h3 + h2 > min_height || h3 + h2 + h1 > min_height)
Bram Moolenaar85a20022019-12-21 18:25:54 +01003362 *lp = loff1; // 1 line overlap
Bram Moolenaar071d4272004-06-13 20:20:40 +00003363 else
Bram Moolenaar85a20022019-12-21 18:25:54 +01003364 *lp = loff2; // 2 lines overlap
Bram Moolenaar071d4272004-06-13 20:20:40 +00003365}
3366
Bram Moolenaar071d4272004-06-13 20:20:40 +00003367/*
3368 * Scroll 'scroll' lines up or down.
3369 */
3370 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003371halfpage(int flag, linenr_T Prenum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003372{
3373 long scrolled = 0;
3374 int i;
3375 int n;
3376 int room;
3377
3378 if (Prenum)
3379 curwin->w_p_scr = (Prenum > curwin->w_height) ?
3380 curwin->w_height : Prenum;
3381 n = (curwin->w_p_scr <= curwin->w_height) ?
3382 curwin->w_p_scr : curwin->w_height;
3383
Bram Moolenaard5d37532017-03-27 23:02:07 +02003384 update_topline();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003385 validate_botline();
3386 room = curwin->w_empty_rows;
3387#ifdef FEAT_DIFF
3388 room += curwin->w_filler_rows;
3389#endif
3390 if (flag)
3391 {
3392 /*
3393 * scroll the text up
3394 */
3395 while (n > 0 && curwin->w_botline <= curbuf->b_ml.ml_line_count)
3396 {
3397#ifdef FEAT_DIFF
3398 if (curwin->w_topfill > 0)
3399 {
3400 i = 1;
Bram Moolenaar8455c5e2020-07-14 21:22:30 +02003401 --n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003402 --curwin->w_topfill;
3403 }
3404 else
3405#endif
3406 {
Bram Moolenaar43335ea2015-09-09 20:59:37 +02003407 i = PLINES_NOFILL(curwin->w_topline);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003408 n -= i;
3409 if (n < 0 && scrolled > 0)
3410 break;
3411#ifdef FEAT_FOLDING
3412 (void)hasFolding(curwin->w_topline, NULL, &curwin->w_topline);
3413#endif
3414 ++curwin->w_topline;
3415#ifdef FEAT_DIFF
3416 curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline);
3417#endif
3418
Bram Moolenaar071d4272004-06-13 20:20:40 +00003419 if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
3420 {
3421 ++curwin->w_cursor.lnum;
3422 curwin->w_valid &=
3423 ~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL);
3424 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003425 }
3426 curwin->w_valid &= ~(VALID_CROW|VALID_WROW);
3427 scrolled += i;
3428
3429 /*
3430 * Correct w_botline for changed w_topline.
3431 * Won't work when there are filler lines.
3432 */
3433#ifdef FEAT_DIFF
3434 if (curwin->w_p_diff)
3435 curwin->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP);
3436 else
3437#endif
3438 {
3439 room += i;
3440 do
3441 {
3442 i = plines(curwin->w_botline);
3443 if (i > room)
3444 break;
3445#ifdef FEAT_FOLDING
3446 (void)hasFolding(curwin->w_botline, NULL,
3447 &curwin->w_botline);
3448#endif
3449 ++curwin->w_botline;
3450 room -= i;
3451 } while (curwin->w_botline <= curbuf->b_ml.ml_line_count);
3452 }
3453 }
3454
Bram Moolenaar071d4272004-06-13 20:20:40 +00003455 /*
3456 * When hit bottom of the file: move cursor down.
3457 */
3458 if (n > 0)
3459 {
3460# ifdef FEAT_FOLDING
3461 if (hasAnyFolding(curwin))
3462 {
3463 while (--n >= 0
3464 && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
3465 {
3466 (void)hasFolding(curwin->w_cursor.lnum, NULL,
3467 &curwin->w_cursor.lnum);
3468 ++curwin->w_cursor.lnum;
3469 }
3470 }
3471 else
3472# endif
3473 curwin->w_cursor.lnum += n;
3474 check_cursor_lnum();
3475 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003476 }
3477 else
3478 {
3479 /*
3480 * scroll the text down
3481 */
3482 while (n > 0 && curwin->w_topline > 1)
3483 {
3484#ifdef FEAT_DIFF
3485 if (curwin->w_topfill < diff_check_fill(curwin, curwin->w_topline))
3486 {
3487 i = 1;
Bram Moolenaard00e0242019-03-20 21:42:20 +01003488 --n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003489 ++curwin->w_topfill;
3490 }
3491 else
3492#endif
3493 {
Bram Moolenaar43335ea2015-09-09 20:59:37 +02003494 i = PLINES_NOFILL(curwin->w_topline - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003495 n -= i;
3496 if (n < 0 && scrolled > 0)
3497 break;
3498 --curwin->w_topline;
3499#ifdef FEAT_FOLDING
3500 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
3501#endif
3502#ifdef FEAT_DIFF
3503 curwin->w_topfill = 0;
3504#endif
3505 }
3506 curwin->w_valid &= ~(VALID_CROW|VALID_WROW|
3507 VALID_BOTLINE|VALID_BOTLINE_AP);
3508 scrolled += i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003509 if (curwin->w_cursor.lnum > 1)
3510 {
3511 --curwin->w_cursor.lnum;
3512 curwin->w_valid &= ~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL);
3513 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003514 }
Bram Moolenaard00e0242019-03-20 21:42:20 +01003515
Bram Moolenaar071d4272004-06-13 20:20:40 +00003516 /*
3517 * When hit top of the file: move cursor up.
3518 */
3519 if (n > 0)
3520 {
3521 if (curwin->w_cursor.lnum <= (linenr_T)n)
3522 curwin->w_cursor.lnum = 1;
3523 else
3524# ifdef FEAT_FOLDING
3525 if (hasAnyFolding(curwin))
3526 {
3527 while (--n >= 0 && curwin->w_cursor.lnum > 1)
3528 {
3529 --curwin->w_cursor.lnum;
3530 (void)hasFolding(curwin->w_cursor.lnum,
3531 &curwin->w_cursor.lnum, NULL);
3532 }
3533 }
3534 else
3535# endif
3536 curwin->w_cursor.lnum -= n;
3537 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003538 }
3539# ifdef FEAT_FOLDING
Bram Moolenaar85a20022019-12-21 18:25:54 +01003540 // Move cursor to first line of closed fold.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003541 foldAdjustCursor();
3542# endif
3543#ifdef FEAT_DIFF
3544 check_topfill(curwin, !flag);
3545#endif
3546 cursor_correct();
3547 beginline(BL_SOL | BL_FIX);
Bram Moolenaara4d158b2022-08-14 14:17:45 +01003548 redraw_later(UPD_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003549}
Bram Moolenaar860cae12010-06-05 23:22:07 +02003550
Bram Moolenaar860cae12010-06-05 23:22:07 +02003551 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003552do_check_cursorbind(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003553{
3554 linenr_T line = curwin->w_cursor.lnum;
Bram Moolenaar1ea69b72012-03-16 19:24:26 +01003555 colnr_T col = curwin->w_cursor.col;
Bram Moolenaar1ea69b72012-03-16 19:24:26 +01003556 colnr_T coladd = curwin->w_cursor.coladd;
Bram Moolenaar524780d2012-03-28 14:19:50 +02003557 colnr_T curswant = curwin->w_curswant;
3558 int set_curswant = curwin->w_set_curswant;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003559 win_T *old_curwin = curwin;
3560 buf_T *old_curbuf = curbuf;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003561 int old_VIsual_select = VIsual_select;
3562 int old_VIsual_active = VIsual_active;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003563
3564 /*
3565 * loop through the cursorbound windows
3566 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003567 VIsual_select = VIsual_active = 0;
Bram Moolenaar29323592016-07-24 22:04:11 +02003568 FOR_ALL_WINDOWS(curwin)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003569 {
3570 curbuf = curwin->w_buffer;
Bram Moolenaar85a20022019-12-21 18:25:54 +01003571 // skip original window and windows with 'noscrollbind'
Bram Moolenaar860cae12010-06-05 23:22:07 +02003572 if (curwin != old_curwin && curwin->w_p_crb)
3573 {
3574# ifdef FEAT_DIFF
3575 if (curwin->w_p_diff)
Bram Moolenaar025e3e02016-10-18 14:50:18 +02003576 curwin->w_cursor.lnum =
3577 diff_get_corresponding_line(old_curbuf, line);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003578 else
3579# endif
3580 curwin->w_cursor.lnum = line;
3581 curwin->w_cursor.col = col;
Bram Moolenaar1ea69b72012-03-16 19:24:26 +01003582 curwin->w_cursor.coladd = coladd;
Bram Moolenaar524780d2012-03-28 14:19:50 +02003583 curwin->w_curswant = curswant;
3584 curwin->w_set_curswant = set_curswant;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003585
Bram Moolenaar85a20022019-12-21 18:25:54 +01003586 // Make sure the cursor is in a valid position. Temporarily set
3587 // "restart_edit" to allow the cursor to be beyond the EOL.
Dominique Pellee764d1b2023-03-12 21:20:59 +00003588 int restart_edit_save = restart_edit;
3589 restart_edit = 'a';
Bram Moolenaar860cae12010-06-05 23:22:07 +02003590 check_cursor();
Bram Moolenaara315ce12022-06-24 12:38:57 +01003591
3592 // Avoid a scroll here for the cursor position, 'scrollbind' is
3593 // more important.
3594 if (!curwin->w_p_scb)
3595 validate_cursor();
3596
Bram Moolenaar61452852011-02-01 18:01:11 +01003597 restart_edit = restart_edit_save;
Bram Moolenaar85a20022019-12-21 18:25:54 +01003598 // Correct cursor for multi-byte character.
Bram Moolenaar860cae12010-06-05 23:22:07 +02003599 if (has_mbyte)
3600 mb_adjust_cursor();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01003601 redraw_later(UPD_VALID);
Bram Moolenaarf3d419d2011-01-22 21:05:07 +01003602
Bram Moolenaar85a20022019-12-21 18:25:54 +01003603 // Only scroll when 'scrollbind' hasn't done this.
Bram Moolenaarf3d419d2011-01-22 21:05:07 +01003604 if (!curwin->w_p_scb)
3605 update_topline();
Bram Moolenaar860cae12010-06-05 23:22:07 +02003606 curwin->w_redr_status = TRUE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003607 }
3608 }
3609
3610 /*
3611 * reset current-window
3612 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003613 VIsual_select = old_VIsual_select;
3614 VIsual_active = old_VIsual_active;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003615 curwin = old_curwin;
3616 curbuf = old_curbuf;
3617}