blob: c07275897d19c1858c76de6d7539dc140dadf8fa [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 void comp_botline(win_T *wp);
23static void redraw_for_cursorline(win_T *wp);
24static int scrolljump_value(void);
25static int check_top_offset(void);
26static void curs_rows(win_T *wp);
27static void validate_cheight(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +000028
29typedef struct
30{
31 linenr_T lnum; /* line number */
32#ifdef FEAT_DIFF
33 int fill; /* filler lines */
34#endif
35 int height; /* height of added line */
36} lineoff_T;
37
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010038static void topline_back(lineoff_T *lp);
39static void botline_forw(lineoff_T *lp);
Bram Moolenaar071d4272004-06-13 20:20:40 +000040#ifdef FEAT_DIFF
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010041static void botline_topline(lineoff_T *lp);
42static void topline_botline(lineoff_T *lp);
43static void max_topfill(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +000044#endif
45
46/*
47 * Compute wp->w_botline for the current wp->w_topline. Can be called after
48 * wp->w_topline changed.
49 */
50 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +010051comp_botline(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +000052{
53 int n;
54 linenr_T lnum;
55 int done;
56#ifdef FEAT_FOLDING
57 linenr_T last;
58 int folded;
59#endif
60
61 /*
62 * If w_cline_row is valid, start there.
63 * Otherwise have to start at w_topline.
64 */
65 check_cursor_moved(wp);
66 if (wp->w_valid & VALID_CROW)
67 {
68 lnum = wp->w_cursor.lnum;
69 done = wp->w_cline_row;
70 }
71 else
72 {
73 lnum = wp->w_topline;
74 done = 0;
75 }
76
77 for ( ; lnum <= wp->w_buffer->b_ml.ml_line_count; ++lnum)
78 {
79#ifdef FEAT_FOLDING
80 last = lnum;
81 folded = FALSE;
82 if (hasFoldingWin(wp, lnum, NULL, &last, TRUE, NULL))
83 {
84 n = 1;
85 folded = TRUE;
86 }
87 else
88#endif
89#ifdef FEAT_DIFF
90 if (lnum == wp->w_topline)
91 n = plines_win_nofill(wp, lnum, TRUE) + wp->w_topfill;
92 else
93#endif
94 n = plines_win(wp, lnum, TRUE);
95 if (
96#ifdef FEAT_FOLDING
97 lnum <= wp->w_cursor.lnum && last >= wp->w_cursor.lnum
98#else
99 lnum == wp->w_cursor.lnum
100#endif
101 )
102 {
103 wp->w_cline_row = done;
104 wp->w_cline_height = n;
105#ifdef FEAT_FOLDING
106 wp->w_cline_folded = folded;
107#endif
Bram Moolenaar3d6db142014-03-28 21:49:32 +0100108 redraw_for_cursorline(wp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000109 wp->w_valid |= (VALID_CROW|VALID_CHEIGHT);
110 }
111 if (done + n > wp->w_height)
112 break;
113 done += n;
114#ifdef FEAT_FOLDING
115 lnum = last;
116#endif
117 }
118
119 /* wp->w_botline is the line that is just below the window */
120 wp->w_botline = lnum;
121 wp->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
122
123 set_empty_rows(wp, done);
124}
125
Bram Moolenaar90a99792018-09-12 21:52:18 +0200126#ifdef FEAT_SYN_HL
127static linenr_T last_cursorline = 0;
Bram Moolenaar8c63e0e2018-09-25 22:17:54 +0200128
129 void
130reset_cursorline(void)
131{
132 last_cursorline = 0;
133}
Bram Moolenaar90a99792018-09-12 21:52:18 +0200134#endif
135
Bram Moolenaar071d4272004-06-13 20:20:40 +0000136/*
Bram Moolenaar3d6db142014-03-28 21:49:32 +0100137 * Redraw when w_cline_row changes and 'relativenumber' or 'cursorline' is
138 * set.
139 */
140 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100141redraw_for_cursorline(win_T *wp)
Bram Moolenaar3d6db142014-03-28 21:49:32 +0100142{
143 if ((wp->w_p_rnu
144#ifdef FEAT_SYN_HL
145 || wp->w_p_cul
146#endif
147 )
148 && (wp->w_valid & VALID_CROW) == 0
149# ifdef FEAT_INS_EXPAND
150 && !pum_visible()
151# endif
152 )
Bram Moolenaar90a99792018-09-12 21:52:18 +0200153 {
Bram Moolenaarbd9a53c2018-09-12 23:15:48 +0200154 if (wp->w_p_rnu)
155 // win_line() will redraw the number column only.
Bram Moolenaar90a99792018-09-12 21:52:18 +0200156 redraw_win_later(wp, VALID);
Bram Moolenaar1b7fefc2018-09-12 22:27:15 +0200157#ifdef FEAT_SYN_HL
Bram Moolenaarbd9a53c2018-09-12 23:15:48 +0200158 if (wp->w_p_cul)
159 {
160 if (wp->w_redr_type <= VALID && last_cursorline != 0)
161 {
162 // "last_cursorline" may be set for another window, worst case
163 // we redraw too much. This is optimized for moving the cursor
164 // around in the same window.
165 redrawWinline(wp, last_cursorline, FALSE);
166 redrawWinline(wp, wp->w_cursor.lnum, FALSE);
167 redraw_win_later(wp, VALID);
168 }
169 else
170 redraw_win_later(wp, SOME_VALID);
171 last_cursorline = wp->w_cursor.lnum;
172 }
Bram Moolenaar1b7fefc2018-09-12 22:27:15 +0200173#endif
Bram Moolenaar90a99792018-09-12 21:52:18 +0200174 }
Bram Moolenaar3d6db142014-03-28 21:49:32 +0100175}
176
177/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000178 * Update curwin->w_topline and redraw if necessary.
179 * Used to update the screen before printing a message.
180 */
181 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100182update_topline_redraw(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000183{
184 update_topline();
185 if (must_redraw)
186 update_screen(0);
187}
188
189/*
190 * Update curwin->w_topline to move the cursor onto the screen.
191 */
192 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100193update_topline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000194{
195 long line_count;
196 int halfheight;
197 int n;
198 linenr_T old_topline;
199#ifdef FEAT_DIFF
200 int old_topfill;
201#endif
202#ifdef FEAT_FOLDING
203 linenr_T lnum;
204#endif
205 int check_topline = FALSE;
206 int check_botline = FALSE;
207#ifdef FEAT_MOUSE
208 int save_so = p_so;
209#endif
210
Bram Moolenaard5d37532017-03-27 23:02:07 +0200211 /* If there is no valid screen and when the window height is zero just use
212 * the cursor line. */
213 if (!screen_valid(TRUE) || curwin->w_height == 0)
Bram Moolenaarcfc216e2014-09-23 18:37:56 +0200214 {
215 curwin->w_topline = curwin->w_cursor.lnum;
216 curwin->w_botline = curwin->w_topline;
217 curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
Bram Moolenaarcfc216e2014-09-23 18:37:56 +0200218 curwin->w_scbind_pos = 1;
Bram Moolenaarcfc216e2014-09-23 18:37:56 +0200219 return;
220 }
221
Bram Moolenaar071d4272004-06-13 20:20:40 +0000222 check_cursor_moved(curwin);
223 if (curwin->w_valid & VALID_TOPLINE)
224 return;
225
226#ifdef FEAT_MOUSE
227 /* When dragging with the mouse, don't scroll that quickly */
Bram Moolenaar9964e462007-05-05 17:54:07 +0000228 if (mouse_dragging > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000229 p_so = mouse_dragging - 1;
230#endif
231
232 old_topline = curwin->w_topline;
233#ifdef FEAT_DIFF
234 old_topfill = curwin->w_topfill;
235#endif
236
237 /*
238 * If the buffer is empty, always set topline to 1.
239 */
Bram Moolenaarb5aedf32017-03-12 18:23:53 +0100240 if (BUFEMPTY()) /* special case - file is empty */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000241 {
242 if (curwin->w_topline != 1)
243 redraw_later(NOT_VALID);
244 curwin->w_topline = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000245 curwin->w_botline = 2;
246 curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000247 curwin->w_scbind_pos = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000248 }
249
250 /*
251 * If the cursor is above or near the top of the window, scroll the window
252 * to show the line the cursor is in, with 'scrolloff' context.
253 */
254 else
255 {
256 if (curwin->w_topline > 1)
257 {
258 /* If the cursor is above topline, scrolling is always needed.
259 * If the cursor is far below topline and there is no folding,
260 * scrolling down is never needed. */
261 if (curwin->w_cursor.lnum < curwin->w_topline)
262 check_topline = TRUE;
263 else if (check_top_offset())
264 check_topline = TRUE;
265 }
266#ifdef FEAT_DIFF
267 /* Check if there are more filler lines than allowed. */
268 if (!check_topline && curwin->w_topfill > diff_check_fill(curwin,
269 curwin->w_topline))
270 check_topline = TRUE;
271#endif
272
273 if (check_topline)
274 {
275 halfheight = curwin->w_height / 2 - 1;
276 if (halfheight < 2)
277 halfheight = 2;
278
279#ifdef FEAT_FOLDING
280 if (hasAnyFolding(curwin))
281 {
282 /* Count the number of logical lines between the cursor and
283 * topline + p_so (approximation of how much will be
284 * scrolled). */
285 n = 0;
286 for (lnum = curwin->w_cursor.lnum;
287 lnum < curwin->w_topline + p_so; ++lnum)
288 {
289 ++n;
290 /* stop at end of file or when we know we are far off */
291 if (lnum >= curbuf->b_ml.ml_line_count || n >= halfheight)
292 break;
293 (void)hasFolding(lnum, NULL, &lnum);
294 }
295 }
296 else
297#endif
298 n = curwin->w_topline + p_so - curwin->w_cursor.lnum;
299
300 /* If we weren't very close to begin with, we scroll to put the
301 * cursor in the middle of the window. Otherwise put the cursor
302 * near the top of the window. */
303 if (n >= halfheight)
304 scroll_cursor_halfway(FALSE);
305 else
306 {
Bram Moolenaar1e015462005-09-25 22:16:38 +0000307 scroll_cursor_top(scrolljump_value(), FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000308 check_botline = TRUE;
309 }
310 }
311
312 else
313 {
314#ifdef FEAT_FOLDING
315 /* Make sure topline is the first line of a fold. */
316 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
317#endif
318 check_botline = TRUE;
319 }
320 }
321
322 /*
323 * If the cursor is below the bottom of the window, scroll the window
324 * to put the cursor on the window.
325 * When w_botline is invalid, recompute it first, to avoid a redraw later.
326 * If w_botline was approximated, we might need a redraw later in a few
327 * cases, but we don't want to spend (a lot of) time recomputing w_botline
328 * for every small change.
329 */
330 if (check_botline)
331 {
332 if (!(curwin->w_valid & VALID_BOTLINE_AP))
333 validate_botline();
334
335 if (curwin->w_botline <= curbuf->b_ml.ml_line_count)
336 {
Bram Moolenaard4153d42008-11-15 15:06:17 +0000337 if (curwin->w_cursor.lnum < curwin->w_botline)
338 {
339 if (((long)curwin->w_cursor.lnum
Bram Moolenaar071d4272004-06-13 20:20:40 +0000340 >= (long)curwin->w_botline - p_so
341#ifdef FEAT_FOLDING
342 || hasAnyFolding(curwin)
343#endif
344 ))
Bram Moolenaard4153d42008-11-15 15:06:17 +0000345 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000346 lineoff_T loff;
347
Bram Moolenaard4153d42008-11-15 15:06:17 +0000348 /* Cursor is (a few lines) above botline, check if there are
349 * 'scrolloff' window lines below the cursor. If not, need to
350 * scroll. */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000351 n = curwin->w_empty_rows;
352 loff.lnum = curwin->w_cursor.lnum;
353#ifdef FEAT_FOLDING
354 /* In a fold go to its last line. */
355 (void)hasFolding(loff.lnum, NULL, &loff.lnum);
356#endif
357#ifdef FEAT_DIFF
358 loff.fill = 0;
359 n += curwin->w_filler_rows;
360#endif
361 loff.height = 0;
362 while (loff.lnum < curwin->w_botline
363#ifdef FEAT_DIFF
364 && (loff.lnum + 1 < curwin->w_botline || loff.fill == 0)
365#endif
366 )
367 {
368 n += loff.height;
369 if (n >= p_so)
370 break;
371 botline_forw(&loff);
372 }
373 if (n >= p_so)
374 /* sufficient context, no need to scroll */
375 check_botline = FALSE;
Bram Moolenaard4153d42008-11-15 15:06:17 +0000376 }
377 else
378 /* sufficient context, no need to scroll */
379 check_botline = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000380 }
381 if (check_botline)
382 {
383#ifdef FEAT_FOLDING
384 if (hasAnyFolding(curwin))
385 {
386 /* Count the number of logical lines between the cursor and
387 * botline - p_so (approximation of how much will be
388 * scrolled). */
389 line_count = 0;
390 for (lnum = curwin->w_cursor.lnum;
391 lnum >= curwin->w_botline - p_so; --lnum)
392 {
393 ++line_count;
394 /* stop at end of file or when we know we are far off */
395 if (lnum <= 0 || line_count > curwin->w_height + 1)
396 break;
397 (void)hasFolding(lnum, &lnum, NULL);
398 }
399 }
400 else
401#endif
402 line_count = curwin->w_cursor.lnum - curwin->w_botline
403 + 1 + p_so;
404 if (line_count <= curwin->w_height + 1)
Bram Moolenaar1e015462005-09-25 22:16:38 +0000405 scroll_cursor_bot(scrolljump_value(), FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000406 else
407 scroll_cursor_halfway(FALSE);
408 }
409 }
410 }
411 curwin->w_valid |= VALID_TOPLINE;
412
413 /*
414 * Need to redraw when topline changed.
415 */
416 if (curwin->w_topline != old_topline
417#ifdef FEAT_DIFF
418 || curwin->w_topfill != old_topfill
419#endif
420 )
421 {
Bram Moolenaar76b9b362012-02-04 23:35:00 +0100422 dollar_vcol = -1;
Bram Moolenaar2b48ad52006-03-12 21:56:11 +0000423 if (curwin->w_skipcol != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000424 {
425 curwin->w_skipcol = 0;
426 redraw_later(NOT_VALID);
427 }
428 else
429 redraw_later(VALID);
430 /* May need to set w_skipcol when cursor in w_topline. */
431 if (curwin->w_cursor.lnum == curwin->w_topline)
432 validate_cursor();
433 }
434
435#ifdef FEAT_MOUSE
436 p_so = save_so;
437#endif
438}
439
440/*
Bram Moolenaar1e015462005-09-25 22:16:38 +0000441 * Return the scrolljump value to use for the current window.
442 * When 'scrolljump' is positive use it as-is.
443 * When 'scrolljump' is negative use it as a percentage of the window height.
444 */
445 static int
Bram Moolenaar9b578142016-01-30 19:39:49 +0100446scrolljump_value(void)
Bram Moolenaar1e015462005-09-25 22:16:38 +0000447{
448 if (p_sj >= 0)
449 return (int)p_sj;
450 return (curwin->w_height * -p_sj) / 100;
451}
452
453/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000454 * Return TRUE when there are not 'scrolloff' lines above the cursor for the
455 * current window.
456 */
457 static int
Bram Moolenaar9b578142016-01-30 19:39:49 +0100458check_top_offset(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000459{
460 lineoff_T loff;
461 int n;
462
463 if (curwin->w_cursor.lnum < curwin->w_topline + p_so
464#ifdef FEAT_FOLDING
465 || hasAnyFolding(curwin)
466#endif
467 )
468 {
469 loff.lnum = curwin->w_cursor.lnum;
470#ifdef FEAT_DIFF
471 loff.fill = 0;
472 n = curwin->w_topfill; /* always have this context */
473#else
474 n = 0;
475#endif
476 /* Count the visible screen lines above the cursor line. */
477 while (n < p_so)
478 {
479 topline_back(&loff);
480 /* Stop when included a line above the window. */
481 if (loff.lnum < curwin->w_topline
482#ifdef FEAT_DIFF
483 || (loff.lnum == curwin->w_topline && loff.fill > 0)
484#endif
485 )
486 break;
487 n += loff.height;
488 }
489 if (n < p_so)
490 return TRUE;
491 }
492 return FALSE;
493}
494
495 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100496update_curswant(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000497{
498 if (curwin->w_set_curswant)
499 {
500 validate_virtcol();
501 curwin->w_curswant = curwin->w_virtcol;
502 curwin->w_set_curswant = FALSE;
503 }
504}
505
506/*
507 * Check if the cursor has moved. Set the w_valid flag accordingly.
508 */
509 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100510check_cursor_moved(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000511{
512 if (wp->w_cursor.lnum != wp->w_valid_cursor.lnum)
513 {
514 wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL
515 |VALID_CHEIGHT|VALID_CROW|VALID_TOPLINE);
516 wp->w_valid_cursor = wp->w_cursor;
517 wp->w_valid_leftcol = wp->w_leftcol;
518 }
519 else if (wp->w_cursor.col != wp->w_valid_cursor.col
520 || wp->w_leftcol != wp->w_valid_leftcol
521#ifdef FEAT_VIRTUALEDIT
522 || wp->w_cursor.coladd != wp->w_valid_cursor.coladd
523#endif
524 )
525 {
526 wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL);
527 wp->w_valid_cursor.col = wp->w_cursor.col;
528 wp->w_valid_leftcol = wp->w_leftcol;
529#ifdef FEAT_VIRTUALEDIT
530 wp->w_valid_cursor.coladd = wp->w_cursor.coladd;
531#endif
532 }
533}
534
535/*
536 * Call this function when some window settings have changed, which require
537 * the cursor position, botline and topline to be recomputed and the window to
538 * be redrawn. E.g, when changing the 'wrap' option or folding.
539 */
540 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100541changed_window_setting(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000542{
543 changed_window_setting_win(curwin);
544}
545
546 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100547changed_window_setting_win(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000548{
549 wp->w_lines_valid = 0;
550 changed_line_abv_curs_win(wp);
551 wp->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP|VALID_TOPLINE);
552 redraw_win_later(wp, NOT_VALID);
553}
554
555/*
556 * Set wp->w_topline to a certain number.
557 */
558 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100559set_topline(win_T *wp, linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000560{
561#ifdef FEAT_FOLDING
562 /* go to first of folded lines */
563 (void)hasFoldingWin(wp, lnum, &lnum, NULL, TRUE, NULL);
564#endif
565 /* Approximate the value of w_botline */
566 wp->w_botline += lnum - wp->w_topline;
567 wp->w_topline = lnum;
Bram Moolenaard4153d42008-11-15 15:06:17 +0000568 wp->w_topline_was_set = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000569#ifdef FEAT_DIFF
570 wp->w_topfill = 0;
571#endif
572 wp->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_TOPLINE);
573 /* Don't set VALID_TOPLINE here, 'scrolloff' needs to be checked. */
574 redraw_later(VALID);
575}
576
577/*
578 * Call this function when the length of the cursor line (in screen
579 * characters) has changed, and the change is before the cursor.
580 * Need to take care of w_botline separately!
581 */
582 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100583changed_cline_bef_curs(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000584{
585 curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL
586 |VALID_CHEIGHT|VALID_TOPLINE);
587}
588
589 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100590changed_cline_bef_curs_win(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000591{
592 wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL
593 |VALID_CHEIGHT|VALID_TOPLINE);
594}
595
Bram Moolenaar071d4272004-06-13 20:20:40 +0000596/*
597 * Call this function when the length of a line (in screen characters) above
598 * the cursor have changed.
599 * Need to take care of w_botline separately!
600 */
601 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100602changed_line_abv_curs(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000603{
604 curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL|VALID_CROW
605 |VALID_CHEIGHT|VALID_TOPLINE);
606}
607
608 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100609changed_line_abv_curs_win(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000610{
611 wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL|VALID_CROW
612 |VALID_CHEIGHT|VALID_TOPLINE);
613}
614
615/*
616 * Make sure the value of curwin->w_botline is valid.
617 */
618 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100619validate_botline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000620{
621 if (!(curwin->w_valid & VALID_BOTLINE))
622 comp_botline(curwin);
623}
624
625/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000626 * Mark curwin->w_botline as invalid (because of some change in the buffer).
627 */
628 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100629invalidate_botline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000630{
631 curwin->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP);
632}
633
634 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100635invalidate_botline_win(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000636{
637 wp->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP);
638}
639
Bram Moolenaar071d4272004-06-13 20:20:40 +0000640 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100641approximate_botline_win(
642 win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000643{
644 wp->w_valid &= ~VALID_BOTLINE;
645}
646
Bram Moolenaar071d4272004-06-13 20:20:40 +0000647/*
648 * Return TRUE if curwin->w_wrow and curwin->w_wcol are valid.
649 */
650 int
Bram Moolenaar9b578142016-01-30 19:39:49 +0100651cursor_valid(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000652{
653 check_cursor_moved(curwin);
654 return ((curwin->w_valid & (VALID_WROW|VALID_WCOL)) ==
655 (VALID_WROW|VALID_WCOL));
656}
657
658/*
659 * Validate cursor position. Makes sure w_wrow and w_wcol are valid.
660 * w_topline must be valid, you may need to call update_topline() first!
661 */
662 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100663validate_cursor(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000664{
665 check_cursor_moved(curwin);
666 if ((curwin->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW))
667 curs_columns(TRUE);
668}
669
670#if defined(FEAT_GUI) || defined(PROTO)
671/*
672 * validate w_cline_row.
673 */
674 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100675validate_cline_row(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000676{
677 /*
678 * First make sure that w_topline is valid (after moving the cursor).
679 */
680 update_topline();
681 check_cursor_moved(curwin);
682 if (!(curwin->w_valid & VALID_CROW))
Bram Moolenaar3f9be972014-12-13 21:09:57 +0100683 curs_rows(curwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000684}
685#endif
686
687/*
688 * Compute wp->w_cline_row and wp->w_cline_height, based on the current value
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200689 * of wp->w_topline.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000690 */
691 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100692curs_rows(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000693{
694 linenr_T lnum;
695 int i;
696 int all_invalid;
697 int valid;
698#ifdef FEAT_FOLDING
699 long fold_count;
700#endif
701
702 /* Check if wp->w_lines[].wl_size is invalid */
703 all_invalid = (!redrawing()
704 || wp->w_lines_valid == 0
705 || wp->w_lines[0].wl_lnum > wp->w_topline);
706 i = 0;
707 wp->w_cline_row = 0;
708 for (lnum = wp->w_topline; lnum < wp->w_cursor.lnum; ++i)
709 {
710 valid = FALSE;
711 if (!all_invalid && i < wp->w_lines_valid)
712 {
713 if (wp->w_lines[i].wl_lnum < lnum || !wp->w_lines[i].wl_valid)
714 continue; /* skip changed or deleted lines */
715 if (wp->w_lines[i].wl_lnum == lnum)
716 {
717#ifdef FEAT_FOLDING
718 /* Check for newly inserted lines below this row, in which
719 * case we need to check for folded lines. */
720 if (!wp->w_buffer->b_mod_set
721 || wp->w_lines[i].wl_lastlnum < wp->w_cursor.lnum
722 || wp->w_buffer->b_mod_top
723 > wp->w_lines[i].wl_lastlnum + 1)
724#endif
725 valid = TRUE;
726 }
727 else if (wp->w_lines[i].wl_lnum > lnum)
728 --i; /* hold at inserted lines */
729 }
730 if (valid
731#ifdef FEAT_DIFF
732 && (lnum != wp->w_topline || !wp->w_p_diff)
733#endif
734 )
735 {
736#ifdef FEAT_FOLDING
737 lnum = wp->w_lines[i].wl_lastlnum + 1;
738 /* Cursor inside folded lines, don't count this row */
739 if (lnum > wp->w_cursor.lnum)
740 break;
741#else
742 ++lnum;
743#endif
744 wp->w_cline_row += wp->w_lines[i].wl_size;
745 }
746 else
747 {
748#ifdef FEAT_FOLDING
749 fold_count = foldedCount(wp, lnum, NULL);
750 if (fold_count)
751 {
752 lnum += fold_count;
753 if (lnum > wp->w_cursor.lnum)
754 break;
755 ++wp->w_cline_row;
756 }
757 else
758#endif
759#ifdef FEAT_DIFF
760 if (lnum == wp->w_topline)
761 wp->w_cline_row += plines_win_nofill(wp, lnum++, TRUE)
762 + wp->w_topfill;
763 else
764#endif
765 wp->w_cline_row += plines_win(wp, lnum++, TRUE);
766 }
767 }
768
769 check_cursor_moved(wp);
770 if (!(wp->w_valid & VALID_CHEIGHT))
771 {
772 if (all_invalid
773 || i == wp->w_lines_valid
774 || (i < wp->w_lines_valid
775 && (!wp->w_lines[i].wl_valid
776 || wp->w_lines[i].wl_lnum != wp->w_cursor.lnum)))
777 {
778#ifdef FEAT_DIFF
779 if (wp->w_cursor.lnum == wp->w_topline)
780 wp->w_cline_height = plines_win_nofill(wp, wp->w_cursor.lnum,
781 TRUE) + wp->w_topfill;
782 else
783#endif
784 wp->w_cline_height = plines_win(wp, wp->w_cursor.lnum, TRUE);
785#ifdef FEAT_FOLDING
786 wp->w_cline_folded = hasFoldingWin(wp, wp->w_cursor.lnum,
787 NULL, NULL, TRUE, NULL);
788#endif
789 }
790 else if (i > wp->w_lines_valid)
791 {
792 /* a line that is too long to fit on the last screen line */
793 wp->w_cline_height = 0;
794#ifdef FEAT_FOLDING
795 wp->w_cline_folded = hasFoldingWin(wp, wp->w_cursor.lnum,
796 NULL, NULL, TRUE, NULL);
797#endif
798 }
799 else
800 {
801 wp->w_cline_height = wp->w_lines[i].wl_size;
802#ifdef FEAT_FOLDING
803 wp->w_cline_folded = wp->w_lines[i].wl_folded;
804#endif
805 }
806 }
807
Bram Moolenaar3d6db142014-03-28 21:49:32 +0100808 redraw_for_cursorline(curwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000809 wp->w_valid |= VALID_CROW|VALID_CHEIGHT;
810
Bram Moolenaar071d4272004-06-13 20:20:40 +0000811}
812
813/*
814 * Validate curwin->w_virtcol only.
815 */
816 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100817validate_virtcol(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000818{
819 validate_virtcol_win(curwin);
820}
821
822/*
823 * Validate wp->w_virtcol only.
824 */
825 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100826validate_virtcol_win(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000827{
828 check_cursor_moved(wp);
829 if (!(wp->w_valid & VALID_VIRTCOL))
830 {
831 getvvcol(wp, &wp->w_cursor, NULL, &(wp->w_virtcol), NULL);
832 wp->w_valid |= VALID_VIRTCOL;
Bram Moolenaar2b48ad52006-03-12 21:56:11 +0000833#ifdef FEAT_SYN_HL
Bram Moolenaar019ff682006-03-13 22:10:45 +0000834 if (wp->w_p_cuc
835# ifdef FEAT_INS_EXPAND
836 && !pum_visible()
837# endif
838 )
Bram Moolenaar2b48ad52006-03-12 21:56:11 +0000839 redraw_win_later(wp, SOME_VALID);
840#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000841 }
842}
843
844/*
845 * Validate curwin->w_cline_height only.
846 */
847 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100848validate_cheight(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000849{
850 check_cursor_moved(curwin);
851 if (!(curwin->w_valid & VALID_CHEIGHT))
852 {
853#ifdef FEAT_DIFF
854 if (curwin->w_cursor.lnum == curwin->w_topline)
855 curwin->w_cline_height = plines_nofill(curwin->w_cursor.lnum)
856 + curwin->w_topfill;
857 else
858#endif
859 curwin->w_cline_height = plines(curwin->w_cursor.lnum);
860#ifdef FEAT_FOLDING
861 curwin->w_cline_folded = hasFolding(curwin->w_cursor.lnum, NULL, NULL);
862#endif
863 curwin->w_valid |= VALID_CHEIGHT;
864 }
865}
866
867/*
Bram Moolenaarc236c162008-07-13 17:41:49 +0000868 * Validate w_wcol and w_virtcol only.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000869 */
870 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100871validate_cursor_col(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000872{
873 colnr_T off;
874 colnr_T col;
Bram Moolenaar6427c602010-02-03 17:43:07 +0100875 int width;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000876
877 validate_virtcol();
878 if (!(curwin->w_valid & VALID_WCOL))
879 {
880 col = curwin->w_virtcol;
881 off = curwin_col_off();
882 col += off;
Bram Moolenaar02631462017-09-22 15:20:32 +0200883 width = curwin->w_width - off + curwin_col_off2();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000884
885 /* long line wrapping, adjust curwin->w_wrow */
Bram Moolenaarc236c162008-07-13 17:41:49 +0000886 if (curwin->w_p_wrap
Bram Moolenaar02631462017-09-22 15:20:32 +0200887 && col >= (colnr_T)curwin->w_width
Bram Moolenaar6427c602010-02-03 17:43:07 +0100888 && width > 0)
889 /* use same formula as what is used in curs_columns() */
Bram Moolenaar02631462017-09-22 15:20:32 +0200890 col -= ((col - curwin->w_width) / width + 1) * width;
Bram Moolenaarc236c162008-07-13 17:41:49 +0000891 if (col > (int)curwin->w_leftcol)
892 col -= curwin->w_leftcol;
893 else
894 col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000895 curwin->w_wcol = col;
Bram Moolenaarc236c162008-07-13 17:41:49 +0000896
Bram Moolenaar071d4272004-06-13 20:20:40 +0000897 curwin->w_valid |= VALID_WCOL;
898 }
899}
900
901/*
Bram Moolenaar64486672010-05-16 15:46:46 +0200902 * Compute offset of a window, occupied by absolute or relative line number,
903 * fold column and sign column (these don't move when scrolling horizontally).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000904 */
905 int
Bram Moolenaar9b578142016-01-30 19:39:49 +0100906win_col_off(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000907{
Bram Moolenaar64486672010-05-16 15:46:46 +0200908 return (((wp->w_p_nu || wp->w_p_rnu) ? number_width(wp) + 1 : 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000909#ifdef FEAT_CMDWIN
910 + (cmdwin_type == 0 || wp != curwin ? 0 : 1)
911#endif
912#ifdef FEAT_FOLDING
913 + wp->w_p_fdc
914#endif
915#ifdef FEAT_SIGNS
Bram Moolenaar95ec9d62016-08-12 18:29:59 +0200916 + (signcolumn_on(wp) ? 2 : 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000917#endif
918 );
919}
920
921 int
Bram Moolenaar9b578142016-01-30 19:39:49 +0100922curwin_col_off(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000923{
924 return win_col_off(curwin);
925}
926
927/*
928 * Return the difference in column offset for the second screen line of a
Bram Moolenaar64486672010-05-16 15:46:46 +0200929 * wrapped line. It's 8 if 'number' or 'relativenumber' is on and 'n' is in
930 * 'cpoptions'.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000931 */
932 int
Bram Moolenaar9b578142016-01-30 19:39:49 +0100933win_col_off2(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000934{
Bram Moolenaar64486672010-05-16 15:46:46 +0200935 if ((wp->w_p_nu || wp->w_p_rnu) && vim_strchr(p_cpo, CPO_NUMCOL) != NULL)
Bram Moolenaar592e0a22004-07-03 16:05:59 +0000936 return number_width(wp) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000937 return 0;
938}
939
940 int
Bram Moolenaar9b578142016-01-30 19:39:49 +0100941curwin_col_off2(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000942{
943 return win_col_off2(curwin);
944}
945
946/*
947 * compute curwin->w_wcol and curwin->w_virtcol.
948 * Also updates curwin->w_wrow and curwin->w_cline_row.
949 * Also updates curwin->w_leftcol.
950 */
951 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100952curs_columns(
953 int may_scroll) /* when TRUE, may scroll horizontally */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000954{
955 int diff;
956 int extra; /* offset for first screen line */
957 int off_left, off_right;
958 int n;
959 int p_lines;
960 int width = 0;
961 int textwidth;
962 int new_leftcol;
963 colnr_T startcol;
964 colnr_T endcol;
965 colnr_T prev_skipcol;
966
967 /*
968 * First make sure that w_topline is valid (after moving the cursor).
969 */
970 update_topline();
971
972 /*
973 * Next make sure that w_cline_row is valid.
974 */
975 if (!(curwin->w_valid & VALID_CROW))
Bram Moolenaar3f9be972014-12-13 21:09:57 +0100976 curs_rows(curwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000977
978 /*
979 * Compute the number of virtual columns.
980 */
981#ifdef FEAT_FOLDING
982 if (curwin->w_cline_folded)
983 /* In a folded line the cursor is always in the first column */
984 startcol = curwin->w_virtcol = endcol = curwin->w_leftcol;
985 else
986#endif
987 getvvcol(curwin, &curwin->w_cursor,
988 &startcol, &(curwin->w_virtcol), &endcol);
989
990 /* remove '$' from change command when cursor moves onto it */
991 if (startcol > dollar_vcol)
Bram Moolenaar76b9b362012-02-04 23:35:00 +0100992 dollar_vcol = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000993
994 extra = curwin_col_off();
995 curwin->w_wcol = curwin->w_virtcol + extra;
996 endcol += extra;
997
998 /*
999 * Now compute w_wrow, counting screen lines from w_cline_row.
1000 */
1001 curwin->w_wrow = curwin->w_cline_row;
1002
Bram Moolenaar02631462017-09-22 15:20:32 +02001003 textwidth = curwin->w_width - extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001004 if (textwidth <= 0)
1005 {
1006 /* No room for text, put cursor in last char of window. */
Bram Moolenaar02631462017-09-22 15:20:32 +02001007 curwin->w_wcol = curwin->w_width - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001008 curwin->w_wrow = curwin->w_height - 1;
1009 }
Bram Moolenaar4033c552017-09-16 20:54:51 +02001010 else if (curwin->w_p_wrap && curwin->w_width != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001011 {
1012 width = textwidth + curwin_col_off2();
1013
1014 /* long line wrapping, adjust curwin->w_wrow */
Bram Moolenaar02631462017-09-22 15:20:32 +02001015 if (curwin->w_wcol >= curwin->w_width)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001016 {
Bram Moolenaar6427c602010-02-03 17:43:07 +01001017 /* this same formula is used in validate_cursor_col() */
Bram Moolenaar02631462017-09-22 15:20:32 +02001018 n = (curwin->w_wcol - curwin->w_width) / width + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001019 curwin->w_wcol -= n * width;
1020 curwin->w_wrow += n;
1021
1022#ifdef FEAT_LINEBREAK
1023 /* When cursor wraps to first char of next line in Insert
1024 * mode, the 'showbreak' string isn't shown, backup to first
1025 * column */
1026 if (*p_sbr && *ml_get_cursor() == NUL
1027 && curwin->w_wcol == (int)vim_strsize(p_sbr))
1028 curwin->w_wcol = 0;
1029#endif
1030 }
1031 }
1032
1033 /* No line wrapping: compute curwin->w_leftcol if scrolling is on and line
1034 * is not folded.
1035 * If scrolling is off, curwin->w_leftcol is assumed to be 0 */
Bram Moolenaar70b2a562012-01-10 22:26:17 +01001036 else if (may_scroll
Bram Moolenaar071d4272004-06-13 20:20:40 +00001037#ifdef FEAT_FOLDING
1038 && !curwin->w_cline_folded
1039#endif
1040 )
1041 {
1042 /*
1043 * If Cursor is left of the screen, scroll rightwards.
1044 * If Cursor is right of the screen, scroll leftwards
1045 * If we get closer to the edge than 'sidescrolloff', scroll a little
1046 * extra
1047 */
1048 off_left = (int)startcol - (int)curwin->w_leftcol - p_siso;
Bram Moolenaar02631462017-09-22 15:20:32 +02001049 off_right = (int)endcol - (int)(curwin->w_leftcol + curwin->w_width
Bram Moolenaar071d4272004-06-13 20:20:40 +00001050 - p_siso) + 1;
1051 if (off_left < 0 || off_right > 0)
1052 {
1053 if (off_left < 0)
1054 diff = -off_left;
1055 else
1056 diff = off_right;
1057
1058 /* When far off or not enough room on either side, put cursor in
1059 * middle of window. */
1060 if (p_ss == 0 || diff >= textwidth / 2 || off_right >= off_left)
1061 new_leftcol = curwin->w_wcol - extra - textwidth / 2;
1062 else
1063 {
1064 if (diff < p_ss)
1065 diff = p_ss;
1066 if (off_left < 0)
1067 new_leftcol = curwin->w_leftcol - diff;
1068 else
1069 new_leftcol = curwin->w_leftcol + diff;
1070 }
1071 if (new_leftcol < 0)
1072 new_leftcol = 0;
1073 if (new_leftcol != (int)curwin->w_leftcol)
1074 {
1075 curwin->w_leftcol = new_leftcol;
1076 /* screen has to be redrawn with new curwin->w_leftcol */
1077 redraw_later(NOT_VALID);
1078 }
1079 }
1080 curwin->w_wcol -= curwin->w_leftcol;
1081 }
1082 else if (curwin->w_wcol > (int)curwin->w_leftcol)
1083 curwin->w_wcol -= curwin->w_leftcol;
1084 else
1085 curwin->w_wcol = 0;
1086
1087#ifdef FEAT_DIFF
1088 /* Skip over filler lines. At the top use w_topfill, there
1089 * may be some filler lines above the window. */
1090 if (curwin->w_cursor.lnum == curwin->w_topline)
1091 curwin->w_wrow += curwin->w_topfill;
1092 else
1093 curwin->w_wrow += diff_check_fill(curwin, curwin->w_cursor.lnum);
1094#endif
1095
1096 prev_skipcol = curwin->w_skipcol;
1097
1098 p_lines = 0;
1099 if ((curwin->w_wrow >= curwin->w_height
1100 || ((prev_skipcol > 0
1101 || curwin->w_wrow + p_so >= curwin->w_height)
1102 && (p_lines =
1103#ifdef FEAT_DIFF
1104 plines_win_nofill
1105#else
1106 plines_win
1107#endif
1108 (curwin, curwin->w_cursor.lnum, FALSE))
1109 - 1 >= curwin->w_height))
1110 && curwin->w_height != 0
1111 && curwin->w_cursor.lnum == curwin->w_topline
1112 && width > 0
Bram Moolenaar4033c552017-09-16 20:54:51 +02001113 && curwin->w_width != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001114 {
1115 /* Cursor past end of screen. Happens with a single line that does
1116 * not fit on screen. Find a skipcol to show the text around the
1117 * cursor. Avoid scrolling all the time. compute value of "extra":
1118 * 1: Less than "p_so" lines above
1119 * 2: Less than "p_so" lines below
1120 * 3: both of them */
1121 extra = 0;
1122 if (curwin->w_skipcol + p_so * width > curwin->w_virtcol)
1123 extra = 1;
1124 /* Compute last display line of the buffer line that we want at the
1125 * bottom of the window. */
1126 if (p_lines == 0)
1127 p_lines = plines_win(curwin, curwin->w_cursor.lnum, FALSE);
1128 --p_lines;
1129 if (p_lines > curwin->w_wrow + p_so)
1130 n = curwin->w_wrow + p_so;
1131 else
1132 n = p_lines;
1133 if ((colnr_T)n >= curwin->w_height + curwin->w_skipcol / width)
1134 extra += 2;
1135
1136 if (extra == 3 || p_lines < p_so * 2)
1137 {
1138 /* not enough room for 'scrolloff', put cursor in the middle */
1139 n = curwin->w_virtcol / width;
1140 if (n > curwin->w_height / 2)
1141 n -= curwin->w_height / 2;
1142 else
1143 n = 0;
1144 /* don't skip more than necessary */
1145 if (n > p_lines - curwin->w_height + 1)
1146 n = p_lines - curwin->w_height + 1;
1147 curwin->w_skipcol = n * width;
1148 }
1149 else if (extra == 1)
1150 {
1151 /* less then 'scrolloff' lines above, decrease skipcol */
1152 extra = (curwin->w_skipcol + p_so * width - curwin->w_virtcol
1153 + width - 1) / width;
1154 if (extra > 0)
1155 {
1156 if ((colnr_T)(extra * width) > curwin->w_skipcol)
1157 extra = curwin->w_skipcol / width;
1158 curwin->w_skipcol -= extra * width;
1159 }
1160 }
1161 else if (extra == 2)
1162 {
1163 /* less then 'scrolloff' lines below, increase skipcol */
1164 endcol = (n - curwin->w_height + 1) * width;
1165 while (endcol > curwin->w_virtcol)
1166 endcol -= width;
1167 if (endcol > curwin->w_skipcol)
1168 curwin->w_skipcol = endcol;
1169 }
1170
1171 curwin->w_wrow -= curwin->w_skipcol / width;
1172 if (curwin->w_wrow >= curwin->w_height)
1173 {
1174 /* small window, make sure cursor is in it */
1175 extra = curwin->w_wrow - curwin->w_height + 1;
1176 curwin->w_skipcol += extra * width;
1177 curwin->w_wrow -= extra;
1178 }
1179
1180 extra = ((int)prev_skipcol - (int)curwin->w_skipcol) / width;
1181 if (extra > 0)
1182 win_ins_lines(curwin, 0, extra, FALSE, FALSE);
1183 else if (extra < 0)
Bram Moolenaarcfce7172017-08-17 20:31:48 +02001184 win_del_lines(curwin, 0, -extra, FALSE, FALSE, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001185 }
1186 else
1187 curwin->w_skipcol = 0;
1188 if (prev_skipcol != curwin->w_skipcol)
1189 redraw_later(NOT_VALID);
1190
Bram Moolenaar2b48ad52006-03-12 21:56:11 +00001191#ifdef FEAT_SYN_HL
Bram Moolenaarb6798752014-03-27 12:11:48 +01001192 /* Redraw when w_virtcol changes and 'cursorcolumn' is set */
1193 if (curwin->w_p_cuc && (curwin->w_valid & VALID_VIRTCOL) == 0
Bram Moolenaar64486672010-05-16 15:46:46 +02001194# ifdef FEAT_INS_EXPAND
Bram Moolenaarb6798752014-03-27 12:11:48 +01001195 && !pum_visible()
Bram Moolenaar64486672010-05-16 15:46:46 +02001196# endif
Bram Moolenaarb6798752014-03-27 12:11:48 +01001197 )
1198 redraw_later(SOME_VALID);
1199#endif
Bram Moolenaar2b48ad52006-03-12 21:56:11 +00001200
Bram Moolenaar071d4272004-06-13 20:20:40 +00001201 curwin->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL;
1202}
1203
1204/*
1205 * Scroll the current window down by "line_count" logical lines. "CTRL-Y"
1206 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001207 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001208scrolldown(
1209 long line_count,
1210 int byfold UNUSED) /* TRUE: count a closed fold as one line */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001211{
1212 long done = 0; /* total # of physical lines done */
1213 int wrow;
1214 int moved = FALSE;
1215
1216#ifdef FEAT_FOLDING
1217 linenr_T first;
1218
1219 /* Make sure w_topline is at the first of a sequence of folded lines. */
1220 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
1221#endif
1222 validate_cursor(); /* w_wrow needs to be valid */
1223 while (line_count-- > 0)
1224 {
1225#ifdef FEAT_DIFF
Bram Moolenaarfa316dd2009-11-03 15:23:14 +00001226 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline)
1227 && curwin->w_topfill < curwin->w_height - 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001228 {
1229 ++curwin->w_topfill;
1230 ++done;
1231 }
1232 else
1233#endif
1234 {
1235 if (curwin->w_topline == 1)
1236 break;
1237 --curwin->w_topline;
1238#ifdef FEAT_DIFF
1239 curwin->w_topfill = 0;
1240#endif
1241#ifdef FEAT_FOLDING
1242 /* A sequence of folded lines only counts for one logical line */
1243 if (hasFolding(curwin->w_topline, &first, NULL))
1244 {
1245 ++done;
1246 if (!byfold)
1247 line_count -= curwin->w_topline - first - 1;
1248 curwin->w_botline -= curwin->w_topline - first;
1249 curwin->w_topline = first;
1250 }
1251 else
1252#endif
Bram Moolenaar43335ea2015-09-09 20:59:37 +02001253 done += PLINES_NOFILL(curwin->w_topline);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001254 }
1255 --curwin->w_botline; /* approximate w_botline */
1256 invalidate_botline();
1257 }
1258 curwin->w_wrow += done; /* keep w_wrow updated */
1259 curwin->w_cline_row += done; /* keep w_cline_row updated */
1260
1261#ifdef FEAT_DIFF
1262 if (curwin->w_cursor.lnum == curwin->w_topline)
1263 curwin->w_cline_row = 0;
1264 check_topfill(curwin, TRUE);
1265#endif
1266
1267 /*
1268 * Compute the row number of the last row of the cursor line
1269 * and move the cursor onto the displayed part of the window.
1270 */
1271 wrow = curwin->w_wrow;
Bram Moolenaar4033c552017-09-16 20:54:51 +02001272 if (curwin->w_p_wrap && curwin->w_width != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001273 {
1274 validate_virtcol();
1275 validate_cheight();
1276 wrow += curwin->w_cline_height - 1 -
Bram Moolenaar02631462017-09-22 15:20:32 +02001277 curwin->w_virtcol / curwin->w_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001278 }
1279 while (wrow >= curwin->w_height && curwin->w_cursor.lnum > 1)
1280 {
1281#ifdef FEAT_FOLDING
1282 if (hasFolding(curwin->w_cursor.lnum, &first, NULL))
1283 {
1284 --wrow;
1285 if (first == 1)
1286 curwin->w_cursor.lnum = 1;
1287 else
1288 curwin->w_cursor.lnum = first - 1;
1289 }
1290 else
1291#endif
1292 wrow -= plines(curwin->w_cursor.lnum--);
1293 curwin->w_valid &=
1294 ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL);
1295 moved = TRUE;
1296 }
1297 if (moved)
1298 {
1299#ifdef FEAT_FOLDING
1300 /* Move cursor to first line of closed fold. */
1301 foldAdjustCursor();
1302#endif
1303 coladvance(curwin->w_curswant);
1304 }
1305}
1306
1307/*
1308 * Scroll the current window up by "line_count" logical lines. "CTRL-E"
1309 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001310 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001311scrollup(
1312 long line_count,
1313 int byfold UNUSED) /* TRUE: count a closed fold as one line */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001314{
1315#if defined(FEAT_FOLDING) || defined(FEAT_DIFF)
1316 linenr_T lnum;
1317
1318 if (
1319# ifdef FEAT_FOLDING
1320 (byfold && hasAnyFolding(curwin))
1321# ifdef FEAT_DIFF
1322 ||
1323# endif
1324# endif
1325# ifdef FEAT_DIFF
1326 curwin->w_p_diff
1327# endif
1328 )
1329 {
1330 /* count each sequence of folded lines as one logical line */
1331 lnum = curwin->w_topline;
1332 while (line_count--)
1333 {
1334# ifdef FEAT_DIFF
1335 if (curwin->w_topfill > 0)
1336 --curwin->w_topfill;
1337 else
1338# endif
1339 {
1340# ifdef FEAT_FOLDING
1341 if (byfold)
1342 (void)hasFolding(lnum, NULL, &lnum);
1343# endif
1344 if (lnum >= curbuf->b_ml.ml_line_count)
1345 break;
1346 ++lnum;
1347# ifdef FEAT_DIFF
1348 curwin->w_topfill = diff_check_fill(curwin, lnum);
1349# endif
1350 }
1351 }
1352 /* approximate w_botline */
1353 curwin->w_botline += lnum - curwin->w_topline;
1354 curwin->w_topline = lnum;
1355 }
1356 else
1357#endif
1358 {
1359 curwin->w_topline += line_count;
1360 curwin->w_botline += line_count; /* approximate w_botline */
1361 }
1362
1363 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1364 curwin->w_topline = curbuf->b_ml.ml_line_count;
1365 if (curwin->w_botline > curbuf->b_ml.ml_line_count + 1)
1366 curwin->w_botline = curbuf->b_ml.ml_line_count + 1;
1367
1368#ifdef FEAT_DIFF
1369 check_topfill(curwin, FALSE);
1370#endif
1371
1372#ifdef FEAT_FOLDING
1373 if (hasAnyFolding(curwin))
1374 /* Make sure w_topline is at the first of a sequence of folded lines. */
1375 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
1376#endif
1377
1378 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
1379 if (curwin->w_cursor.lnum < curwin->w_topline)
1380 {
1381 curwin->w_cursor.lnum = curwin->w_topline;
1382 curwin->w_valid &=
1383 ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL);
1384 coladvance(curwin->w_curswant);
1385 }
1386}
1387
1388#ifdef FEAT_DIFF
1389/*
1390 * Don't end up with too many filler lines in the window.
1391 */
1392 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001393check_topfill(
1394 win_T *wp,
1395 int down) /* when TRUE scroll down when not enough space */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001396{
1397 int n;
1398
1399 if (wp->w_topfill > 0)
1400 {
1401 n = plines_win_nofill(wp, wp->w_topline, TRUE);
1402 if (wp->w_topfill + n > wp->w_height)
1403 {
1404 if (down && wp->w_topline > 1)
1405 {
1406 --wp->w_topline;
1407 wp->w_topfill = 0;
1408 }
1409 else
1410 {
1411 wp->w_topfill = wp->w_height - n;
1412 if (wp->w_topfill < 0)
1413 wp->w_topfill = 0;
1414 }
1415 }
1416 }
1417}
1418
1419/*
1420 * Use as many filler lines as possible for w_topline. Make sure w_topline
1421 * is still visible.
1422 */
1423 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001424max_topfill(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001425{
1426 int n;
1427
1428 n = plines_nofill(curwin->w_topline);
1429 if (n >= curwin->w_height)
1430 curwin->w_topfill = 0;
1431 else
1432 {
1433 curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline);
1434 if (curwin->w_topfill + n > curwin->w_height)
1435 curwin->w_topfill = curwin->w_height - n;
1436 }
1437}
1438#endif
1439
1440#if defined(FEAT_INS_EXPAND) || defined(PROTO)
1441/*
1442 * Scroll the screen one line down, but don't do it if it would move the
1443 * cursor off the screen.
1444 */
1445 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001446scrolldown_clamp(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001447{
1448 int end_row;
1449#ifdef FEAT_DIFF
1450 int can_fill = (curwin->w_topfill
1451 < diff_check_fill(curwin, curwin->w_topline));
1452#endif
1453
1454 if (curwin->w_topline <= 1
1455#ifdef FEAT_DIFF
1456 && !can_fill
1457#endif
1458 )
1459 return;
1460
1461 validate_cursor(); /* w_wrow needs to be valid */
1462
1463 /*
1464 * Compute the row number of the last row of the cursor line
1465 * and make sure it doesn't go off the screen. Make sure the cursor
1466 * doesn't go past 'scrolloff' lines from the screen end.
1467 */
1468 end_row = curwin->w_wrow;
1469#ifdef FEAT_DIFF
1470 if (can_fill)
1471 ++end_row;
1472 else
1473 end_row += plines_nofill(curwin->w_topline - 1);
1474#else
1475 end_row += plines(curwin->w_topline - 1);
1476#endif
Bram Moolenaar4033c552017-09-16 20:54:51 +02001477 if (curwin->w_p_wrap && curwin->w_width != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001478 {
1479 validate_cheight();
1480 validate_virtcol();
1481 end_row += curwin->w_cline_height - 1 -
Bram Moolenaar02631462017-09-22 15:20:32 +02001482 curwin->w_virtcol / curwin->w_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001483 }
1484 if (end_row < curwin->w_height - p_so)
1485 {
1486#ifdef FEAT_DIFF
1487 if (can_fill)
1488 {
1489 ++curwin->w_topfill;
1490 check_topfill(curwin, TRUE);
1491 }
1492 else
1493 {
1494 --curwin->w_topline;
1495 curwin->w_topfill = 0;
1496 }
1497#else
1498 --curwin->w_topline;
1499#endif
1500#ifdef FEAT_FOLDING
Bram Moolenaarcde88542015-08-11 19:14:00 +02001501 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001502#endif
1503 --curwin->w_botline; /* approximate w_botline */
1504 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
1505 }
1506}
1507
1508/*
1509 * Scroll the screen one line up, but don't do it if it would move the cursor
1510 * off the screen.
1511 */
1512 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001513scrollup_clamp(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001514{
1515 int start_row;
1516
1517 if (curwin->w_topline == curbuf->b_ml.ml_line_count
1518#ifdef FEAT_DIFF
1519 && curwin->w_topfill == 0
1520#endif
1521 )
1522 return;
1523
1524 validate_cursor(); /* w_wrow needs to be valid */
1525
1526 /*
1527 * Compute the row number of the first row of the cursor line
1528 * and make sure it doesn't go off the screen. Make sure the cursor
1529 * doesn't go before 'scrolloff' lines from the screen start.
1530 */
1531#ifdef FEAT_DIFF
1532 start_row = curwin->w_wrow - plines_nofill(curwin->w_topline)
1533 - curwin->w_topfill;
1534#else
1535 start_row = curwin->w_wrow - plines(curwin->w_topline);
1536#endif
Bram Moolenaar4033c552017-09-16 20:54:51 +02001537 if (curwin->w_p_wrap && curwin->w_width != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001538 {
1539 validate_virtcol();
Bram Moolenaar02631462017-09-22 15:20:32 +02001540 start_row -= curwin->w_virtcol / curwin->w_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001541 }
1542 if (start_row >= p_so)
1543 {
1544#ifdef FEAT_DIFF
1545 if (curwin->w_topfill > 0)
1546 --curwin->w_topfill;
1547 else
1548#endif
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00001549 {
1550#ifdef FEAT_FOLDING
1551 (void)hasFolding(curwin->w_topline, NULL, &curwin->w_topline);
1552#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001553 ++curwin->w_topline;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00001554 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001555 ++curwin->w_botline; /* approximate w_botline */
1556 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
1557 }
1558}
1559#endif /* FEAT_INS_EXPAND */
1560
1561/*
1562 * Add one line above "lp->lnum". This can be a filler line, a closed fold or
1563 * a (wrapped) text line. Uses and sets "lp->fill".
1564 * Returns the height of the added line in "lp->height".
Bram Moolenaarbacd9da2010-02-17 18:20:37 +01001565 * Lines above the first one are incredibly high: MAXCOL.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001566 */
1567 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001568topline_back(lineoff_T *lp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001569{
1570#ifdef FEAT_DIFF
1571 if (lp->fill < diff_check_fill(curwin, lp->lnum))
1572 {
1573 /* Add a filler line. */
1574 ++lp->fill;
1575 lp->height = 1;
1576 }
1577 else
1578#endif
1579 {
1580 --lp->lnum;
1581#ifdef FEAT_DIFF
1582 lp->fill = 0;
1583#endif
1584 if (lp->lnum < 1)
1585 lp->height = MAXCOL;
1586 else
1587#ifdef FEAT_FOLDING
1588 if (hasFolding(lp->lnum, &lp->lnum, NULL))
1589 /* Add a closed fold */
1590 lp->height = 1;
1591 else
1592#endif
Bram Moolenaar43335ea2015-09-09 20:59:37 +02001593 lp->height = PLINES_NOFILL(lp->lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001594 }
1595}
1596
1597/*
1598 * Add one line below "lp->lnum". This can be a filler line, a closed fold or
1599 * a (wrapped) text line. Uses and sets "lp->fill".
1600 * Returns the height of the added line in "lp->height".
1601 * Lines below the last one are incredibly high.
1602 */
1603 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001604botline_forw(lineoff_T *lp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001605{
1606#ifdef FEAT_DIFF
1607 if (lp->fill < diff_check_fill(curwin, lp->lnum + 1))
1608 {
1609 /* Add a filler line. */
1610 ++lp->fill;
1611 lp->height = 1;
1612 }
1613 else
1614#endif
1615 {
1616 ++lp->lnum;
1617#ifdef FEAT_DIFF
1618 lp->fill = 0;
1619#endif
1620 if (lp->lnum > curbuf->b_ml.ml_line_count)
1621 lp->height = MAXCOL;
1622 else
1623#ifdef FEAT_FOLDING
1624 if (hasFolding(lp->lnum, NULL, &lp->lnum))
1625 /* Add a closed fold */
1626 lp->height = 1;
1627 else
1628#endif
1629 {
Bram Moolenaar43335ea2015-09-09 20:59:37 +02001630 lp->height = PLINES_NOFILL(lp->lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001631 }
1632 }
1633}
1634
1635#ifdef FEAT_DIFF
1636/*
1637 * Switch from including filler lines below lp->lnum to including filler
1638 * lines above loff.lnum + 1. This keeps pointing to the same line.
1639 * When there are no filler lines nothing changes.
1640 */
1641 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001642botline_topline(lineoff_T *lp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001643{
1644 if (lp->fill > 0)
1645 {
1646 ++lp->lnum;
1647 lp->fill = diff_check_fill(curwin, lp->lnum) - lp->fill + 1;
1648 }
1649}
1650
1651/*
1652 * Switch from including filler lines above lp->lnum to including filler
1653 * lines below loff.lnum - 1. This keeps pointing to the same line.
1654 * When there are no filler lines nothing changes.
1655 */
1656 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001657topline_botline(lineoff_T *lp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001658{
1659 if (lp->fill > 0)
1660 {
1661 lp->fill = diff_check_fill(curwin, lp->lnum) - lp->fill + 1;
1662 --lp->lnum;
1663 }
1664}
1665#endif
1666
1667/*
1668 * Recompute topline to put the cursor at the top of the window.
1669 * Scroll at least "min_scroll" lines.
1670 * If "always" is TRUE, always set topline (for "zt").
1671 */
1672 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001673scroll_cursor_top(int min_scroll, int always)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001674{
1675 int scrolled = 0;
1676 int extra = 0;
1677 int used;
1678 int i;
1679 linenr_T top; /* just above displayed lines */
1680 linenr_T bot; /* just below displayed lines */
1681 linenr_T old_topline = curwin->w_topline;
1682#ifdef FEAT_DIFF
1683 linenr_T old_topfill = curwin->w_topfill;
1684#endif
1685 linenr_T new_topline;
1686 int off = p_so;
1687
1688#ifdef FEAT_MOUSE
1689 if (mouse_dragging > 0)
1690 off = mouse_dragging - 1;
1691#endif
1692
1693 /*
1694 * Decrease topline until:
1695 * - it has become 1
1696 * - (part of) the cursor line is moved off the screen or
1697 * - moved at least 'scrolljump' lines and
1698 * - at least 'scrolloff' lines above and below the cursor
1699 */
1700 validate_cheight();
Bram Moolenaarcf619da2015-09-01 20:53:24 +02001701 used = curwin->w_cline_height; /* includes filler lines above */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001702 if (curwin->w_cursor.lnum < curwin->w_topline)
1703 scrolled = used;
1704
1705#ifdef FEAT_FOLDING
1706 if (hasFolding(curwin->w_cursor.lnum, &top, &bot))
1707 {
1708 --top;
1709 ++bot;
1710 }
1711 else
1712#endif
1713 {
1714 top = curwin->w_cursor.lnum - 1;
1715 bot = curwin->w_cursor.lnum + 1;
1716 }
1717 new_topline = top + 1;
1718
1719#ifdef FEAT_DIFF
Bram Moolenaara09a2c52015-09-08 17:31:59 +02001720 /* "used" already contains the number of filler lines above, don't add it
Bram Moolenaarcf619da2015-09-01 20:53:24 +02001721 * again.
Bram Moolenaara09a2c52015-09-08 17:31:59 +02001722 * Hide filler lines above cursor line by adding them to "extra". */
1723 extra += diff_check_fill(curwin, curwin->w_cursor.lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001724#endif
1725
1726 /*
1727 * Check if the lines from "top" to "bot" fit in the window. If they do,
1728 * set new_topline and advance "top" and "bot" to include more lines.
1729 */
1730 while (top > 0)
1731 {
1732#ifdef FEAT_FOLDING
1733 if (hasFolding(top, &top, NULL))
1734 /* count one logical line for a sequence of folded lines */
1735 i = 1;
1736 else
1737#endif
Bram Moolenaar43335ea2015-09-09 20:59:37 +02001738 i = PLINES_NOFILL(top);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001739 used += i;
1740 if (extra + i <= off && bot < curbuf->b_ml.ml_line_count)
1741 {
1742#ifdef FEAT_FOLDING
1743 if (hasFolding(bot, NULL, &bot))
1744 /* count one logical line for a sequence of folded lines */
1745 ++used;
1746 else
1747#endif
1748 used += plines(bot);
1749 }
1750 if (used > curwin->w_height)
1751 break;
1752 if (top < curwin->w_topline)
1753 scrolled += i;
1754
1755 /*
1756 * If scrolling is needed, scroll at least 'sj' lines.
1757 */
1758 if ((new_topline >= curwin->w_topline || scrolled > min_scroll)
1759 && extra >= off)
1760 break;
1761
1762 extra += i;
1763 new_topline = top;
1764 --top;
1765 ++bot;
1766 }
1767
1768 /*
1769 * If we don't have enough space, put cursor in the middle.
1770 * This makes sure we get the same position when using "k" and "j"
1771 * in a small window.
1772 */
1773 if (used > curwin->w_height)
1774 scroll_cursor_halfway(FALSE);
1775 else
1776 {
1777 /*
1778 * If "always" is FALSE, only adjust topline to a lower value, higher
1779 * value may happen with wrapping lines
1780 */
1781 if (new_topline < curwin->w_topline || always)
1782 curwin->w_topline = new_topline;
1783 if (curwin->w_topline > curwin->w_cursor.lnum)
1784 curwin->w_topline = curwin->w_cursor.lnum;
1785#ifdef FEAT_DIFF
1786 curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline);
1787 if (curwin->w_topfill > 0 && extra > off)
1788 {
1789 curwin->w_topfill -= extra - off;
1790 if (curwin->w_topfill < 0)
1791 curwin->w_topfill = 0;
1792 }
1793 check_topfill(curwin, FALSE);
1794#endif
1795 if (curwin->w_topline != old_topline
1796#ifdef FEAT_DIFF
1797 || curwin->w_topfill != old_topfill
1798#endif
1799 )
1800 curwin->w_valid &=
1801 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
1802 curwin->w_valid |= VALID_TOPLINE;
1803 }
1804}
1805
1806/*
1807 * Set w_empty_rows and w_filler_rows for window "wp", having used up "used"
1808 * screen lines for text lines.
1809 */
1810 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001811set_empty_rows(win_T *wp, int used)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001812{
1813#ifdef FEAT_DIFF
1814 wp->w_filler_rows = 0;
1815#endif
1816 if (used == 0)
1817 wp->w_empty_rows = 0; /* single line that doesn't fit */
1818 else
1819 {
1820 wp->w_empty_rows = wp->w_height - used;
1821#ifdef FEAT_DIFF
1822 if (wp->w_botline <= wp->w_buffer->b_ml.ml_line_count)
1823 {
1824 wp->w_filler_rows = diff_check_fill(wp, wp->w_botline);
1825 if (wp->w_empty_rows > wp->w_filler_rows)
1826 wp->w_empty_rows -= wp->w_filler_rows;
1827 else
1828 {
1829 wp->w_filler_rows = wp->w_empty_rows;
1830 wp->w_empty_rows = 0;
1831 }
1832 }
1833#endif
1834 }
1835}
1836
1837/*
1838 * Recompute topline to put the cursor at the bottom of the window.
1839 * Scroll at least "min_scroll" lines.
1840 * If "set_topbot" is TRUE, set topline and botline first (for "zb").
1841 * This is messy stuff!!!
1842 */
1843 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001844scroll_cursor_bot(int min_scroll, int set_topbot)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001845{
1846 int used;
1847 int scrolled = 0;
1848 int extra = 0;
1849 int i;
1850 linenr_T line_count;
1851 linenr_T old_topline = curwin->w_topline;
1852 lineoff_T loff;
1853 lineoff_T boff;
1854#ifdef FEAT_DIFF
1855 int old_topfill = curwin->w_topfill;
1856 int fill_below_window;
1857#endif
1858 linenr_T old_botline = curwin->w_botline;
1859 linenr_T old_valid = curwin->w_valid;
1860 int old_empty_rows = curwin->w_empty_rows;
1861 linenr_T cln; /* Cursor Line Number */
1862
1863 cln = curwin->w_cursor.lnum;
1864 if (set_topbot)
1865 {
1866 used = 0;
1867 curwin->w_botline = cln + 1;
1868#ifdef FEAT_DIFF
1869 loff.fill = 0;
1870#endif
1871 for (curwin->w_topline = curwin->w_botline;
1872 curwin->w_topline > 1;
1873 curwin->w_topline = loff.lnum)
1874 {
1875 loff.lnum = curwin->w_topline;
1876 topline_back(&loff);
Bram Moolenaarbacd9da2010-02-17 18:20:37 +01001877 if (loff.height == MAXCOL || used + loff.height > curwin->w_height)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001878 break;
1879 used += loff.height;
1880#ifdef FEAT_DIFF
1881 curwin->w_topfill = loff.fill;
1882#endif
1883 }
1884 set_empty_rows(curwin, used);
1885 curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
1886 if (curwin->w_topline != old_topline
1887#ifdef FEAT_DIFF
1888 || curwin->w_topfill != old_topfill
1889#endif
1890 )
1891 curwin->w_valid &= ~(VALID_WROW|VALID_CROW);
1892 }
1893 else
1894 validate_botline();
1895
1896 /* The lines of the cursor line itself are always used. */
1897#ifdef FEAT_DIFF
1898 used = plines_nofill(cln);
1899#else
1900 validate_cheight();
1901 used = curwin->w_cline_height;
1902#endif
1903
1904 /* If the cursor is below botline, we will at least scroll by the height
1905 * of the cursor line. Correct for empty lines, which are really part of
1906 * botline. */
1907 if (cln >= curwin->w_botline)
1908 {
1909 scrolled = used;
1910 if (cln == curwin->w_botline)
1911 scrolled -= curwin->w_empty_rows;
1912 }
1913
1914 /*
1915 * Stop counting lines to scroll when
1916 * - hitting start of the file
1917 * - scrolled nothing or at least 'sj' lines
1918 * - at least 'so' lines below the cursor
1919 * - lines between botline and cursor have been counted
1920 */
1921#ifdef FEAT_FOLDING
1922 if (!hasFolding(curwin->w_cursor.lnum, &loff.lnum, &boff.lnum))
1923#endif
1924 {
1925 loff.lnum = cln;
1926 boff.lnum = cln;
1927 }
1928#ifdef FEAT_DIFF
1929 loff.fill = 0;
1930 boff.fill = 0;
1931 fill_below_window = diff_check_fill(curwin, curwin->w_botline)
1932 - curwin->w_filler_rows;
1933#endif
1934
1935 while (loff.lnum > 1)
1936 {
1937 /* Stop when scrolled nothing or at least "min_scroll", found "extra"
1938 * context for 'scrolloff' and counted all lines below the window. */
1939 if ((((scrolled <= 0 || scrolled >= min_scroll)
1940 && extra >= (
1941#ifdef FEAT_MOUSE
Bram Moolenaar9964e462007-05-05 17:54:07 +00001942 mouse_dragging > 0 ? mouse_dragging - 1 :
Bram Moolenaar071d4272004-06-13 20:20:40 +00001943#endif
1944 p_so))
1945 || boff.lnum + 1 > curbuf->b_ml.ml_line_count)
1946 && loff.lnum <= curwin->w_botline
1947#ifdef FEAT_DIFF
1948 && (loff.lnum < curwin->w_botline
1949 || loff.fill >= fill_below_window)
1950#endif
1951 )
1952 break;
1953
1954 /* Add one line above */
1955 topline_back(&loff);
Bram Moolenaarbacd9da2010-02-17 18:20:37 +01001956 if (loff.height == MAXCOL)
1957 used = MAXCOL;
1958 else
1959 used += loff.height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001960 if (used > curwin->w_height)
1961 break;
1962 if (loff.lnum >= curwin->w_botline
1963#ifdef FEAT_DIFF
1964 && (loff.lnum > curwin->w_botline
1965 || loff.fill <= fill_below_window)
1966#endif
1967 )
1968 {
1969 /* Count screen lines that are below the window. */
1970 scrolled += loff.height;
1971 if (loff.lnum == curwin->w_botline
1972#ifdef FEAT_DIFF
1973 && boff.fill == 0
1974#endif
1975 )
1976 scrolled -= curwin->w_empty_rows;
1977 }
1978
1979 if (boff.lnum < curbuf->b_ml.ml_line_count)
1980 {
1981 /* Add one line below */
1982 botline_forw(&boff);
1983 used += boff.height;
1984 if (used > curwin->w_height)
1985 break;
1986 if (extra < (
1987#ifdef FEAT_MOUSE
1988 mouse_dragging > 0 ? mouse_dragging - 1 :
1989#endif
1990 p_so) || scrolled < min_scroll)
1991 {
1992 extra += boff.height;
1993 if (boff.lnum >= curwin->w_botline
1994#ifdef FEAT_DIFF
1995 || (boff.lnum + 1 == curwin->w_botline
1996 && boff.fill > curwin->w_filler_rows)
1997#endif
1998 )
1999 {
2000 /* Count screen lines that are below the window. */
2001 scrolled += boff.height;
2002 if (boff.lnum == curwin->w_botline
2003#ifdef FEAT_DIFF
2004 && boff.fill == 0
2005#endif
2006 )
2007 scrolled -= curwin->w_empty_rows;
2008 }
2009 }
2010 }
2011 }
2012
2013 /* curwin->w_empty_rows is larger, no need to scroll */
2014 if (scrolled <= 0)
2015 line_count = 0;
2016 /* more than a screenfull, don't scroll but redraw */
2017 else if (used > curwin->w_height)
2018 line_count = used;
2019 /* scroll minimal number of lines */
2020 else
2021 {
2022 line_count = 0;
2023#ifdef FEAT_DIFF
2024 boff.fill = curwin->w_topfill;
2025#endif
2026 boff.lnum = curwin->w_topline - 1;
2027 for (i = 0; i < scrolled && boff.lnum < curwin->w_botline; )
2028 {
2029 botline_forw(&boff);
2030 i += boff.height;
2031 ++line_count;
2032 }
2033 if (i < scrolled) /* below curwin->w_botline, don't scroll */
2034 line_count = 9999;
2035 }
2036
2037 /*
2038 * Scroll up if the cursor is off the bottom of the screen a bit.
2039 * Otherwise put it at 1/2 of the screen.
2040 */
2041 if (line_count >= curwin->w_height && line_count > min_scroll)
2042 scroll_cursor_halfway(FALSE);
2043 else
2044 scrollup(line_count, TRUE);
2045
2046 /*
2047 * If topline didn't change we need to restore w_botline and w_empty_rows
2048 * (we changed them).
2049 * If topline did change, update_screen() will set botline.
2050 */
2051 if (curwin->w_topline == old_topline && set_topbot)
2052 {
2053 curwin->w_botline = old_botline;
2054 curwin->w_empty_rows = old_empty_rows;
2055 curwin->w_valid = old_valid;
2056 }
2057 curwin->w_valid |= VALID_TOPLINE;
2058}
2059
2060/*
2061 * Recompute topline to put the cursor halfway the window
2062 * If "atend" is TRUE, also put it halfway at the end of the file.
2063 */
2064 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002065scroll_cursor_halfway(int atend)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002066{
2067 int above = 0;
2068 linenr_T topline;
2069#ifdef FEAT_DIFF
2070 int topfill = 0;
2071#endif
2072 int below = 0;
2073 int used;
2074 lineoff_T loff;
2075 lineoff_T boff;
Bram Moolenaarb8e23052014-02-11 18:58:09 +01002076#ifdef FEAT_DIFF
Bram Moolenaar12a0f222014-02-11 15:47:46 +01002077 linenr_T old_topline = curwin->w_topline;
Bram Moolenaarb8e23052014-02-11 18:58:09 +01002078#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002079
2080 loff.lnum = boff.lnum = curwin->w_cursor.lnum;
2081#ifdef FEAT_FOLDING
2082 (void)hasFolding(loff.lnum, &loff.lnum, &boff.lnum);
2083#endif
2084#ifdef FEAT_DIFF
2085 used = plines_nofill(loff.lnum);
2086 loff.fill = 0;
2087 boff.fill = 0;
2088#else
2089 used = plines(loff.lnum);
2090#endif
2091 topline = loff.lnum;
2092 while (topline > 1)
2093 {
2094 if (below <= above) /* add a line below the cursor first */
2095 {
2096 if (boff.lnum < curbuf->b_ml.ml_line_count)
2097 {
2098 botline_forw(&boff);
2099 used += boff.height;
2100 if (used > curwin->w_height)
2101 break;
2102 below += boff.height;
2103 }
2104 else
2105 {
2106 ++below; /* count a "~" line */
2107 if (atend)
2108 ++used;
2109 }
2110 }
2111
2112 if (below > above) /* add a line above the cursor */
2113 {
2114 topline_back(&loff);
Bram Moolenaarbacd9da2010-02-17 18:20:37 +01002115 if (loff.height == MAXCOL)
2116 used = MAXCOL;
2117 else
2118 used += loff.height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002119 if (used > curwin->w_height)
2120 break;
2121 above += loff.height;
2122 topline = loff.lnum;
2123#ifdef FEAT_DIFF
2124 topfill = loff.fill;
2125#endif
2126 }
2127 }
2128#ifdef FEAT_FOLDING
2129 if (!hasFolding(topline, &curwin->w_topline, NULL))
2130#endif
2131 curwin->w_topline = topline;
2132#ifdef FEAT_DIFF
2133 curwin->w_topfill = topfill;
Bram Moolenaar12a0f222014-02-11 15:47:46 +01002134 if (old_topline > curwin->w_topline + curwin->w_height)
2135 curwin->w_botfill = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002136 check_topfill(curwin, FALSE);
2137#endif
2138 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
2139 curwin->w_valid |= VALID_TOPLINE;
2140}
2141
2142/*
2143 * Correct the cursor position so that it is in a part of the screen at least
2144 * 'so' lines from the top and bottom, if possible.
2145 * If not possible, put it at the same position as scroll_cursor_halfway().
2146 * When called topline must be valid!
2147 */
2148 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002149cursor_correct(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002150{
2151 int above = 0; /* screen lines above topline */
2152 linenr_T topline;
2153 int below = 0; /* screen lines below botline */
2154 linenr_T botline;
2155 int above_wanted, below_wanted;
2156 linenr_T cln; /* Cursor Line Number */
2157 int max_off;
2158
2159 /*
2160 * How many lines we would like to have above/below the cursor depends on
2161 * whether the first/last line of the file is on screen.
2162 */
2163 above_wanted = p_so;
2164 below_wanted = p_so;
2165#ifdef FEAT_MOUSE
Bram Moolenaar9964e462007-05-05 17:54:07 +00002166 if (mouse_dragging > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002167 {
2168 above_wanted = mouse_dragging - 1;
2169 below_wanted = mouse_dragging - 1;
2170 }
2171#endif
2172 if (curwin->w_topline == 1)
2173 {
2174 above_wanted = 0;
2175 max_off = curwin->w_height / 2;
2176 if (below_wanted > max_off)
2177 below_wanted = max_off;
2178 }
2179 validate_botline();
2180 if (curwin->w_botline == curbuf->b_ml.ml_line_count + 1
2181#ifdef FEAT_MOUSE
Bram Moolenaar9964e462007-05-05 17:54:07 +00002182 && mouse_dragging == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002183#endif
2184 )
2185 {
2186 below_wanted = 0;
2187 max_off = (curwin->w_height - 1) / 2;
2188 if (above_wanted > max_off)
2189 above_wanted = max_off;
2190 }
2191
2192 /*
2193 * If there are sufficient file-lines above and below the cursor, we can
2194 * return now.
2195 */
2196 cln = curwin->w_cursor.lnum;
2197 if (cln >= curwin->w_topline + above_wanted
2198 && cln < curwin->w_botline - below_wanted
2199#ifdef FEAT_FOLDING
2200 && !hasAnyFolding(curwin)
2201#endif
2202 )
2203 return;
2204
2205 /*
2206 * Narrow down the area where the cursor can be put by taking lines from
2207 * the top and the bottom until:
2208 * - the desired context lines are found
2209 * - the lines from the top is past the lines from the bottom
2210 */
2211 topline = curwin->w_topline;
2212 botline = curwin->w_botline - 1;
2213#ifdef FEAT_DIFF
2214 /* count filler lines as context */
2215 above = curwin->w_topfill;
2216 below = curwin->w_filler_rows;
2217#endif
2218 while ((above < above_wanted || below < below_wanted) && topline < botline)
2219 {
2220 if (below < below_wanted && (below <= above || above >= above_wanted))
2221 {
2222#ifdef FEAT_FOLDING
2223 if (hasFolding(botline, &botline, NULL))
2224 ++below;
2225 else
2226#endif
2227 below += plines(botline);
2228 --botline;
2229 }
2230 if (above < above_wanted && (above < below || below >= below_wanted))
2231 {
2232#ifdef FEAT_FOLDING
2233 if (hasFolding(topline, NULL, &topline))
2234 ++above;
2235 else
2236#endif
Bram Moolenaar43335ea2015-09-09 20:59:37 +02002237 above += PLINES_NOFILL(topline);
2238#ifdef FEAT_DIFF
Bram Moolenaar071d4272004-06-13 20:20:40 +00002239 /* Count filler lines below this line as context. */
2240 if (topline < botline)
2241 above += diff_check_fill(curwin, topline + 1);
2242#endif
2243 ++topline;
2244 }
2245 }
2246 if (topline == botline || botline == 0)
2247 curwin->w_cursor.lnum = topline;
2248 else if (topline > botline)
2249 curwin->w_cursor.lnum = botline;
2250 else
2251 {
2252 if (cln < topline && curwin->w_topline > 1)
2253 {
2254 curwin->w_cursor.lnum = topline;
2255 curwin->w_valid &=
2256 ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW);
2257 }
2258 if (cln > botline && curwin->w_botline <= curbuf->b_ml.ml_line_count)
2259 {
2260 curwin->w_cursor.lnum = botline;
2261 curwin->w_valid &=
2262 ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW);
2263 }
2264 }
2265 curwin->w_valid |= VALID_TOPLINE;
2266}
2267
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +01002268static void get_scroll_overlap(lineoff_T *lp, int dir);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002269
2270/*
2271 * move screen 'count' pages up or down and update screen
2272 *
2273 * return FAIL for failure, OK otherwise
2274 */
2275 int
Bram Moolenaar9b578142016-01-30 19:39:49 +01002276onepage(int dir, long count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002277{
2278 long n;
2279 int retval = OK;
2280 lineoff_T loff;
2281 linenr_T old_topline = curwin->w_topline;
2282
2283 if (curbuf->b_ml.ml_line_count == 1) /* nothing to do */
2284 {
2285 beep_flush();
2286 return FAIL;
2287 }
2288
2289 for ( ; count > 0; --count)
2290 {
2291 validate_botline();
2292 /*
2293 * It's an error to move a page up when the first line is already on
2294 * the screen. It's an error to move a page down when the last line
2295 * is on the screen and the topline is 'scrolloff' lines from the
2296 * last line.
2297 */
2298 if (dir == FORWARD
2299 ? ((curwin->w_topline >= curbuf->b_ml.ml_line_count - p_so)
2300 && curwin->w_botline > curbuf->b_ml.ml_line_count)
2301 : (curwin->w_topline == 1
2302#ifdef FEAT_DIFF
2303 && curwin->w_topfill ==
2304 diff_check_fill(curwin, curwin->w_topline)
2305#endif
2306 ))
2307 {
2308 beep_flush();
2309 retval = FAIL;
2310 break;
2311 }
2312
2313#ifdef FEAT_DIFF
2314 loff.fill = 0;
2315#endif
2316 if (dir == FORWARD)
2317 {
Bram Moolenaara1f4cb92016-11-06 15:25:42 +01002318 if (ONE_WINDOW && p_window > 0 && p_window < Rows - 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002319 {
Bram Moolenaar4399ef42005-02-12 14:29:27 +00002320 /* Vi compatible scrolling */
2321 if (p_window <= 2)
2322 ++curwin->w_topline;
2323 else
2324 curwin->w_topline += p_window - 2;
2325 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
2326 curwin->w_topline = curbuf->b_ml.ml_line_count;
2327 curwin->w_cursor.lnum = curwin->w_topline;
2328 }
2329 else if (curwin->w_botline > curbuf->b_ml.ml_line_count)
2330 {
2331 /* at end of file */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002332 curwin->w_topline = curbuf->b_ml.ml_line_count;
2333#ifdef FEAT_DIFF
2334 curwin->w_topfill = 0;
2335#endif
2336 curwin->w_valid &= ~(VALID_WROW|VALID_CROW);
2337 }
2338 else
2339 {
2340 /* For the overlap, start with the line just below the window
2341 * and go upwards. */
2342 loff.lnum = curwin->w_botline;
2343#ifdef FEAT_DIFF
2344 loff.fill = diff_check_fill(curwin, loff.lnum)
2345 - curwin->w_filler_rows;
2346#endif
2347 get_scroll_overlap(&loff, -1);
2348 curwin->w_topline = loff.lnum;
2349#ifdef FEAT_DIFF
2350 curwin->w_topfill = loff.fill;
2351 check_topfill(curwin, FALSE);
2352#endif
2353 curwin->w_cursor.lnum = curwin->w_topline;
2354 curwin->w_valid &= ~(VALID_WCOL|VALID_CHEIGHT|VALID_WROW|
2355 VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
2356 }
2357 }
2358 else /* dir == BACKWARDS */
2359 {
2360#ifdef FEAT_DIFF
2361 if (curwin->w_topline == 1)
2362 {
2363 /* Include max number of filler lines */
2364 max_topfill();
2365 continue;
2366 }
2367#endif
Bram Moolenaara1f4cb92016-11-06 15:25:42 +01002368 if (ONE_WINDOW && p_window > 0 && p_window < Rows - 1)
Bram Moolenaar4399ef42005-02-12 14:29:27 +00002369 {
2370 /* Vi compatible scrolling (sort of) */
2371 if (p_window <= 2)
2372 --curwin->w_topline;
2373 else
2374 curwin->w_topline -= p_window - 2;
2375 if (curwin->w_topline < 1)
2376 curwin->w_topline = 1;
2377 curwin->w_cursor.lnum = curwin->w_topline + p_window - 1;
2378 if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
2379 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
2380 continue;
2381 }
2382
Bram Moolenaar071d4272004-06-13 20:20:40 +00002383 /* Find the line at the top of the window that is going to be the
2384 * line at the bottom of the window. Make sure this results in
2385 * the same line as before doing CTRL-F. */
2386 loff.lnum = curwin->w_topline - 1;
2387#ifdef FEAT_DIFF
2388 loff.fill = diff_check_fill(curwin, loff.lnum + 1)
2389 - curwin->w_topfill;
2390#endif
2391 get_scroll_overlap(&loff, 1);
2392
2393 if (loff.lnum >= curbuf->b_ml.ml_line_count)
2394 {
2395 loff.lnum = curbuf->b_ml.ml_line_count;
2396#ifdef FEAT_DIFF
2397 loff.fill = 0;
2398 }
2399 else
2400 {
2401 botline_topline(&loff);
2402#endif
2403 }
2404 curwin->w_cursor.lnum = loff.lnum;
2405
2406 /* Find the line just above the new topline to get the right line
2407 * at the bottom of the window. */
2408 n = 0;
2409 while (n <= curwin->w_height && loff.lnum >= 1)
2410 {
2411 topline_back(&loff);
Bram Moolenaarbacd9da2010-02-17 18:20:37 +01002412 if (loff.height == MAXCOL)
2413 n = MAXCOL;
2414 else
2415 n += loff.height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002416 }
Bram Moolenaarbacd9da2010-02-17 18:20:37 +01002417 if (loff.lnum < 1) /* at begin of file */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002418 {
2419 curwin->w_topline = 1;
2420#ifdef FEAT_DIFF
2421 max_topfill();
2422#endif
2423 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
2424 }
2425 else
2426 {
2427 /* Go two lines forward again. */
2428#ifdef FEAT_DIFF
2429 topline_botline(&loff);
2430#endif
2431 botline_forw(&loff);
2432 botline_forw(&loff);
2433#ifdef FEAT_DIFF
2434 botline_topline(&loff);
2435#endif
2436#ifdef FEAT_FOLDING
2437 /* We're at the wrong end of a fold now. */
2438 (void)hasFolding(loff.lnum, &loff.lnum, NULL);
2439#endif
2440
2441 /* Always scroll at least one line. Avoid getting stuck on
2442 * very long lines. */
2443 if (loff.lnum >= curwin->w_topline
2444#ifdef FEAT_DIFF
2445 && (loff.lnum > curwin->w_topline
2446 || loff.fill >= curwin->w_topfill)
2447#endif
2448 )
2449 {
2450#ifdef FEAT_DIFF
2451 /* First try using the maximum number of filler lines. If
2452 * that's not enough, backup one line. */
2453 loff.fill = curwin->w_topfill;
2454 if (curwin->w_topfill < diff_check_fill(curwin,
2455 curwin->w_topline))
2456 max_topfill();
2457 if (curwin->w_topfill == loff.fill)
2458#endif
2459 {
2460 --curwin->w_topline;
2461#ifdef FEAT_DIFF
2462 curwin->w_topfill = 0;
2463#endif
2464 }
2465 comp_botline(curwin);
2466 curwin->w_cursor.lnum = curwin->w_botline - 1;
Bram Moolenaar3d6db142014-03-28 21:49:32 +01002467 curwin->w_valid &=
2468 ~(VALID_WCOL|VALID_CHEIGHT|VALID_WROW|VALID_CROW);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002469 }
2470 else
2471 {
2472 curwin->w_topline = loff.lnum;
2473#ifdef FEAT_DIFF
2474 curwin->w_topfill = loff.fill;
2475 check_topfill(curwin, FALSE);
2476#endif
2477 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
2478 }
2479 }
2480 }
2481 }
2482#ifdef FEAT_FOLDING
2483 foldAdjustCursor();
2484#endif
2485 cursor_correct();
Bram Moolenaarbc54f3f2016-09-04 14:34:28 +02002486 check_cursor_col();
Bram Moolenaar7c626922005-02-07 22:01:03 +00002487 if (retval == OK)
2488 beginline(BL_SOL | BL_FIX);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002489 curwin->w_valid &= ~(VALID_WCOL|VALID_WROW|VALID_VIRTCOL);
2490
Bram Moolenaar907dad72018-07-10 15:07:15 +02002491 if (retval == OK && dir == FORWARD)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002492 {
Bram Moolenaar907dad72018-07-10 15:07:15 +02002493 // Avoid the screen jumping up and down when 'scrolloff' is non-zero.
2494 // But make sure we scroll at least one line (happens with mix of long
2495 // wrapping lines and non-wrapping line).
2496 if (check_top_offset())
Bram Moolenaar071d4272004-06-13 20:20:40 +00002497 {
Bram Moolenaar907dad72018-07-10 15:07:15 +02002498 scroll_cursor_top(1, FALSE);
2499 if (curwin->w_topline <= old_topline
2500 && old_topline < curbuf->b_ml.ml_line_count)
2501 {
2502 curwin->w_topline = old_topline + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002503#ifdef FEAT_FOLDING
Bram Moolenaar907dad72018-07-10 15:07:15 +02002504 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
2505#endif
2506 }
2507 }
2508#ifdef FEAT_FOLDING
2509 else if (curwin->w_botline > curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002510 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
2511#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002512 }
2513
2514 redraw_later(VALID);
2515 return retval;
2516}
2517
2518/*
2519 * Decide how much overlap to use for page-up or page-down scrolling.
2520 * This is symmetric, so that doing both keeps the same lines displayed.
2521 * Three lines are examined:
2522 *
2523 * before CTRL-F after CTRL-F / before CTRL-B
2524 * etc. l1
2525 * l1 last but one line ------------
2526 * l2 last text line l2 top text line
2527 * ------------- l3 second text line
2528 * l3 etc.
2529 */
2530 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002531get_scroll_overlap(lineoff_T *lp, int dir)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002532{
2533 int h1, h2, h3, h4;
2534 int min_height = curwin->w_height - 2;
2535 lineoff_T loff0, loff1, loff2;
2536
2537#ifdef FEAT_DIFF
2538 if (lp->fill > 0)
2539 lp->height = 1;
2540 else
2541 lp->height = plines_nofill(lp->lnum);
2542#else
2543 lp->height = plines(lp->lnum);
2544#endif
2545 h1 = lp->height;
2546 if (h1 > min_height)
2547 return; /* no overlap */
2548
2549 loff0 = *lp;
2550 if (dir > 0)
2551 botline_forw(lp);
2552 else
2553 topline_back(lp);
2554 h2 = lp->height;
Bram Moolenaarf4f19562012-11-28 18:22:11 +01002555 if (h2 == MAXCOL || h2 + h1 > min_height)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002556 {
2557 *lp = loff0; /* no overlap */
2558 return;
2559 }
2560
2561 loff1 = *lp;
2562 if (dir > 0)
2563 botline_forw(lp);
2564 else
2565 topline_back(lp);
2566 h3 = lp->height;
Bram Moolenaarf4f19562012-11-28 18:22:11 +01002567 if (h3 == MAXCOL || h3 + h2 > min_height)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002568 {
2569 *lp = loff0; /* no overlap */
2570 return;
2571 }
2572
2573 loff2 = *lp;
2574 if (dir > 0)
2575 botline_forw(lp);
2576 else
2577 topline_back(lp);
2578 h4 = lp->height;
Bram Moolenaarf4f19562012-11-28 18:22:11 +01002579 if (h4 == MAXCOL || h4 + h3 + h2 > min_height || h3 + h2 + h1 > min_height)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002580 *lp = loff1; /* 1 line overlap */
2581 else
2582 *lp = loff2; /* 2 lines overlap */
2583 return;
2584}
2585
2586/* #define KEEP_SCREEN_LINE */
2587/*
2588 * Scroll 'scroll' lines up or down.
2589 */
2590 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002591halfpage(int flag, linenr_T Prenum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002592{
2593 long scrolled = 0;
2594 int i;
2595 int n;
2596 int room;
2597
2598 if (Prenum)
2599 curwin->w_p_scr = (Prenum > curwin->w_height) ?
2600 curwin->w_height : Prenum;
2601 n = (curwin->w_p_scr <= curwin->w_height) ?
2602 curwin->w_p_scr : curwin->w_height;
2603
Bram Moolenaard5d37532017-03-27 23:02:07 +02002604 update_topline();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002605 validate_botline();
2606 room = curwin->w_empty_rows;
2607#ifdef FEAT_DIFF
2608 room += curwin->w_filler_rows;
2609#endif
2610 if (flag)
2611 {
2612 /*
2613 * scroll the text up
2614 */
2615 while (n > 0 && curwin->w_botline <= curbuf->b_ml.ml_line_count)
2616 {
2617#ifdef FEAT_DIFF
2618 if (curwin->w_topfill > 0)
2619 {
2620 i = 1;
2621 if (--n < 0 && scrolled > 0)
2622 break;
2623 --curwin->w_topfill;
2624 }
2625 else
2626#endif
2627 {
Bram Moolenaar43335ea2015-09-09 20:59:37 +02002628 i = PLINES_NOFILL(curwin->w_topline);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002629 n -= i;
2630 if (n < 0 && scrolled > 0)
2631 break;
2632#ifdef FEAT_FOLDING
2633 (void)hasFolding(curwin->w_topline, NULL, &curwin->w_topline);
2634#endif
2635 ++curwin->w_topline;
2636#ifdef FEAT_DIFF
2637 curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline);
2638#endif
2639
2640#ifndef KEEP_SCREEN_LINE
2641 if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
2642 {
2643 ++curwin->w_cursor.lnum;
2644 curwin->w_valid &=
2645 ~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL);
2646 }
2647#endif
2648 }
2649 curwin->w_valid &= ~(VALID_CROW|VALID_WROW);
2650 scrolled += i;
2651
2652 /*
2653 * Correct w_botline for changed w_topline.
2654 * Won't work when there are filler lines.
2655 */
2656#ifdef FEAT_DIFF
2657 if (curwin->w_p_diff)
2658 curwin->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP);
2659 else
2660#endif
2661 {
2662 room += i;
2663 do
2664 {
2665 i = plines(curwin->w_botline);
2666 if (i > room)
2667 break;
2668#ifdef FEAT_FOLDING
2669 (void)hasFolding(curwin->w_botline, NULL,
2670 &curwin->w_botline);
2671#endif
2672 ++curwin->w_botline;
2673 room -= i;
2674 } while (curwin->w_botline <= curbuf->b_ml.ml_line_count);
2675 }
2676 }
2677
2678#ifndef KEEP_SCREEN_LINE
2679 /*
2680 * When hit bottom of the file: move cursor down.
2681 */
2682 if (n > 0)
2683 {
2684# ifdef FEAT_FOLDING
2685 if (hasAnyFolding(curwin))
2686 {
2687 while (--n >= 0
2688 && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
2689 {
2690 (void)hasFolding(curwin->w_cursor.lnum, NULL,
2691 &curwin->w_cursor.lnum);
2692 ++curwin->w_cursor.lnum;
2693 }
2694 }
2695 else
2696# endif
2697 curwin->w_cursor.lnum += n;
2698 check_cursor_lnum();
2699 }
2700#else
2701 /* try to put the cursor in the same screen line */
2702 while ((curwin->w_cursor.lnum < curwin->w_topline || scrolled > 0)
2703 && curwin->w_cursor.lnum < curwin->w_botline - 1)
2704 {
2705 scrolled -= plines(curwin->w_cursor.lnum);
2706 if (scrolled < 0 && curwin->w_cursor.lnum >= curwin->w_topline)
2707 break;
2708# ifdef FEAT_FOLDING
2709 (void)hasFolding(curwin->w_cursor.lnum, NULL,
2710 &curwin->w_cursor.lnum);
2711# endif
2712 ++curwin->w_cursor.lnum;
2713 }
2714#endif
2715 }
2716 else
2717 {
2718 /*
2719 * scroll the text down
2720 */
2721 while (n > 0 && curwin->w_topline > 1)
2722 {
2723#ifdef FEAT_DIFF
2724 if (curwin->w_topfill < diff_check_fill(curwin, curwin->w_topline))
2725 {
2726 i = 1;
2727 if (--n < 0 && scrolled > 0)
2728 break;
2729 ++curwin->w_topfill;
2730 }
2731 else
2732#endif
2733 {
Bram Moolenaar43335ea2015-09-09 20:59:37 +02002734 i = PLINES_NOFILL(curwin->w_topline - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002735 n -= i;
2736 if (n < 0 && scrolled > 0)
2737 break;
2738 --curwin->w_topline;
2739#ifdef FEAT_FOLDING
2740 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
2741#endif
2742#ifdef FEAT_DIFF
2743 curwin->w_topfill = 0;
2744#endif
2745 }
2746 curwin->w_valid &= ~(VALID_CROW|VALID_WROW|
2747 VALID_BOTLINE|VALID_BOTLINE_AP);
2748 scrolled += i;
2749#ifndef KEEP_SCREEN_LINE
2750 if (curwin->w_cursor.lnum > 1)
2751 {
2752 --curwin->w_cursor.lnum;
2753 curwin->w_valid &= ~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL);
2754 }
2755#endif
2756 }
2757#ifndef KEEP_SCREEN_LINE
2758 /*
2759 * When hit top of the file: move cursor up.
2760 */
2761 if (n > 0)
2762 {
2763 if (curwin->w_cursor.lnum <= (linenr_T)n)
2764 curwin->w_cursor.lnum = 1;
2765 else
2766# ifdef FEAT_FOLDING
2767 if (hasAnyFolding(curwin))
2768 {
2769 while (--n >= 0 && curwin->w_cursor.lnum > 1)
2770 {
2771 --curwin->w_cursor.lnum;
2772 (void)hasFolding(curwin->w_cursor.lnum,
2773 &curwin->w_cursor.lnum, NULL);
2774 }
2775 }
2776 else
2777# endif
2778 curwin->w_cursor.lnum -= n;
2779 }
2780#else
2781 /* try to put the cursor in the same screen line */
2782 scrolled += n; /* move cursor when topline is 1 */
2783 while (curwin->w_cursor.lnum > curwin->w_topline
2784 && (scrolled > 0 || curwin->w_cursor.lnum >= curwin->w_botline))
2785 {
2786 scrolled -= plines(curwin->w_cursor.lnum - 1);
2787 if (scrolled < 0 && curwin->w_cursor.lnum < curwin->w_botline)
2788 break;
2789 --curwin->w_cursor.lnum;
2790# ifdef FEAT_FOLDING
2791 foldAdjustCursor();
2792# endif
2793 }
2794#endif
2795 }
2796# ifdef FEAT_FOLDING
2797 /* Move cursor to first line of closed fold. */
2798 foldAdjustCursor();
2799# endif
2800#ifdef FEAT_DIFF
2801 check_topfill(curwin, !flag);
2802#endif
2803 cursor_correct();
2804 beginline(BL_SOL | BL_FIX);
2805 redraw_later(VALID);
2806}
Bram Moolenaar860cae12010-06-05 23:22:07 +02002807
Bram Moolenaar860cae12010-06-05 23:22:07 +02002808 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002809do_check_cursorbind(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002810{
2811 linenr_T line = curwin->w_cursor.lnum;
Bram Moolenaar1ea69b72012-03-16 19:24:26 +01002812 colnr_T col = curwin->w_cursor.col;
2813# ifdef FEAT_VIRTUALEDIT
2814 colnr_T coladd = curwin->w_cursor.coladd;
2815# endif
Bram Moolenaar524780d2012-03-28 14:19:50 +02002816 colnr_T curswant = curwin->w_curswant;
2817 int set_curswant = curwin->w_set_curswant;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002818 win_T *old_curwin = curwin;
2819 buf_T *old_curbuf = curbuf;
Bram Moolenaar61452852011-02-01 18:01:11 +01002820 int restart_edit_save;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002821 int old_VIsual_select = VIsual_select;
2822 int old_VIsual_active = VIsual_active;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002823
2824 /*
2825 * loop through the cursorbound windows
2826 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002827 VIsual_select = VIsual_active = 0;
Bram Moolenaar29323592016-07-24 22:04:11 +02002828 FOR_ALL_WINDOWS(curwin)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002829 {
2830 curbuf = curwin->w_buffer;
2831 /* skip original window and windows with 'noscrollbind' */
2832 if (curwin != old_curwin && curwin->w_p_crb)
2833 {
2834# ifdef FEAT_DIFF
2835 if (curwin->w_p_diff)
Bram Moolenaar025e3e02016-10-18 14:50:18 +02002836 curwin->w_cursor.lnum =
2837 diff_get_corresponding_line(old_curbuf, line);
Bram Moolenaar860cae12010-06-05 23:22:07 +02002838 else
2839# endif
2840 curwin->w_cursor.lnum = line;
2841 curwin->w_cursor.col = col;
Bram Moolenaar1ea69b72012-03-16 19:24:26 +01002842# ifdef FEAT_VIRTUALEDIT
2843 curwin->w_cursor.coladd = coladd;
2844# endif
Bram Moolenaar524780d2012-03-28 14:19:50 +02002845 curwin->w_curswant = curswant;
2846 curwin->w_set_curswant = set_curswant;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002847
Bram Moolenaar61452852011-02-01 18:01:11 +01002848 /* Make sure the cursor is in a valid position. Temporarily set
2849 * "restart_edit" to allow the cursor to be beyond the EOL. */
2850 restart_edit_save = restart_edit;
2851 restart_edit = TRUE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002852 check_cursor();
Bram Moolenaar1b9750d2017-01-15 20:51:37 +01002853# ifdef FEAT_SYN_HL
Bram Moolenaar9506cad2017-01-15 13:53:49 +01002854 if (curwin->w_p_cul || curwin->w_p_cuc)
Bram Moolenaar519d7782017-01-14 14:54:33 +01002855 validate_cursor();
Bram Moolenaar1b9750d2017-01-15 20:51:37 +01002856# endif
Bram Moolenaar61452852011-02-01 18:01:11 +01002857 restart_edit = restart_edit_save;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002858# ifdef FEAT_MBYTE
2859 /* Correct cursor for multi-byte character. */
2860 if (has_mbyte)
2861 mb_adjust_cursor();
2862# endif
Bram Moolenaar9506cad2017-01-15 13:53:49 +01002863 redraw_later(VALID);
Bram Moolenaarf3d419d2011-01-22 21:05:07 +01002864
2865 /* Only scroll when 'scrollbind' hasn't done this. */
2866 if (!curwin->w_p_scb)
2867 update_topline();
Bram Moolenaar860cae12010-06-05 23:22:07 +02002868 curwin->w_redr_status = TRUE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002869 }
2870 }
2871
2872 /*
2873 * reset current-window
2874 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002875 VIsual_select = old_VIsual_select;
2876 VIsual_active = old_VIsual_active;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002877 curwin = old_curwin;
2878 curbuf = old_curbuf;
2879}