blob: 6322acf989c9f4bc975395cd7d8817742b38717d [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
126/*
Bram Moolenaar3d6db142014-03-28 21:49:32 +0100127 * Redraw when w_cline_row changes and 'relativenumber' or 'cursorline' is
128 * set.
129 */
130 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100131redraw_for_cursorline(win_T *wp)
Bram Moolenaar3d6db142014-03-28 21:49:32 +0100132{
133 if ((wp->w_p_rnu
134#ifdef FEAT_SYN_HL
135 || wp->w_p_cul
136#endif
137 )
138 && (wp->w_valid & VALID_CROW) == 0
139# ifdef FEAT_INS_EXPAND
140 && !pum_visible()
141# endif
142 )
143 redraw_win_later(wp, SOME_VALID);
144}
145
146/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000147 * Update curwin->w_topline and redraw if necessary.
148 * Used to update the screen before printing a message.
149 */
150 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100151update_topline_redraw(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000152{
153 update_topline();
154 if (must_redraw)
155 update_screen(0);
156}
157
158/*
159 * Update curwin->w_topline to move the cursor onto the screen.
160 */
161 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100162update_topline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000163{
164 long line_count;
165 int halfheight;
166 int n;
167 linenr_T old_topline;
168#ifdef FEAT_DIFF
169 int old_topfill;
170#endif
171#ifdef FEAT_FOLDING
172 linenr_T lnum;
173#endif
174 int check_topline = FALSE;
175 int check_botline = FALSE;
176#ifdef FEAT_MOUSE
177 int save_so = p_so;
178#endif
179
180 if (!screen_valid(TRUE))
181 return;
182
Bram Moolenaarcfc216e2014-09-23 18:37:56 +0200183 /* If the window height is zero just use the cursor line. */
184 if (curwin->w_height == 0)
185 {
186 curwin->w_topline = curwin->w_cursor.lnum;
187 curwin->w_botline = curwin->w_topline;
188 curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
189#ifdef FEAT_SCROLLBIND
190 curwin->w_scbind_pos = 1;
191#endif
192 return;
193 }
194
Bram Moolenaar071d4272004-06-13 20:20:40 +0000195 check_cursor_moved(curwin);
196 if (curwin->w_valid & VALID_TOPLINE)
197 return;
198
199#ifdef FEAT_MOUSE
200 /* When dragging with the mouse, don't scroll that quickly */
Bram Moolenaar9964e462007-05-05 17:54:07 +0000201 if (mouse_dragging > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000202 p_so = mouse_dragging - 1;
203#endif
204
205 old_topline = curwin->w_topline;
206#ifdef FEAT_DIFF
207 old_topfill = curwin->w_topfill;
208#endif
209
210 /*
211 * If the buffer is empty, always set topline to 1.
212 */
213 if (bufempty()) /* special case - file is empty */
214 {
215 if (curwin->w_topline != 1)
216 redraw_later(NOT_VALID);
217 curwin->w_topline = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000218 curwin->w_botline = 2;
219 curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
220#ifdef FEAT_SCROLLBIND
221 curwin->w_scbind_pos = 1;
222#endif
223 }
224
225 /*
226 * If the cursor is above or near the top of the window, scroll the window
227 * to show the line the cursor is in, with 'scrolloff' context.
228 */
229 else
230 {
231 if (curwin->w_topline > 1)
232 {
233 /* If the cursor is above topline, scrolling is always needed.
234 * If the cursor is far below topline and there is no folding,
235 * scrolling down is never needed. */
236 if (curwin->w_cursor.lnum < curwin->w_topline)
237 check_topline = TRUE;
238 else if (check_top_offset())
239 check_topline = TRUE;
240 }
241#ifdef FEAT_DIFF
242 /* Check if there are more filler lines than allowed. */
243 if (!check_topline && curwin->w_topfill > diff_check_fill(curwin,
244 curwin->w_topline))
245 check_topline = TRUE;
246#endif
247
248 if (check_topline)
249 {
250 halfheight = curwin->w_height / 2 - 1;
251 if (halfheight < 2)
252 halfheight = 2;
253
254#ifdef FEAT_FOLDING
255 if (hasAnyFolding(curwin))
256 {
257 /* Count the number of logical lines between the cursor and
258 * topline + p_so (approximation of how much will be
259 * scrolled). */
260 n = 0;
261 for (lnum = curwin->w_cursor.lnum;
262 lnum < curwin->w_topline + p_so; ++lnum)
263 {
264 ++n;
265 /* stop at end of file or when we know we are far off */
266 if (lnum >= curbuf->b_ml.ml_line_count || n >= halfheight)
267 break;
268 (void)hasFolding(lnum, NULL, &lnum);
269 }
270 }
271 else
272#endif
273 n = curwin->w_topline + p_so - curwin->w_cursor.lnum;
274
275 /* If we weren't very close to begin with, we scroll to put the
276 * cursor in the middle of the window. Otherwise put the cursor
277 * near the top of the window. */
278 if (n >= halfheight)
279 scroll_cursor_halfway(FALSE);
280 else
281 {
Bram Moolenaar1e015462005-09-25 22:16:38 +0000282 scroll_cursor_top(scrolljump_value(), FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000283 check_botline = TRUE;
284 }
285 }
286
287 else
288 {
289#ifdef FEAT_FOLDING
290 /* Make sure topline is the first line of a fold. */
291 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
292#endif
293 check_botline = TRUE;
294 }
295 }
296
297 /*
298 * If the cursor is below the bottom of the window, scroll the window
299 * to put the cursor on the window.
300 * When w_botline is invalid, recompute it first, to avoid a redraw later.
301 * If w_botline was approximated, we might need a redraw later in a few
302 * cases, but we don't want to spend (a lot of) time recomputing w_botline
303 * for every small change.
304 */
305 if (check_botline)
306 {
307 if (!(curwin->w_valid & VALID_BOTLINE_AP))
308 validate_botline();
309
310 if (curwin->w_botline <= curbuf->b_ml.ml_line_count)
311 {
Bram Moolenaard4153d42008-11-15 15:06:17 +0000312 if (curwin->w_cursor.lnum < curwin->w_botline)
313 {
314 if (((long)curwin->w_cursor.lnum
Bram Moolenaar071d4272004-06-13 20:20:40 +0000315 >= (long)curwin->w_botline - p_so
316#ifdef FEAT_FOLDING
317 || hasAnyFolding(curwin)
318#endif
319 ))
Bram Moolenaard4153d42008-11-15 15:06:17 +0000320 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000321 lineoff_T loff;
322
Bram Moolenaard4153d42008-11-15 15:06:17 +0000323 /* Cursor is (a few lines) above botline, check if there are
324 * 'scrolloff' window lines below the cursor. If not, need to
325 * scroll. */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000326 n = curwin->w_empty_rows;
327 loff.lnum = curwin->w_cursor.lnum;
328#ifdef FEAT_FOLDING
329 /* In a fold go to its last line. */
330 (void)hasFolding(loff.lnum, NULL, &loff.lnum);
331#endif
332#ifdef FEAT_DIFF
333 loff.fill = 0;
334 n += curwin->w_filler_rows;
335#endif
336 loff.height = 0;
337 while (loff.lnum < curwin->w_botline
338#ifdef FEAT_DIFF
339 && (loff.lnum + 1 < curwin->w_botline || loff.fill == 0)
340#endif
341 )
342 {
343 n += loff.height;
344 if (n >= p_so)
345 break;
346 botline_forw(&loff);
347 }
348 if (n >= p_so)
349 /* sufficient context, no need to scroll */
350 check_botline = FALSE;
Bram Moolenaard4153d42008-11-15 15:06:17 +0000351 }
352 else
353 /* sufficient context, no need to scroll */
354 check_botline = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000355 }
356 if (check_botline)
357 {
358#ifdef FEAT_FOLDING
359 if (hasAnyFolding(curwin))
360 {
361 /* Count the number of logical lines between the cursor and
362 * botline - p_so (approximation of how much will be
363 * scrolled). */
364 line_count = 0;
365 for (lnum = curwin->w_cursor.lnum;
366 lnum >= curwin->w_botline - p_so; --lnum)
367 {
368 ++line_count;
369 /* stop at end of file or when we know we are far off */
370 if (lnum <= 0 || line_count > curwin->w_height + 1)
371 break;
372 (void)hasFolding(lnum, &lnum, NULL);
373 }
374 }
375 else
376#endif
377 line_count = curwin->w_cursor.lnum - curwin->w_botline
378 + 1 + p_so;
379 if (line_count <= curwin->w_height + 1)
Bram Moolenaar1e015462005-09-25 22:16:38 +0000380 scroll_cursor_bot(scrolljump_value(), FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000381 else
382 scroll_cursor_halfway(FALSE);
383 }
384 }
385 }
386 curwin->w_valid |= VALID_TOPLINE;
387
388 /*
389 * Need to redraw when topline changed.
390 */
391 if (curwin->w_topline != old_topline
392#ifdef FEAT_DIFF
393 || curwin->w_topfill != old_topfill
394#endif
395 )
396 {
Bram Moolenaar76b9b362012-02-04 23:35:00 +0100397 dollar_vcol = -1;
Bram Moolenaar2b48ad52006-03-12 21:56:11 +0000398 if (curwin->w_skipcol != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000399 {
400 curwin->w_skipcol = 0;
401 redraw_later(NOT_VALID);
402 }
403 else
404 redraw_later(VALID);
405 /* May need to set w_skipcol when cursor in w_topline. */
406 if (curwin->w_cursor.lnum == curwin->w_topline)
407 validate_cursor();
408 }
409
410#ifdef FEAT_MOUSE
411 p_so = save_so;
412#endif
413}
414
415/*
Bram Moolenaar1e015462005-09-25 22:16:38 +0000416 * Return the scrolljump value to use for the current window.
417 * When 'scrolljump' is positive use it as-is.
418 * When 'scrolljump' is negative use it as a percentage of the window height.
419 */
420 static int
Bram Moolenaar9b578142016-01-30 19:39:49 +0100421scrolljump_value(void)
Bram Moolenaar1e015462005-09-25 22:16:38 +0000422{
423 if (p_sj >= 0)
424 return (int)p_sj;
425 return (curwin->w_height * -p_sj) / 100;
426}
427
428/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000429 * Return TRUE when there are not 'scrolloff' lines above the cursor for the
430 * current window.
431 */
432 static int
Bram Moolenaar9b578142016-01-30 19:39:49 +0100433check_top_offset(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000434{
435 lineoff_T loff;
436 int n;
437
438 if (curwin->w_cursor.lnum < curwin->w_topline + p_so
439#ifdef FEAT_FOLDING
440 || hasAnyFolding(curwin)
441#endif
442 )
443 {
444 loff.lnum = curwin->w_cursor.lnum;
445#ifdef FEAT_DIFF
446 loff.fill = 0;
447 n = curwin->w_topfill; /* always have this context */
448#else
449 n = 0;
450#endif
451 /* Count the visible screen lines above the cursor line. */
452 while (n < p_so)
453 {
454 topline_back(&loff);
455 /* Stop when included a line above the window. */
456 if (loff.lnum < curwin->w_topline
457#ifdef FEAT_DIFF
458 || (loff.lnum == curwin->w_topline && loff.fill > 0)
459#endif
460 )
461 break;
462 n += loff.height;
463 }
464 if (n < p_so)
465 return TRUE;
466 }
467 return FALSE;
468}
469
470 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100471update_curswant(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000472{
473 if (curwin->w_set_curswant)
474 {
475 validate_virtcol();
476 curwin->w_curswant = curwin->w_virtcol;
477 curwin->w_set_curswant = FALSE;
478 }
479}
480
481/*
482 * Check if the cursor has moved. Set the w_valid flag accordingly.
483 */
484 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100485check_cursor_moved(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000486{
487 if (wp->w_cursor.lnum != wp->w_valid_cursor.lnum)
488 {
489 wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL
490 |VALID_CHEIGHT|VALID_CROW|VALID_TOPLINE);
491 wp->w_valid_cursor = wp->w_cursor;
492 wp->w_valid_leftcol = wp->w_leftcol;
493 }
494 else if (wp->w_cursor.col != wp->w_valid_cursor.col
495 || wp->w_leftcol != wp->w_valid_leftcol
496#ifdef FEAT_VIRTUALEDIT
497 || wp->w_cursor.coladd != wp->w_valid_cursor.coladd
498#endif
499 )
500 {
501 wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL);
502 wp->w_valid_cursor.col = wp->w_cursor.col;
503 wp->w_valid_leftcol = wp->w_leftcol;
504#ifdef FEAT_VIRTUALEDIT
505 wp->w_valid_cursor.coladd = wp->w_cursor.coladd;
506#endif
507 }
508}
509
510/*
511 * Call this function when some window settings have changed, which require
512 * the cursor position, botline and topline to be recomputed and the window to
513 * be redrawn. E.g, when changing the 'wrap' option or folding.
514 */
515 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100516changed_window_setting(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000517{
518 changed_window_setting_win(curwin);
519}
520
521 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100522changed_window_setting_win(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000523{
524 wp->w_lines_valid = 0;
525 changed_line_abv_curs_win(wp);
526 wp->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP|VALID_TOPLINE);
527 redraw_win_later(wp, NOT_VALID);
528}
529
530/*
531 * Set wp->w_topline to a certain number.
532 */
533 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100534set_topline(win_T *wp, linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000535{
536#ifdef FEAT_FOLDING
537 /* go to first of folded lines */
538 (void)hasFoldingWin(wp, lnum, &lnum, NULL, TRUE, NULL);
539#endif
540 /* Approximate the value of w_botline */
541 wp->w_botline += lnum - wp->w_topline;
542 wp->w_topline = lnum;
Bram Moolenaard4153d42008-11-15 15:06:17 +0000543#ifdef FEAT_AUTOCMD
544 wp->w_topline_was_set = TRUE;
545#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000546#ifdef FEAT_DIFF
547 wp->w_topfill = 0;
548#endif
549 wp->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_TOPLINE);
550 /* Don't set VALID_TOPLINE here, 'scrolloff' needs to be checked. */
551 redraw_later(VALID);
552}
553
554/*
555 * Call this function when the length of the cursor line (in screen
556 * characters) has changed, and the change is before the cursor.
557 * Need to take care of w_botline separately!
558 */
559 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100560changed_cline_bef_curs(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000561{
562 curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL
563 |VALID_CHEIGHT|VALID_TOPLINE);
564}
565
566 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100567changed_cline_bef_curs_win(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000568{
569 wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL
570 |VALID_CHEIGHT|VALID_TOPLINE);
571}
572
Bram Moolenaar071d4272004-06-13 20:20:40 +0000573/*
574 * Call this function when the length of a line (in screen characters) above
575 * the cursor have changed.
576 * Need to take care of w_botline separately!
577 */
578 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100579changed_line_abv_curs(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000580{
581 curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL|VALID_CROW
582 |VALID_CHEIGHT|VALID_TOPLINE);
583}
584
585 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100586changed_line_abv_curs_win(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000587{
588 wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL|VALID_CROW
589 |VALID_CHEIGHT|VALID_TOPLINE);
590}
591
592/*
593 * Make sure the value of curwin->w_botline is valid.
594 */
595 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100596validate_botline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000597{
598 if (!(curwin->w_valid & VALID_BOTLINE))
599 comp_botline(curwin);
600}
601
602/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000603 * Mark curwin->w_botline as invalid (because of some change in the buffer).
604 */
605 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100606invalidate_botline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000607{
608 curwin->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP);
609}
610
611 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100612invalidate_botline_win(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000613{
614 wp->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP);
615}
616
Bram Moolenaar071d4272004-06-13 20:20:40 +0000617 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100618approximate_botline_win(
619 win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000620{
621 wp->w_valid &= ~VALID_BOTLINE;
622}
623
Bram Moolenaar071d4272004-06-13 20:20:40 +0000624/*
625 * Return TRUE if curwin->w_wrow and curwin->w_wcol are valid.
626 */
627 int
Bram Moolenaar9b578142016-01-30 19:39:49 +0100628cursor_valid(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000629{
630 check_cursor_moved(curwin);
631 return ((curwin->w_valid & (VALID_WROW|VALID_WCOL)) ==
632 (VALID_WROW|VALID_WCOL));
633}
634
635/*
636 * Validate cursor position. Makes sure w_wrow and w_wcol are valid.
637 * w_topline must be valid, you may need to call update_topline() first!
638 */
639 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100640validate_cursor(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000641{
642 check_cursor_moved(curwin);
643 if ((curwin->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW))
644 curs_columns(TRUE);
645}
646
647#if defined(FEAT_GUI) || defined(PROTO)
648/*
649 * validate w_cline_row.
650 */
651 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100652validate_cline_row(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000653{
654 /*
655 * First make sure that w_topline is valid (after moving the cursor).
656 */
657 update_topline();
658 check_cursor_moved(curwin);
659 if (!(curwin->w_valid & VALID_CROW))
Bram Moolenaar3f9be972014-12-13 21:09:57 +0100660 curs_rows(curwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000661}
662#endif
663
664/*
665 * Compute wp->w_cline_row and wp->w_cline_height, based on the current value
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200666 * of wp->w_topline.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000667 */
668 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100669curs_rows(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000670{
671 linenr_T lnum;
672 int i;
673 int all_invalid;
674 int valid;
675#ifdef FEAT_FOLDING
676 long fold_count;
677#endif
678
679 /* Check if wp->w_lines[].wl_size is invalid */
680 all_invalid = (!redrawing()
681 || wp->w_lines_valid == 0
682 || wp->w_lines[0].wl_lnum > wp->w_topline);
683 i = 0;
684 wp->w_cline_row = 0;
685 for (lnum = wp->w_topline; lnum < wp->w_cursor.lnum; ++i)
686 {
687 valid = FALSE;
688 if (!all_invalid && i < wp->w_lines_valid)
689 {
690 if (wp->w_lines[i].wl_lnum < lnum || !wp->w_lines[i].wl_valid)
691 continue; /* skip changed or deleted lines */
692 if (wp->w_lines[i].wl_lnum == lnum)
693 {
694#ifdef FEAT_FOLDING
695 /* Check for newly inserted lines below this row, in which
696 * case we need to check for folded lines. */
697 if (!wp->w_buffer->b_mod_set
698 || wp->w_lines[i].wl_lastlnum < wp->w_cursor.lnum
699 || wp->w_buffer->b_mod_top
700 > wp->w_lines[i].wl_lastlnum + 1)
701#endif
702 valid = TRUE;
703 }
704 else if (wp->w_lines[i].wl_lnum > lnum)
705 --i; /* hold at inserted lines */
706 }
707 if (valid
708#ifdef FEAT_DIFF
709 && (lnum != wp->w_topline || !wp->w_p_diff)
710#endif
711 )
712 {
713#ifdef FEAT_FOLDING
714 lnum = wp->w_lines[i].wl_lastlnum + 1;
715 /* Cursor inside folded lines, don't count this row */
716 if (lnum > wp->w_cursor.lnum)
717 break;
718#else
719 ++lnum;
720#endif
721 wp->w_cline_row += wp->w_lines[i].wl_size;
722 }
723 else
724 {
725#ifdef FEAT_FOLDING
726 fold_count = foldedCount(wp, lnum, NULL);
727 if (fold_count)
728 {
729 lnum += fold_count;
730 if (lnum > wp->w_cursor.lnum)
731 break;
732 ++wp->w_cline_row;
733 }
734 else
735#endif
736#ifdef FEAT_DIFF
737 if (lnum == wp->w_topline)
738 wp->w_cline_row += plines_win_nofill(wp, lnum++, TRUE)
739 + wp->w_topfill;
740 else
741#endif
742 wp->w_cline_row += plines_win(wp, lnum++, TRUE);
743 }
744 }
745
746 check_cursor_moved(wp);
747 if (!(wp->w_valid & VALID_CHEIGHT))
748 {
749 if (all_invalid
750 || i == wp->w_lines_valid
751 || (i < wp->w_lines_valid
752 && (!wp->w_lines[i].wl_valid
753 || wp->w_lines[i].wl_lnum != wp->w_cursor.lnum)))
754 {
755#ifdef FEAT_DIFF
756 if (wp->w_cursor.lnum == wp->w_topline)
757 wp->w_cline_height = plines_win_nofill(wp, wp->w_cursor.lnum,
758 TRUE) + wp->w_topfill;
759 else
760#endif
761 wp->w_cline_height = plines_win(wp, wp->w_cursor.lnum, TRUE);
762#ifdef FEAT_FOLDING
763 wp->w_cline_folded = hasFoldingWin(wp, wp->w_cursor.lnum,
764 NULL, NULL, TRUE, NULL);
765#endif
766 }
767 else if (i > wp->w_lines_valid)
768 {
769 /* a line that is too long to fit on the last screen line */
770 wp->w_cline_height = 0;
771#ifdef FEAT_FOLDING
772 wp->w_cline_folded = hasFoldingWin(wp, wp->w_cursor.lnum,
773 NULL, NULL, TRUE, NULL);
774#endif
775 }
776 else
777 {
778 wp->w_cline_height = wp->w_lines[i].wl_size;
779#ifdef FEAT_FOLDING
780 wp->w_cline_folded = wp->w_lines[i].wl_folded;
781#endif
782 }
783 }
784
Bram Moolenaar3d6db142014-03-28 21:49:32 +0100785 redraw_for_cursorline(curwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000786 wp->w_valid |= VALID_CROW|VALID_CHEIGHT;
787
Bram Moolenaar071d4272004-06-13 20:20:40 +0000788}
789
790/*
791 * Validate curwin->w_virtcol only.
792 */
793 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100794validate_virtcol(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000795{
796 validate_virtcol_win(curwin);
797}
798
799/*
800 * Validate wp->w_virtcol only.
801 */
802 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100803validate_virtcol_win(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000804{
805 check_cursor_moved(wp);
806 if (!(wp->w_valid & VALID_VIRTCOL))
807 {
808 getvvcol(wp, &wp->w_cursor, NULL, &(wp->w_virtcol), NULL);
809 wp->w_valid |= VALID_VIRTCOL;
Bram Moolenaar2b48ad52006-03-12 21:56:11 +0000810#ifdef FEAT_SYN_HL
Bram Moolenaar019ff682006-03-13 22:10:45 +0000811 if (wp->w_p_cuc
812# ifdef FEAT_INS_EXPAND
813 && !pum_visible()
814# endif
815 )
Bram Moolenaar2b48ad52006-03-12 21:56:11 +0000816 redraw_win_later(wp, SOME_VALID);
817#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000818 }
819}
820
821/*
822 * Validate curwin->w_cline_height only.
823 */
824 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100825validate_cheight(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000826{
827 check_cursor_moved(curwin);
828 if (!(curwin->w_valid & VALID_CHEIGHT))
829 {
830#ifdef FEAT_DIFF
831 if (curwin->w_cursor.lnum == curwin->w_topline)
832 curwin->w_cline_height = plines_nofill(curwin->w_cursor.lnum)
833 + curwin->w_topfill;
834 else
835#endif
836 curwin->w_cline_height = plines(curwin->w_cursor.lnum);
837#ifdef FEAT_FOLDING
838 curwin->w_cline_folded = hasFolding(curwin->w_cursor.lnum, NULL, NULL);
839#endif
840 curwin->w_valid |= VALID_CHEIGHT;
841 }
842}
843
844/*
Bram Moolenaarc236c162008-07-13 17:41:49 +0000845 * Validate w_wcol and w_virtcol only.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000846 */
847 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100848validate_cursor_col(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000849{
850 colnr_T off;
851 colnr_T col;
Bram Moolenaar6427c602010-02-03 17:43:07 +0100852 int width;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000853
854 validate_virtcol();
855 if (!(curwin->w_valid & VALID_WCOL))
856 {
857 col = curwin->w_virtcol;
858 off = curwin_col_off();
859 col += off;
Bram Moolenaar6427c602010-02-03 17:43:07 +0100860 width = W_WIDTH(curwin) - off + curwin_col_off2();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000861
862 /* long line wrapping, adjust curwin->w_wrow */
Bram Moolenaarc236c162008-07-13 17:41:49 +0000863 if (curwin->w_p_wrap
864 && col >= (colnr_T)W_WIDTH(curwin)
Bram Moolenaar6427c602010-02-03 17:43:07 +0100865 && width > 0)
866 /* use same formula as what is used in curs_columns() */
867 col -= ((col - W_WIDTH(curwin)) / width + 1) * width;
Bram Moolenaarc236c162008-07-13 17:41:49 +0000868 if (col > (int)curwin->w_leftcol)
869 col -= curwin->w_leftcol;
870 else
871 col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000872 curwin->w_wcol = col;
Bram Moolenaarc236c162008-07-13 17:41:49 +0000873
Bram Moolenaar071d4272004-06-13 20:20:40 +0000874 curwin->w_valid |= VALID_WCOL;
875 }
876}
877
878/*
Bram Moolenaar64486672010-05-16 15:46:46 +0200879 * Compute offset of a window, occupied by absolute or relative line number,
880 * fold column and sign column (these don't move when scrolling horizontally).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000881 */
882 int
Bram Moolenaar9b578142016-01-30 19:39:49 +0100883win_col_off(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000884{
Bram Moolenaar64486672010-05-16 15:46:46 +0200885 return (((wp->w_p_nu || wp->w_p_rnu) ? number_width(wp) + 1 : 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000886#ifdef FEAT_CMDWIN
887 + (cmdwin_type == 0 || wp != curwin ? 0 : 1)
888#endif
889#ifdef FEAT_FOLDING
890 + wp->w_p_fdc
891#endif
892#ifdef FEAT_SIGNS
Bram Moolenaar95ec9d62016-08-12 18:29:59 +0200893 + (signcolumn_on(wp) ? 2 : 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000894#endif
895 );
896}
897
898 int
Bram Moolenaar9b578142016-01-30 19:39:49 +0100899curwin_col_off(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000900{
901 return win_col_off(curwin);
902}
903
904/*
905 * Return the difference in column offset for the second screen line of a
Bram Moolenaar64486672010-05-16 15:46:46 +0200906 * wrapped line. It's 8 if 'number' or 'relativenumber' is on and 'n' is in
907 * 'cpoptions'.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000908 */
909 int
Bram Moolenaar9b578142016-01-30 19:39:49 +0100910win_col_off2(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000911{
Bram Moolenaar64486672010-05-16 15:46:46 +0200912 if ((wp->w_p_nu || wp->w_p_rnu) && vim_strchr(p_cpo, CPO_NUMCOL) != NULL)
Bram Moolenaar592e0a22004-07-03 16:05:59 +0000913 return number_width(wp) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000914 return 0;
915}
916
917 int
Bram Moolenaar9b578142016-01-30 19:39:49 +0100918curwin_col_off2(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000919{
920 return win_col_off2(curwin);
921}
922
923/*
924 * compute curwin->w_wcol and curwin->w_virtcol.
925 * Also updates curwin->w_wrow and curwin->w_cline_row.
926 * Also updates curwin->w_leftcol.
927 */
928 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100929curs_columns(
930 int may_scroll) /* when TRUE, may scroll horizontally */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000931{
932 int diff;
933 int extra; /* offset for first screen line */
934 int off_left, off_right;
935 int n;
936 int p_lines;
937 int width = 0;
938 int textwidth;
939 int new_leftcol;
940 colnr_T startcol;
941 colnr_T endcol;
942 colnr_T prev_skipcol;
943
944 /*
945 * First make sure that w_topline is valid (after moving the cursor).
946 */
947 update_topline();
948
949 /*
950 * Next make sure that w_cline_row is valid.
951 */
952 if (!(curwin->w_valid & VALID_CROW))
Bram Moolenaar3f9be972014-12-13 21:09:57 +0100953 curs_rows(curwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000954
955 /*
956 * Compute the number of virtual columns.
957 */
958#ifdef FEAT_FOLDING
959 if (curwin->w_cline_folded)
960 /* In a folded line the cursor is always in the first column */
961 startcol = curwin->w_virtcol = endcol = curwin->w_leftcol;
962 else
963#endif
964 getvvcol(curwin, &curwin->w_cursor,
965 &startcol, &(curwin->w_virtcol), &endcol);
966
967 /* remove '$' from change command when cursor moves onto it */
968 if (startcol > dollar_vcol)
Bram Moolenaar76b9b362012-02-04 23:35:00 +0100969 dollar_vcol = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000970
971 extra = curwin_col_off();
972 curwin->w_wcol = curwin->w_virtcol + extra;
973 endcol += extra;
974
975 /*
976 * Now compute w_wrow, counting screen lines from w_cline_row.
977 */
978 curwin->w_wrow = curwin->w_cline_row;
979
980 textwidth = W_WIDTH(curwin) - extra;
981 if (textwidth <= 0)
982 {
983 /* No room for text, put cursor in last char of window. */
984 curwin->w_wcol = W_WIDTH(curwin) - 1;
985 curwin->w_wrow = curwin->w_height - 1;
986 }
987 else if (curwin->w_p_wrap
Bram Moolenaar44a2f922016-03-19 22:11:51 +0100988#ifdef FEAT_WINDOWS
Bram Moolenaar071d4272004-06-13 20:20:40 +0000989 && curwin->w_width != 0
990#endif
991 )
992 {
993 width = textwidth + curwin_col_off2();
994
995 /* long line wrapping, adjust curwin->w_wrow */
996 if (curwin->w_wcol >= W_WIDTH(curwin))
997 {
Bram Moolenaar6427c602010-02-03 17:43:07 +0100998 /* this same formula is used in validate_cursor_col() */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000999 n = (curwin->w_wcol - W_WIDTH(curwin)) / width + 1;
1000 curwin->w_wcol -= n * width;
1001 curwin->w_wrow += n;
1002
1003#ifdef FEAT_LINEBREAK
1004 /* When cursor wraps to first char of next line in Insert
1005 * mode, the 'showbreak' string isn't shown, backup to first
1006 * column */
1007 if (*p_sbr && *ml_get_cursor() == NUL
1008 && curwin->w_wcol == (int)vim_strsize(p_sbr))
1009 curwin->w_wcol = 0;
1010#endif
1011 }
1012 }
1013
1014 /* No line wrapping: compute curwin->w_leftcol if scrolling is on and line
1015 * is not folded.
1016 * If scrolling is off, curwin->w_leftcol is assumed to be 0 */
Bram Moolenaar70b2a562012-01-10 22:26:17 +01001017 else if (may_scroll
Bram Moolenaar071d4272004-06-13 20:20:40 +00001018#ifdef FEAT_FOLDING
1019 && !curwin->w_cline_folded
1020#endif
1021 )
1022 {
1023 /*
1024 * If Cursor is left of the screen, scroll rightwards.
1025 * If Cursor is right of the screen, scroll leftwards
1026 * If we get closer to the edge than 'sidescrolloff', scroll a little
1027 * extra
1028 */
1029 off_left = (int)startcol - (int)curwin->w_leftcol - p_siso;
1030 off_right = (int)endcol - (int)(curwin->w_leftcol + W_WIDTH(curwin)
1031 - p_siso) + 1;
1032 if (off_left < 0 || off_right > 0)
1033 {
1034 if (off_left < 0)
1035 diff = -off_left;
1036 else
1037 diff = off_right;
1038
1039 /* When far off or not enough room on either side, put cursor in
1040 * middle of window. */
1041 if (p_ss == 0 || diff >= textwidth / 2 || off_right >= off_left)
1042 new_leftcol = curwin->w_wcol - extra - textwidth / 2;
1043 else
1044 {
1045 if (diff < p_ss)
1046 diff = p_ss;
1047 if (off_left < 0)
1048 new_leftcol = curwin->w_leftcol - diff;
1049 else
1050 new_leftcol = curwin->w_leftcol + diff;
1051 }
1052 if (new_leftcol < 0)
1053 new_leftcol = 0;
1054 if (new_leftcol != (int)curwin->w_leftcol)
1055 {
1056 curwin->w_leftcol = new_leftcol;
1057 /* screen has to be redrawn with new curwin->w_leftcol */
1058 redraw_later(NOT_VALID);
1059 }
1060 }
1061 curwin->w_wcol -= curwin->w_leftcol;
1062 }
1063 else if (curwin->w_wcol > (int)curwin->w_leftcol)
1064 curwin->w_wcol -= curwin->w_leftcol;
1065 else
1066 curwin->w_wcol = 0;
1067
1068#ifdef FEAT_DIFF
1069 /* Skip over filler lines. At the top use w_topfill, there
1070 * may be some filler lines above the window. */
1071 if (curwin->w_cursor.lnum == curwin->w_topline)
1072 curwin->w_wrow += curwin->w_topfill;
1073 else
1074 curwin->w_wrow += diff_check_fill(curwin, curwin->w_cursor.lnum);
1075#endif
1076
1077 prev_skipcol = curwin->w_skipcol;
1078
1079 p_lines = 0;
1080 if ((curwin->w_wrow >= curwin->w_height
1081 || ((prev_skipcol > 0
1082 || curwin->w_wrow + p_so >= curwin->w_height)
1083 && (p_lines =
1084#ifdef FEAT_DIFF
1085 plines_win_nofill
1086#else
1087 plines_win
1088#endif
1089 (curwin, curwin->w_cursor.lnum, FALSE))
1090 - 1 >= curwin->w_height))
1091 && curwin->w_height != 0
1092 && curwin->w_cursor.lnum == curwin->w_topline
1093 && width > 0
Bram Moolenaar44a2f922016-03-19 22:11:51 +01001094#ifdef FEAT_WINDOWS
Bram Moolenaar071d4272004-06-13 20:20:40 +00001095 && curwin->w_width != 0
1096#endif
1097 )
1098 {
1099 /* Cursor past end of screen. Happens with a single line that does
1100 * not fit on screen. Find a skipcol to show the text around the
1101 * cursor. Avoid scrolling all the time. compute value of "extra":
1102 * 1: Less than "p_so" lines above
1103 * 2: Less than "p_so" lines below
1104 * 3: both of them */
1105 extra = 0;
1106 if (curwin->w_skipcol + p_so * width > curwin->w_virtcol)
1107 extra = 1;
1108 /* Compute last display line of the buffer line that we want at the
1109 * bottom of the window. */
1110 if (p_lines == 0)
1111 p_lines = plines_win(curwin, curwin->w_cursor.lnum, FALSE);
1112 --p_lines;
1113 if (p_lines > curwin->w_wrow + p_so)
1114 n = curwin->w_wrow + p_so;
1115 else
1116 n = p_lines;
1117 if ((colnr_T)n >= curwin->w_height + curwin->w_skipcol / width)
1118 extra += 2;
1119
1120 if (extra == 3 || p_lines < p_so * 2)
1121 {
1122 /* not enough room for 'scrolloff', put cursor in the middle */
1123 n = curwin->w_virtcol / width;
1124 if (n > curwin->w_height / 2)
1125 n -= curwin->w_height / 2;
1126 else
1127 n = 0;
1128 /* don't skip more than necessary */
1129 if (n > p_lines - curwin->w_height + 1)
1130 n = p_lines - curwin->w_height + 1;
1131 curwin->w_skipcol = n * width;
1132 }
1133 else if (extra == 1)
1134 {
1135 /* less then 'scrolloff' lines above, decrease skipcol */
1136 extra = (curwin->w_skipcol + p_so * width - curwin->w_virtcol
1137 + width - 1) / width;
1138 if (extra > 0)
1139 {
1140 if ((colnr_T)(extra * width) > curwin->w_skipcol)
1141 extra = curwin->w_skipcol / width;
1142 curwin->w_skipcol -= extra * width;
1143 }
1144 }
1145 else if (extra == 2)
1146 {
1147 /* less then 'scrolloff' lines below, increase skipcol */
1148 endcol = (n - curwin->w_height + 1) * width;
1149 while (endcol > curwin->w_virtcol)
1150 endcol -= width;
1151 if (endcol > curwin->w_skipcol)
1152 curwin->w_skipcol = endcol;
1153 }
1154
1155 curwin->w_wrow -= curwin->w_skipcol / width;
1156 if (curwin->w_wrow >= curwin->w_height)
1157 {
1158 /* small window, make sure cursor is in it */
1159 extra = curwin->w_wrow - curwin->w_height + 1;
1160 curwin->w_skipcol += extra * width;
1161 curwin->w_wrow -= extra;
1162 }
1163
1164 extra = ((int)prev_skipcol - (int)curwin->w_skipcol) / width;
1165 if (extra > 0)
1166 win_ins_lines(curwin, 0, extra, FALSE, FALSE);
1167 else if (extra < 0)
1168 win_del_lines(curwin, 0, -extra, FALSE, FALSE);
1169 }
1170 else
1171 curwin->w_skipcol = 0;
1172 if (prev_skipcol != curwin->w_skipcol)
1173 redraw_later(NOT_VALID);
1174
Bram Moolenaar2b48ad52006-03-12 21:56:11 +00001175#ifdef FEAT_SYN_HL
Bram Moolenaarb6798752014-03-27 12:11:48 +01001176 /* Redraw when w_virtcol changes and 'cursorcolumn' is set */
1177 if (curwin->w_p_cuc && (curwin->w_valid & VALID_VIRTCOL) == 0
Bram Moolenaar64486672010-05-16 15:46:46 +02001178# ifdef FEAT_INS_EXPAND
Bram Moolenaarb6798752014-03-27 12:11:48 +01001179 && !pum_visible()
Bram Moolenaar64486672010-05-16 15:46:46 +02001180# endif
Bram Moolenaarb6798752014-03-27 12:11:48 +01001181 )
1182 redraw_later(SOME_VALID);
1183#endif
Bram Moolenaar2b48ad52006-03-12 21:56:11 +00001184
Bram Moolenaar071d4272004-06-13 20:20:40 +00001185 curwin->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL;
1186}
1187
1188/*
1189 * Scroll the current window down by "line_count" logical lines. "CTRL-Y"
1190 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001191 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001192scrolldown(
1193 long line_count,
1194 int byfold UNUSED) /* TRUE: count a closed fold as one line */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001195{
1196 long done = 0; /* total # of physical lines done */
1197 int wrow;
1198 int moved = FALSE;
1199
1200#ifdef FEAT_FOLDING
1201 linenr_T first;
1202
1203 /* Make sure w_topline is at the first of a sequence of folded lines. */
1204 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
1205#endif
1206 validate_cursor(); /* w_wrow needs to be valid */
1207 while (line_count-- > 0)
1208 {
1209#ifdef FEAT_DIFF
Bram Moolenaarfa316dd2009-11-03 15:23:14 +00001210 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline)
1211 && curwin->w_topfill < curwin->w_height - 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001212 {
1213 ++curwin->w_topfill;
1214 ++done;
1215 }
1216 else
1217#endif
1218 {
1219 if (curwin->w_topline == 1)
1220 break;
1221 --curwin->w_topline;
1222#ifdef FEAT_DIFF
1223 curwin->w_topfill = 0;
1224#endif
1225#ifdef FEAT_FOLDING
1226 /* A sequence of folded lines only counts for one logical line */
1227 if (hasFolding(curwin->w_topline, &first, NULL))
1228 {
1229 ++done;
1230 if (!byfold)
1231 line_count -= curwin->w_topline - first - 1;
1232 curwin->w_botline -= curwin->w_topline - first;
1233 curwin->w_topline = first;
1234 }
1235 else
1236#endif
Bram Moolenaar43335ea2015-09-09 20:59:37 +02001237 done += PLINES_NOFILL(curwin->w_topline);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001238 }
1239 --curwin->w_botline; /* approximate w_botline */
1240 invalidate_botline();
1241 }
1242 curwin->w_wrow += done; /* keep w_wrow updated */
1243 curwin->w_cline_row += done; /* keep w_cline_row updated */
1244
1245#ifdef FEAT_DIFF
1246 if (curwin->w_cursor.lnum == curwin->w_topline)
1247 curwin->w_cline_row = 0;
1248 check_topfill(curwin, TRUE);
1249#endif
1250
1251 /*
1252 * Compute the row number of the last row of the cursor line
1253 * and move the cursor onto the displayed part of the window.
1254 */
1255 wrow = curwin->w_wrow;
1256 if (curwin->w_p_wrap
Bram Moolenaar44a2f922016-03-19 22:11:51 +01001257#ifdef FEAT_WINDOWS
Bram Moolenaar071d4272004-06-13 20:20:40 +00001258 && curwin->w_width != 0
1259#endif
1260 )
1261 {
1262 validate_virtcol();
1263 validate_cheight();
1264 wrow += curwin->w_cline_height - 1 -
1265 curwin->w_virtcol / W_WIDTH(curwin);
1266 }
1267 while (wrow >= curwin->w_height && curwin->w_cursor.lnum > 1)
1268 {
1269#ifdef FEAT_FOLDING
1270 if (hasFolding(curwin->w_cursor.lnum, &first, NULL))
1271 {
1272 --wrow;
1273 if (first == 1)
1274 curwin->w_cursor.lnum = 1;
1275 else
1276 curwin->w_cursor.lnum = first - 1;
1277 }
1278 else
1279#endif
1280 wrow -= plines(curwin->w_cursor.lnum--);
1281 curwin->w_valid &=
1282 ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL);
1283 moved = TRUE;
1284 }
1285 if (moved)
1286 {
1287#ifdef FEAT_FOLDING
1288 /* Move cursor to first line of closed fold. */
1289 foldAdjustCursor();
1290#endif
1291 coladvance(curwin->w_curswant);
1292 }
1293}
1294
1295/*
1296 * Scroll the current window up by "line_count" logical lines. "CTRL-E"
1297 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001298 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001299scrollup(
1300 long line_count,
1301 int byfold UNUSED) /* TRUE: count a closed fold as one line */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001302{
1303#if defined(FEAT_FOLDING) || defined(FEAT_DIFF)
1304 linenr_T lnum;
1305
1306 if (
1307# ifdef FEAT_FOLDING
1308 (byfold && hasAnyFolding(curwin))
1309# ifdef FEAT_DIFF
1310 ||
1311# endif
1312# endif
1313# ifdef FEAT_DIFF
1314 curwin->w_p_diff
1315# endif
1316 )
1317 {
1318 /* count each sequence of folded lines as one logical line */
1319 lnum = curwin->w_topline;
1320 while (line_count--)
1321 {
1322# ifdef FEAT_DIFF
1323 if (curwin->w_topfill > 0)
1324 --curwin->w_topfill;
1325 else
1326# endif
1327 {
1328# ifdef FEAT_FOLDING
1329 if (byfold)
1330 (void)hasFolding(lnum, NULL, &lnum);
1331# endif
1332 if (lnum >= curbuf->b_ml.ml_line_count)
1333 break;
1334 ++lnum;
1335# ifdef FEAT_DIFF
1336 curwin->w_topfill = diff_check_fill(curwin, lnum);
1337# endif
1338 }
1339 }
1340 /* approximate w_botline */
1341 curwin->w_botline += lnum - curwin->w_topline;
1342 curwin->w_topline = lnum;
1343 }
1344 else
1345#endif
1346 {
1347 curwin->w_topline += line_count;
1348 curwin->w_botline += line_count; /* approximate w_botline */
1349 }
1350
1351 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1352 curwin->w_topline = curbuf->b_ml.ml_line_count;
1353 if (curwin->w_botline > curbuf->b_ml.ml_line_count + 1)
1354 curwin->w_botline = curbuf->b_ml.ml_line_count + 1;
1355
1356#ifdef FEAT_DIFF
1357 check_topfill(curwin, FALSE);
1358#endif
1359
1360#ifdef FEAT_FOLDING
1361 if (hasAnyFolding(curwin))
1362 /* Make sure w_topline is at the first of a sequence of folded lines. */
1363 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
1364#endif
1365
1366 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
1367 if (curwin->w_cursor.lnum < curwin->w_topline)
1368 {
1369 curwin->w_cursor.lnum = curwin->w_topline;
1370 curwin->w_valid &=
1371 ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL);
1372 coladvance(curwin->w_curswant);
1373 }
1374}
1375
1376#ifdef FEAT_DIFF
1377/*
1378 * Don't end up with too many filler lines in the window.
1379 */
1380 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001381check_topfill(
1382 win_T *wp,
1383 int down) /* when TRUE scroll down when not enough space */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001384{
1385 int n;
1386
1387 if (wp->w_topfill > 0)
1388 {
1389 n = plines_win_nofill(wp, wp->w_topline, TRUE);
1390 if (wp->w_topfill + n > wp->w_height)
1391 {
1392 if (down && wp->w_topline > 1)
1393 {
1394 --wp->w_topline;
1395 wp->w_topfill = 0;
1396 }
1397 else
1398 {
1399 wp->w_topfill = wp->w_height - n;
1400 if (wp->w_topfill < 0)
1401 wp->w_topfill = 0;
1402 }
1403 }
1404 }
1405}
1406
1407/*
1408 * Use as many filler lines as possible for w_topline. Make sure w_topline
1409 * is still visible.
1410 */
1411 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001412max_topfill(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001413{
1414 int n;
1415
1416 n = plines_nofill(curwin->w_topline);
1417 if (n >= curwin->w_height)
1418 curwin->w_topfill = 0;
1419 else
1420 {
1421 curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline);
1422 if (curwin->w_topfill + n > curwin->w_height)
1423 curwin->w_topfill = curwin->w_height - n;
1424 }
1425}
1426#endif
1427
1428#if defined(FEAT_INS_EXPAND) || defined(PROTO)
1429/*
1430 * Scroll the screen one line down, but don't do it if it would move the
1431 * cursor off the screen.
1432 */
1433 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001434scrolldown_clamp(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001435{
1436 int end_row;
1437#ifdef FEAT_DIFF
1438 int can_fill = (curwin->w_topfill
1439 < diff_check_fill(curwin, curwin->w_topline));
1440#endif
1441
1442 if (curwin->w_topline <= 1
1443#ifdef FEAT_DIFF
1444 && !can_fill
1445#endif
1446 )
1447 return;
1448
1449 validate_cursor(); /* w_wrow needs to be valid */
1450
1451 /*
1452 * Compute the row number of the last row of the cursor line
1453 * and make sure it doesn't go off the screen. Make sure the cursor
1454 * doesn't go past 'scrolloff' lines from the screen end.
1455 */
1456 end_row = curwin->w_wrow;
1457#ifdef FEAT_DIFF
1458 if (can_fill)
1459 ++end_row;
1460 else
1461 end_row += plines_nofill(curwin->w_topline - 1);
1462#else
1463 end_row += plines(curwin->w_topline - 1);
1464#endif
1465 if (curwin->w_p_wrap
Bram Moolenaar44a2f922016-03-19 22:11:51 +01001466#ifdef FEAT_WINDOWS
Bram Moolenaar071d4272004-06-13 20:20:40 +00001467 && curwin->w_width != 0
1468#endif
1469 )
1470 {
1471 validate_cheight();
1472 validate_virtcol();
1473 end_row += curwin->w_cline_height - 1 -
1474 curwin->w_virtcol / W_WIDTH(curwin);
1475 }
1476 if (end_row < curwin->w_height - p_so)
1477 {
1478#ifdef FEAT_DIFF
1479 if (can_fill)
1480 {
1481 ++curwin->w_topfill;
1482 check_topfill(curwin, TRUE);
1483 }
1484 else
1485 {
1486 --curwin->w_topline;
1487 curwin->w_topfill = 0;
1488 }
1489#else
1490 --curwin->w_topline;
1491#endif
1492#ifdef FEAT_FOLDING
Bram Moolenaarcde88542015-08-11 19:14:00 +02001493 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001494#endif
1495 --curwin->w_botline; /* approximate w_botline */
1496 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
1497 }
1498}
1499
1500/*
1501 * Scroll the screen one line up, but don't do it if it would move the cursor
1502 * off the screen.
1503 */
1504 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001505scrollup_clamp(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001506{
1507 int start_row;
1508
1509 if (curwin->w_topline == curbuf->b_ml.ml_line_count
1510#ifdef FEAT_DIFF
1511 && curwin->w_topfill == 0
1512#endif
1513 )
1514 return;
1515
1516 validate_cursor(); /* w_wrow needs to be valid */
1517
1518 /*
1519 * Compute the row number of the first row of the cursor line
1520 * and make sure it doesn't go off the screen. Make sure the cursor
1521 * doesn't go before 'scrolloff' lines from the screen start.
1522 */
1523#ifdef FEAT_DIFF
1524 start_row = curwin->w_wrow - plines_nofill(curwin->w_topline)
1525 - curwin->w_topfill;
1526#else
1527 start_row = curwin->w_wrow - plines(curwin->w_topline);
1528#endif
1529 if (curwin->w_p_wrap
Bram Moolenaar44a2f922016-03-19 22:11:51 +01001530#ifdef FEAT_WINDOWS
Bram Moolenaar071d4272004-06-13 20:20:40 +00001531 && curwin->w_width != 0
1532#endif
1533 )
1534 {
1535 validate_virtcol();
1536 start_row -= curwin->w_virtcol / W_WIDTH(curwin);
1537 }
1538 if (start_row >= p_so)
1539 {
1540#ifdef FEAT_DIFF
1541 if (curwin->w_topfill > 0)
1542 --curwin->w_topfill;
1543 else
1544#endif
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00001545 {
1546#ifdef FEAT_FOLDING
1547 (void)hasFolding(curwin->w_topline, NULL, &curwin->w_topline);
1548#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001549 ++curwin->w_topline;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00001550 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001551 ++curwin->w_botline; /* approximate w_botline */
1552 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
1553 }
1554}
1555#endif /* FEAT_INS_EXPAND */
1556
1557/*
1558 * Add one line above "lp->lnum". This can be a filler line, a closed fold or
1559 * a (wrapped) text line. Uses and sets "lp->fill".
1560 * Returns the height of the added line in "lp->height".
Bram Moolenaarbacd9da2010-02-17 18:20:37 +01001561 * Lines above the first one are incredibly high: MAXCOL.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001562 */
1563 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001564topline_back(lineoff_T *lp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001565{
1566#ifdef FEAT_DIFF
1567 if (lp->fill < diff_check_fill(curwin, lp->lnum))
1568 {
1569 /* Add a filler line. */
1570 ++lp->fill;
1571 lp->height = 1;
1572 }
1573 else
1574#endif
1575 {
1576 --lp->lnum;
1577#ifdef FEAT_DIFF
1578 lp->fill = 0;
1579#endif
1580 if (lp->lnum < 1)
1581 lp->height = MAXCOL;
1582 else
1583#ifdef FEAT_FOLDING
1584 if (hasFolding(lp->lnum, &lp->lnum, NULL))
1585 /* Add a closed fold */
1586 lp->height = 1;
1587 else
1588#endif
Bram Moolenaar43335ea2015-09-09 20:59:37 +02001589 lp->height = PLINES_NOFILL(lp->lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001590 }
1591}
1592
1593/*
1594 * Add one line below "lp->lnum". This can be a filler line, a closed fold or
1595 * a (wrapped) text line. Uses and sets "lp->fill".
1596 * Returns the height of the added line in "lp->height".
1597 * Lines below the last one are incredibly high.
1598 */
1599 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001600botline_forw(lineoff_T *lp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001601{
1602#ifdef FEAT_DIFF
1603 if (lp->fill < diff_check_fill(curwin, lp->lnum + 1))
1604 {
1605 /* Add a filler line. */
1606 ++lp->fill;
1607 lp->height = 1;
1608 }
1609 else
1610#endif
1611 {
1612 ++lp->lnum;
1613#ifdef FEAT_DIFF
1614 lp->fill = 0;
1615#endif
1616 if (lp->lnum > curbuf->b_ml.ml_line_count)
1617 lp->height = MAXCOL;
1618 else
1619#ifdef FEAT_FOLDING
1620 if (hasFolding(lp->lnum, NULL, &lp->lnum))
1621 /* Add a closed fold */
1622 lp->height = 1;
1623 else
1624#endif
1625 {
Bram Moolenaar43335ea2015-09-09 20:59:37 +02001626 lp->height = PLINES_NOFILL(lp->lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001627 }
1628 }
1629}
1630
1631#ifdef FEAT_DIFF
1632/*
1633 * Switch from including filler lines below lp->lnum to including filler
1634 * lines above loff.lnum + 1. This keeps pointing to the same line.
1635 * When there are no filler lines nothing changes.
1636 */
1637 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001638botline_topline(lineoff_T *lp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001639{
1640 if (lp->fill > 0)
1641 {
1642 ++lp->lnum;
1643 lp->fill = diff_check_fill(curwin, lp->lnum) - lp->fill + 1;
1644 }
1645}
1646
1647/*
1648 * Switch from including filler lines above lp->lnum to including filler
1649 * lines below loff.lnum - 1. This keeps pointing to the same line.
1650 * When there are no filler lines nothing changes.
1651 */
1652 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001653topline_botline(lineoff_T *lp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001654{
1655 if (lp->fill > 0)
1656 {
1657 lp->fill = diff_check_fill(curwin, lp->lnum) - lp->fill + 1;
1658 --lp->lnum;
1659 }
1660}
1661#endif
1662
1663/*
1664 * Recompute topline to put the cursor at the top of the window.
1665 * Scroll at least "min_scroll" lines.
1666 * If "always" is TRUE, always set topline (for "zt").
1667 */
1668 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001669scroll_cursor_top(int min_scroll, int always)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001670{
1671 int scrolled = 0;
1672 int extra = 0;
1673 int used;
1674 int i;
1675 linenr_T top; /* just above displayed lines */
1676 linenr_T bot; /* just below displayed lines */
1677 linenr_T old_topline = curwin->w_topline;
1678#ifdef FEAT_DIFF
1679 linenr_T old_topfill = curwin->w_topfill;
1680#endif
1681 linenr_T new_topline;
1682 int off = p_so;
1683
1684#ifdef FEAT_MOUSE
1685 if (mouse_dragging > 0)
1686 off = mouse_dragging - 1;
1687#endif
1688
1689 /*
1690 * Decrease topline until:
1691 * - it has become 1
1692 * - (part of) the cursor line is moved off the screen or
1693 * - moved at least 'scrolljump' lines and
1694 * - at least 'scrolloff' lines above and below the cursor
1695 */
1696 validate_cheight();
Bram Moolenaarcf619da2015-09-01 20:53:24 +02001697 used = curwin->w_cline_height; /* includes filler lines above */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001698 if (curwin->w_cursor.lnum < curwin->w_topline)
1699 scrolled = used;
1700
1701#ifdef FEAT_FOLDING
1702 if (hasFolding(curwin->w_cursor.lnum, &top, &bot))
1703 {
1704 --top;
1705 ++bot;
1706 }
1707 else
1708#endif
1709 {
1710 top = curwin->w_cursor.lnum - 1;
1711 bot = curwin->w_cursor.lnum + 1;
1712 }
1713 new_topline = top + 1;
1714
1715#ifdef FEAT_DIFF
Bram Moolenaara09a2c52015-09-08 17:31:59 +02001716 /* "used" already contains the number of filler lines above, don't add it
Bram Moolenaarcf619da2015-09-01 20:53:24 +02001717 * again.
Bram Moolenaara09a2c52015-09-08 17:31:59 +02001718 * Hide filler lines above cursor line by adding them to "extra". */
1719 extra += diff_check_fill(curwin, curwin->w_cursor.lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001720#endif
1721
1722 /*
1723 * Check if the lines from "top" to "bot" fit in the window. If they do,
1724 * set new_topline and advance "top" and "bot" to include more lines.
1725 */
1726 while (top > 0)
1727 {
1728#ifdef FEAT_FOLDING
1729 if (hasFolding(top, &top, NULL))
1730 /* count one logical line for a sequence of folded lines */
1731 i = 1;
1732 else
1733#endif
Bram Moolenaar43335ea2015-09-09 20:59:37 +02001734 i = PLINES_NOFILL(top);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001735 used += i;
1736 if (extra + i <= off && bot < curbuf->b_ml.ml_line_count)
1737 {
1738#ifdef FEAT_FOLDING
1739 if (hasFolding(bot, NULL, &bot))
1740 /* count one logical line for a sequence of folded lines */
1741 ++used;
1742 else
1743#endif
1744 used += plines(bot);
1745 }
1746 if (used > curwin->w_height)
1747 break;
1748 if (top < curwin->w_topline)
1749 scrolled += i;
1750
1751 /*
1752 * If scrolling is needed, scroll at least 'sj' lines.
1753 */
1754 if ((new_topline >= curwin->w_topline || scrolled > min_scroll)
1755 && extra >= off)
1756 break;
1757
1758 extra += i;
1759 new_topline = top;
1760 --top;
1761 ++bot;
1762 }
1763
1764 /*
1765 * If we don't have enough space, put cursor in the middle.
1766 * This makes sure we get the same position when using "k" and "j"
1767 * in a small window.
1768 */
1769 if (used > curwin->w_height)
1770 scroll_cursor_halfway(FALSE);
1771 else
1772 {
1773 /*
1774 * If "always" is FALSE, only adjust topline to a lower value, higher
1775 * value may happen with wrapping lines
1776 */
1777 if (new_topline < curwin->w_topline || always)
1778 curwin->w_topline = new_topline;
1779 if (curwin->w_topline > curwin->w_cursor.lnum)
1780 curwin->w_topline = curwin->w_cursor.lnum;
1781#ifdef FEAT_DIFF
1782 curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline);
1783 if (curwin->w_topfill > 0 && extra > off)
1784 {
1785 curwin->w_topfill -= extra - off;
1786 if (curwin->w_topfill < 0)
1787 curwin->w_topfill = 0;
1788 }
1789 check_topfill(curwin, FALSE);
1790#endif
1791 if (curwin->w_topline != old_topline
1792#ifdef FEAT_DIFF
1793 || curwin->w_topfill != old_topfill
1794#endif
1795 )
1796 curwin->w_valid &=
1797 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
1798 curwin->w_valid |= VALID_TOPLINE;
1799 }
1800}
1801
1802/*
1803 * Set w_empty_rows and w_filler_rows for window "wp", having used up "used"
1804 * screen lines for text lines.
1805 */
1806 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001807set_empty_rows(win_T *wp, int used)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001808{
1809#ifdef FEAT_DIFF
1810 wp->w_filler_rows = 0;
1811#endif
1812 if (used == 0)
1813 wp->w_empty_rows = 0; /* single line that doesn't fit */
1814 else
1815 {
1816 wp->w_empty_rows = wp->w_height - used;
1817#ifdef FEAT_DIFF
1818 if (wp->w_botline <= wp->w_buffer->b_ml.ml_line_count)
1819 {
1820 wp->w_filler_rows = diff_check_fill(wp, wp->w_botline);
1821 if (wp->w_empty_rows > wp->w_filler_rows)
1822 wp->w_empty_rows -= wp->w_filler_rows;
1823 else
1824 {
1825 wp->w_filler_rows = wp->w_empty_rows;
1826 wp->w_empty_rows = 0;
1827 }
1828 }
1829#endif
1830 }
1831}
1832
1833/*
1834 * Recompute topline to put the cursor at the bottom of the window.
1835 * Scroll at least "min_scroll" lines.
1836 * If "set_topbot" is TRUE, set topline and botline first (for "zb").
1837 * This is messy stuff!!!
1838 */
1839 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001840scroll_cursor_bot(int min_scroll, int set_topbot)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001841{
1842 int used;
1843 int scrolled = 0;
1844 int extra = 0;
1845 int i;
1846 linenr_T line_count;
1847 linenr_T old_topline = curwin->w_topline;
1848 lineoff_T loff;
1849 lineoff_T boff;
1850#ifdef FEAT_DIFF
1851 int old_topfill = curwin->w_topfill;
1852 int fill_below_window;
1853#endif
1854 linenr_T old_botline = curwin->w_botline;
1855 linenr_T old_valid = curwin->w_valid;
1856 int old_empty_rows = curwin->w_empty_rows;
1857 linenr_T cln; /* Cursor Line Number */
1858
1859 cln = curwin->w_cursor.lnum;
1860 if (set_topbot)
1861 {
1862 used = 0;
1863 curwin->w_botline = cln + 1;
1864#ifdef FEAT_DIFF
1865 loff.fill = 0;
1866#endif
1867 for (curwin->w_topline = curwin->w_botline;
1868 curwin->w_topline > 1;
1869 curwin->w_topline = loff.lnum)
1870 {
1871 loff.lnum = curwin->w_topline;
1872 topline_back(&loff);
Bram Moolenaarbacd9da2010-02-17 18:20:37 +01001873 if (loff.height == MAXCOL || used + loff.height > curwin->w_height)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001874 break;
1875 used += loff.height;
1876#ifdef FEAT_DIFF
1877 curwin->w_topfill = loff.fill;
1878#endif
1879 }
1880 set_empty_rows(curwin, used);
1881 curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
1882 if (curwin->w_topline != old_topline
1883#ifdef FEAT_DIFF
1884 || curwin->w_topfill != old_topfill
1885#endif
1886 )
1887 curwin->w_valid &= ~(VALID_WROW|VALID_CROW);
1888 }
1889 else
1890 validate_botline();
1891
1892 /* The lines of the cursor line itself are always used. */
1893#ifdef FEAT_DIFF
1894 used = plines_nofill(cln);
1895#else
1896 validate_cheight();
1897 used = curwin->w_cline_height;
1898#endif
1899
1900 /* If the cursor is below botline, we will at least scroll by the height
1901 * of the cursor line. Correct for empty lines, which are really part of
1902 * botline. */
1903 if (cln >= curwin->w_botline)
1904 {
1905 scrolled = used;
1906 if (cln == curwin->w_botline)
1907 scrolled -= curwin->w_empty_rows;
1908 }
1909
1910 /*
1911 * Stop counting lines to scroll when
1912 * - hitting start of the file
1913 * - scrolled nothing or at least 'sj' lines
1914 * - at least 'so' lines below the cursor
1915 * - lines between botline and cursor have been counted
1916 */
1917#ifdef FEAT_FOLDING
1918 if (!hasFolding(curwin->w_cursor.lnum, &loff.lnum, &boff.lnum))
1919#endif
1920 {
1921 loff.lnum = cln;
1922 boff.lnum = cln;
1923 }
1924#ifdef FEAT_DIFF
1925 loff.fill = 0;
1926 boff.fill = 0;
1927 fill_below_window = diff_check_fill(curwin, curwin->w_botline)
1928 - curwin->w_filler_rows;
1929#endif
1930
1931 while (loff.lnum > 1)
1932 {
1933 /* Stop when scrolled nothing or at least "min_scroll", found "extra"
1934 * context for 'scrolloff' and counted all lines below the window. */
1935 if ((((scrolled <= 0 || scrolled >= min_scroll)
1936 && extra >= (
1937#ifdef FEAT_MOUSE
Bram Moolenaar9964e462007-05-05 17:54:07 +00001938 mouse_dragging > 0 ? mouse_dragging - 1 :
Bram Moolenaar071d4272004-06-13 20:20:40 +00001939#endif
1940 p_so))
1941 || boff.lnum + 1 > curbuf->b_ml.ml_line_count)
1942 && loff.lnum <= curwin->w_botline
1943#ifdef FEAT_DIFF
1944 && (loff.lnum < curwin->w_botline
1945 || loff.fill >= fill_below_window)
1946#endif
1947 )
1948 break;
1949
1950 /* Add one line above */
1951 topline_back(&loff);
Bram Moolenaarbacd9da2010-02-17 18:20:37 +01001952 if (loff.height == MAXCOL)
1953 used = MAXCOL;
1954 else
1955 used += loff.height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001956 if (used > curwin->w_height)
1957 break;
1958 if (loff.lnum >= curwin->w_botline
1959#ifdef FEAT_DIFF
1960 && (loff.lnum > curwin->w_botline
1961 || loff.fill <= fill_below_window)
1962#endif
1963 )
1964 {
1965 /* Count screen lines that are below the window. */
1966 scrolled += loff.height;
1967 if (loff.lnum == curwin->w_botline
1968#ifdef FEAT_DIFF
1969 && boff.fill == 0
1970#endif
1971 )
1972 scrolled -= curwin->w_empty_rows;
1973 }
1974
1975 if (boff.lnum < curbuf->b_ml.ml_line_count)
1976 {
1977 /* Add one line below */
1978 botline_forw(&boff);
1979 used += boff.height;
1980 if (used > curwin->w_height)
1981 break;
1982 if (extra < (
1983#ifdef FEAT_MOUSE
1984 mouse_dragging > 0 ? mouse_dragging - 1 :
1985#endif
1986 p_so) || scrolled < min_scroll)
1987 {
1988 extra += boff.height;
1989 if (boff.lnum >= curwin->w_botline
1990#ifdef FEAT_DIFF
1991 || (boff.lnum + 1 == curwin->w_botline
1992 && boff.fill > curwin->w_filler_rows)
1993#endif
1994 )
1995 {
1996 /* Count screen lines that are below the window. */
1997 scrolled += boff.height;
1998 if (boff.lnum == curwin->w_botline
1999#ifdef FEAT_DIFF
2000 && boff.fill == 0
2001#endif
2002 )
2003 scrolled -= curwin->w_empty_rows;
2004 }
2005 }
2006 }
2007 }
2008
2009 /* curwin->w_empty_rows is larger, no need to scroll */
2010 if (scrolled <= 0)
2011 line_count = 0;
2012 /* more than a screenfull, don't scroll but redraw */
2013 else if (used > curwin->w_height)
2014 line_count = used;
2015 /* scroll minimal number of lines */
2016 else
2017 {
2018 line_count = 0;
2019#ifdef FEAT_DIFF
2020 boff.fill = curwin->w_topfill;
2021#endif
2022 boff.lnum = curwin->w_topline - 1;
2023 for (i = 0; i < scrolled && boff.lnum < curwin->w_botline; )
2024 {
2025 botline_forw(&boff);
2026 i += boff.height;
2027 ++line_count;
2028 }
2029 if (i < scrolled) /* below curwin->w_botline, don't scroll */
2030 line_count = 9999;
2031 }
2032
2033 /*
2034 * Scroll up if the cursor is off the bottom of the screen a bit.
2035 * Otherwise put it at 1/2 of the screen.
2036 */
2037 if (line_count >= curwin->w_height && line_count > min_scroll)
2038 scroll_cursor_halfway(FALSE);
2039 else
2040 scrollup(line_count, TRUE);
2041
2042 /*
2043 * If topline didn't change we need to restore w_botline and w_empty_rows
2044 * (we changed them).
2045 * If topline did change, update_screen() will set botline.
2046 */
2047 if (curwin->w_topline == old_topline && set_topbot)
2048 {
2049 curwin->w_botline = old_botline;
2050 curwin->w_empty_rows = old_empty_rows;
2051 curwin->w_valid = old_valid;
2052 }
2053 curwin->w_valid |= VALID_TOPLINE;
2054}
2055
2056/*
2057 * Recompute topline to put the cursor halfway the window
2058 * If "atend" is TRUE, also put it halfway at the end of the file.
2059 */
2060 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002061scroll_cursor_halfway(int atend)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002062{
2063 int above = 0;
2064 linenr_T topline;
2065#ifdef FEAT_DIFF
2066 int topfill = 0;
2067#endif
2068 int below = 0;
2069 int used;
2070 lineoff_T loff;
2071 lineoff_T boff;
Bram Moolenaarb8e23052014-02-11 18:58:09 +01002072#ifdef FEAT_DIFF
Bram Moolenaar12a0f222014-02-11 15:47:46 +01002073 linenr_T old_topline = curwin->w_topline;
Bram Moolenaarb8e23052014-02-11 18:58:09 +01002074#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002075
2076 loff.lnum = boff.lnum = curwin->w_cursor.lnum;
2077#ifdef FEAT_FOLDING
2078 (void)hasFolding(loff.lnum, &loff.lnum, &boff.lnum);
2079#endif
2080#ifdef FEAT_DIFF
2081 used = plines_nofill(loff.lnum);
2082 loff.fill = 0;
2083 boff.fill = 0;
2084#else
2085 used = plines(loff.lnum);
2086#endif
2087 topline = loff.lnum;
2088 while (topline > 1)
2089 {
2090 if (below <= above) /* add a line below the cursor first */
2091 {
2092 if (boff.lnum < curbuf->b_ml.ml_line_count)
2093 {
2094 botline_forw(&boff);
2095 used += boff.height;
2096 if (used > curwin->w_height)
2097 break;
2098 below += boff.height;
2099 }
2100 else
2101 {
2102 ++below; /* count a "~" line */
2103 if (atend)
2104 ++used;
2105 }
2106 }
2107
2108 if (below > above) /* add a line above the cursor */
2109 {
2110 topline_back(&loff);
Bram Moolenaarbacd9da2010-02-17 18:20:37 +01002111 if (loff.height == MAXCOL)
2112 used = MAXCOL;
2113 else
2114 used += loff.height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002115 if (used > curwin->w_height)
2116 break;
2117 above += loff.height;
2118 topline = loff.lnum;
2119#ifdef FEAT_DIFF
2120 topfill = loff.fill;
2121#endif
2122 }
2123 }
2124#ifdef FEAT_FOLDING
2125 if (!hasFolding(topline, &curwin->w_topline, NULL))
2126#endif
2127 curwin->w_topline = topline;
2128#ifdef FEAT_DIFF
2129 curwin->w_topfill = topfill;
Bram Moolenaar12a0f222014-02-11 15:47:46 +01002130 if (old_topline > curwin->w_topline + curwin->w_height)
2131 curwin->w_botfill = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002132 check_topfill(curwin, FALSE);
2133#endif
2134 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
2135 curwin->w_valid |= VALID_TOPLINE;
2136}
2137
2138/*
2139 * Correct the cursor position so that it is in a part of the screen at least
2140 * 'so' lines from the top and bottom, if possible.
2141 * If not possible, put it at the same position as scroll_cursor_halfway().
2142 * When called topline must be valid!
2143 */
2144 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002145cursor_correct(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002146{
2147 int above = 0; /* screen lines above topline */
2148 linenr_T topline;
2149 int below = 0; /* screen lines below botline */
2150 linenr_T botline;
2151 int above_wanted, below_wanted;
2152 linenr_T cln; /* Cursor Line Number */
2153 int max_off;
2154
2155 /*
2156 * How many lines we would like to have above/below the cursor depends on
2157 * whether the first/last line of the file is on screen.
2158 */
2159 above_wanted = p_so;
2160 below_wanted = p_so;
2161#ifdef FEAT_MOUSE
Bram Moolenaar9964e462007-05-05 17:54:07 +00002162 if (mouse_dragging > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002163 {
2164 above_wanted = mouse_dragging - 1;
2165 below_wanted = mouse_dragging - 1;
2166 }
2167#endif
2168 if (curwin->w_topline == 1)
2169 {
2170 above_wanted = 0;
2171 max_off = curwin->w_height / 2;
2172 if (below_wanted > max_off)
2173 below_wanted = max_off;
2174 }
2175 validate_botline();
2176 if (curwin->w_botline == curbuf->b_ml.ml_line_count + 1
2177#ifdef FEAT_MOUSE
Bram Moolenaar9964e462007-05-05 17:54:07 +00002178 && mouse_dragging == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002179#endif
2180 )
2181 {
2182 below_wanted = 0;
2183 max_off = (curwin->w_height - 1) / 2;
2184 if (above_wanted > max_off)
2185 above_wanted = max_off;
2186 }
2187
2188 /*
2189 * If there are sufficient file-lines above and below the cursor, we can
2190 * return now.
2191 */
2192 cln = curwin->w_cursor.lnum;
2193 if (cln >= curwin->w_topline + above_wanted
2194 && cln < curwin->w_botline - below_wanted
2195#ifdef FEAT_FOLDING
2196 && !hasAnyFolding(curwin)
2197#endif
2198 )
2199 return;
2200
2201 /*
2202 * Narrow down the area where the cursor can be put by taking lines from
2203 * the top and the bottom until:
2204 * - the desired context lines are found
2205 * - the lines from the top is past the lines from the bottom
2206 */
2207 topline = curwin->w_topline;
2208 botline = curwin->w_botline - 1;
2209#ifdef FEAT_DIFF
2210 /* count filler lines as context */
2211 above = curwin->w_topfill;
2212 below = curwin->w_filler_rows;
2213#endif
2214 while ((above < above_wanted || below < below_wanted) && topline < botline)
2215 {
2216 if (below < below_wanted && (below <= above || above >= above_wanted))
2217 {
2218#ifdef FEAT_FOLDING
2219 if (hasFolding(botline, &botline, NULL))
2220 ++below;
2221 else
2222#endif
2223 below += plines(botline);
2224 --botline;
2225 }
2226 if (above < above_wanted && (above < below || below >= below_wanted))
2227 {
2228#ifdef FEAT_FOLDING
2229 if (hasFolding(topline, NULL, &topline))
2230 ++above;
2231 else
2232#endif
Bram Moolenaar43335ea2015-09-09 20:59:37 +02002233 above += PLINES_NOFILL(topline);
2234#ifdef FEAT_DIFF
Bram Moolenaar071d4272004-06-13 20:20:40 +00002235 /* Count filler lines below this line as context. */
2236 if (topline < botline)
2237 above += diff_check_fill(curwin, topline + 1);
2238#endif
2239 ++topline;
2240 }
2241 }
2242 if (topline == botline || botline == 0)
2243 curwin->w_cursor.lnum = topline;
2244 else if (topline > botline)
2245 curwin->w_cursor.lnum = botline;
2246 else
2247 {
2248 if (cln < topline && curwin->w_topline > 1)
2249 {
2250 curwin->w_cursor.lnum = topline;
2251 curwin->w_valid &=
2252 ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW);
2253 }
2254 if (cln > botline && curwin->w_botline <= curbuf->b_ml.ml_line_count)
2255 {
2256 curwin->w_cursor.lnum = botline;
2257 curwin->w_valid &=
2258 ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW);
2259 }
2260 }
2261 curwin->w_valid |= VALID_TOPLINE;
2262}
2263
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +01002264static void get_scroll_overlap(lineoff_T *lp, int dir);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002265
2266/*
2267 * move screen 'count' pages up or down and update screen
2268 *
2269 * return FAIL for failure, OK otherwise
2270 */
2271 int
Bram Moolenaar9b578142016-01-30 19:39:49 +01002272onepage(int dir, long count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002273{
2274 long n;
2275 int retval = OK;
2276 lineoff_T loff;
2277 linenr_T old_topline = curwin->w_topline;
2278
2279 if (curbuf->b_ml.ml_line_count == 1) /* nothing to do */
2280 {
2281 beep_flush();
2282 return FAIL;
2283 }
2284
2285 for ( ; count > 0; --count)
2286 {
2287 validate_botline();
2288 /*
2289 * It's an error to move a page up when the first line is already on
2290 * the screen. It's an error to move a page down when the last line
2291 * is on the screen and the topline is 'scrolloff' lines from the
2292 * last line.
2293 */
2294 if (dir == FORWARD
2295 ? ((curwin->w_topline >= curbuf->b_ml.ml_line_count - p_so)
2296 && curwin->w_botline > curbuf->b_ml.ml_line_count)
2297 : (curwin->w_topline == 1
2298#ifdef FEAT_DIFF
2299 && curwin->w_topfill ==
2300 diff_check_fill(curwin, curwin->w_topline)
2301#endif
2302 ))
2303 {
2304 beep_flush();
2305 retval = FAIL;
2306 break;
2307 }
2308
2309#ifdef FEAT_DIFF
2310 loff.fill = 0;
2311#endif
2312 if (dir == FORWARD)
2313 {
Bram Moolenaar4399ef42005-02-12 14:29:27 +00002314 if (firstwin == lastwin && p_window > 0 && p_window < Rows - 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002315 {
Bram Moolenaar4399ef42005-02-12 14:29:27 +00002316 /* Vi compatible scrolling */
2317 if (p_window <= 2)
2318 ++curwin->w_topline;
2319 else
2320 curwin->w_topline += p_window - 2;
2321 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
2322 curwin->w_topline = curbuf->b_ml.ml_line_count;
2323 curwin->w_cursor.lnum = curwin->w_topline;
2324 }
2325 else if (curwin->w_botline > curbuf->b_ml.ml_line_count)
2326 {
2327 /* at end of file */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002328 curwin->w_topline = curbuf->b_ml.ml_line_count;
2329#ifdef FEAT_DIFF
2330 curwin->w_topfill = 0;
2331#endif
2332 curwin->w_valid &= ~(VALID_WROW|VALID_CROW);
2333 }
2334 else
2335 {
2336 /* For the overlap, start with the line just below the window
2337 * and go upwards. */
2338 loff.lnum = curwin->w_botline;
2339#ifdef FEAT_DIFF
2340 loff.fill = diff_check_fill(curwin, loff.lnum)
2341 - curwin->w_filler_rows;
2342#endif
2343 get_scroll_overlap(&loff, -1);
2344 curwin->w_topline = loff.lnum;
2345#ifdef FEAT_DIFF
2346 curwin->w_topfill = loff.fill;
2347 check_topfill(curwin, FALSE);
2348#endif
2349 curwin->w_cursor.lnum = curwin->w_topline;
2350 curwin->w_valid &= ~(VALID_WCOL|VALID_CHEIGHT|VALID_WROW|
2351 VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
2352 }
2353 }
2354 else /* dir == BACKWARDS */
2355 {
2356#ifdef FEAT_DIFF
2357 if (curwin->w_topline == 1)
2358 {
2359 /* Include max number of filler lines */
2360 max_topfill();
2361 continue;
2362 }
2363#endif
Bram Moolenaar4399ef42005-02-12 14:29:27 +00002364 if (firstwin == lastwin && p_window > 0 && p_window < Rows - 1)
2365 {
2366 /* Vi compatible scrolling (sort of) */
2367 if (p_window <= 2)
2368 --curwin->w_topline;
2369 else
2370 curwin->w_topline -= p_window - 2;
2371 if (curwin->w_topline < 1)
2372 curwin->w_topline = 1;
2373 curwin->w_cursor.lnum = curwin->w_topline + p_window - 1;
2374 if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
2375 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
2376 continue;
2377 }
2378
Bram Moolenaar071d4272004-06-13 20:20:40 +00002379 /* Find the line at the top of the window that is going to be the
2380 * line at the bottom of the window. Make sure this results in
2381 * the same line as before doing CTRL-F. */
2382 loff.lnum = curwin->w_topline - 1;
2383#ifdef FEAT_DIFF
2384 loff.fill = diff_check_fill(curwin, loff.lnum + 1)
2385 - curwin->w_topfill;
2386#endif
2387 get_scroll_overlap(&loff, 1);
2388
2389 if (loff.lnum >= curbuf->b_ml.ml_line_count)
2390 {
2391 loff.lnum = curbuf->b_ml.ml_line_count;
2392#ifdef FEAT_DIFF
2393 loff.fill = 0;
2394 }
2395 else
2396 {
2397 botline_topline(&loff);
2398#endif
2399 }
2400 curwin->w_cursor.lnum = loff.lnum;
2401
2402 /* Find the line just above the new topline to get the right line
2403 * at the bottom of the window. */
2404 n = 0;
2405 while (n <= curwin->w_height && loff.lnum >= 1)
2406 {
2407 topline_back(&loff);
Bram Moolenaarbacd9da2010-02-17 18:20:37 +01002408 if (loff.height == MAXCOL)
2409 n = MAXCOL;
2410 else
2411 n += loff.height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002412 }
Bram Moolenaarbacd9da2010-02-17 18:20:37 +01002413 if (loff.lnum < 1) /* at begin of file */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002414 {
2415 curwin->w_topline = 1;
2416#ifdef FEAT_DIFF
2417 max_topfill();
2418#endif
2419 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
2420 }
2421 else
2422 {
2423 /* Go two lines forward again. */
2424#ifdef FEAT_DIFF
2425 topline_botline(&loff);
2426#endif
2427 botline_forw(&loff);
2428 botline_forw(&loff);
2429#ifdef FEAT_DIFF
2430 botline_topline(&loff);
2431#endif
2432#ifdef FEAT_FOLDING
2433 /* We're at the wrong end of a fold now. */
2434 (void)hasFolding(loff.lnum, &loff.lnum, NULL);
2435#endif
2436
2437 /* Always scroll at least one line. Avoid getting stuck on
2438 * very long lines. */
2439 if (loff.lnum >= curwin->w_topline
2440#ifdef FEAT_DIFF
2441 && (loff.lnum > curwin->w_topline
2442 || loff.fill >= curwin->w_topfill)
2443#endif
2444 )
2445 {
2446#ifdef FEAT_DIFF
2447 /* First try using the maximum number of filler lines. If
2448 * that's not enough, backup one line. */
2449 loff.fill = curwin->w_topfill;
2450 if (curwin->w_topfill < diff_check_fill(curwin,
2451 curwin->w_topline))
2452 max_topfill();
2453 if (curwin->w_topfill == loff.fill)
2454#endif
2455 {
2456 --curwin->w_topline;
2457#ifdef FEAT_DIFF
2458 curwin->w_topfill = 0;
2459#endif
2460 }
2461 comp_botline(curwin);
2462 curwin->w_cursor.lnum = curwin->w_botline - 1;
Bram Moolenaar3d6db142014-03-28 21:49:32 +01002463 curwin->w_valid &=
2464 ~(VALID_WCOL|VALID_CHEIGHT|VALID_WROW|VALID_CROW);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002465 }
2466 else
2467 {
2468 curwin->w_topline = loff.lnum;
2469#ifdef FEAT_DIFF
2470 curwin->w_topfill = loff.fill;
2471 check_topfill(curwin, FALSE);
2472#endif
2473 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
2474 }
2475 }
2476 }
2477 }
2478#ifdef FEAT_FOLDING
2479 foldAdjustCursor();
2480#endif
2481 cursor_correct();
Bram Moolenaar7c626922005-02-07 22:01:03 +00002482 if (retval == OK)
2483 beginline(BL_SOL | BL_FIX);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002484 curwin->w_valid &= ~(VALID_WCOL|VALID_WROW|VALID_VIRTCOL);
2485
2486 /*
2487 * Avoid the screen jumping up and down when 'scrolloff' is non-zero.
2488 * But make sure we scroll at least one line (happens with mix of long
2489 * wrapping lines and non-wrapping line).
2490 */
2491 if (retval == OK && dir == FORWARD && check_top_offset())
2492 {
2493 scroll_cursor_top(1, FALSE);
2494 if (curwin->w_topline <= old_topline
2495 && old_topline < curbuf->b_ml.ml_line_count)
2496 {
2497 curwin->w_topline = old_topline + 1;
2498#ifdef FEAT_FOLDING
2499 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
2500#endif
2501 }
2502 }
2503
2504 redraw_later(VALID);
2505 return retval;
2506}
2507
2508/*
2509 * Decide how much overlap to use for page-up or page-down scrolling.
2510 * This is symmetric, so that doing both keeps the same lines displayed.
2511 * Three lines are examined:
2512 *
2513 * before CTRL-F after CTRL-F / before CTRL-B
2514 * etc. l1
2515 * l1 last but one line ------------
2516 * l2 last text line l2 top text line
2517 * ------------- l3 second text line
2518 * l3 etc.
2519 */
2520 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002521get_scroll_overlap(lineoff_T *lp, int dir)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002522{
2523 int h1, h2, h3, h4;
2524 int min_height = curwin->w_height - 2;
2525 lineoff_T loff0, loff1, loff2;
2526
2527#ifdef FEAT_DIFF
2528 if (lp->fill > 0)
2529 lp->height = 1;
2530 else
2531 lp->height = plines_nofill(lp->lnum);
2532#else
2533 lp->height = plines(lp->lnum);
2534#endif
2535 h1 = lp->height;
2536 if (h1 > min_height)
2537 return; /* no overlap */
2538
2539 loff0 = *lp;
2540 if (dir > 0)
2541 botline_forw(lp);
2542 else
2543 topline_back(lp);
2544 h2 = lp->height;
Bram Moolenaarf4f19562012-11-28 18:22:11 +01002545 if (h2 == MAXCOL || h2 + h1 > min_height)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002546 {
2547 *lp = loff0; /* no overlap */
2548 return;
2549 }
2550
2551 loff1 = *lp;
2552 if (dir > 0)
2553 botline_forw(lp);
2554 else
2555 topline_back(lp);
2556 h3 = lp->height;
Bram Moolenaarf4f19562012-11-28 18:22:11 +01002557 if (h3 == MAXCOL || h3 + h2 > min_height)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002558 {
2559 *lp = loff0; /* no overlap */
2560 return;
2561 }
2562
2563 loff2 = *lp;
2564 if (dir > 0)
2565 botline_forw(lp);
2566 else
2567 topline_back(lp);
2568 h4 = lp->height;
Bram Moolenaarf4f19562012-11-28 18:22:11 +01002569 if (h4 == MAXCOL || h4 + h3 + h2 > min_height || h3 + h2 + h1 > min_height)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002570 *lp = loff1; /* 1 line overlap */
2571 else
2572 *lp = loff2; /* 2 lines overlap */
2573 return;
2574}
2575
2576/* #define KEEP_SCREEN_LINE */
2577/*
2578 * Scroll 'scroll' lines up or down.
2579 */
2580 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002581halfpage(int flag, linenr_T Prenum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002582{
2583 long scrolled = 0;
2584 int i;
2585 int n;
2586 int room;
2587
2588 if (Prenum)
2589 curwin->w_p_scr = (Prenum > curwin->w_height) ?
2590 curwin->w_height : Prenum;
2591 n = (curwin->w_p_scr <= curwin->w_height) ?
2592 curwin->w_p_scr : curwin->w_height;
2593
2594 validate_botline();
2595 room = curwin->w_empty_rows;
2596#ifdef FEAT_DIFF
2597 room += curwin->w_filler_rows;
2598#endif
2599 if (flag)
2600 {
2601 /*
2602 * scroll the text up
2603 */
2604 while (n > 0 && curwin->w_botline <= curbuf->b_ml.ml_line_count)
2605 {
2606#ifdef FEAT_DIFF
2607 if (curwin->w_topfill > 0)
2608 {
2609 i = 1;
2610 if (--n < 0 && scrolled > 0)
2611 break;
2612 --curwin->w_topfill;
2613 }
2614 else
2615#endif
2616 {
Bram Moolenaar43335ea2015-09-09 20:59:37 +02002617 i = PLINES_NOFILL(curwin->w_topline);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002618 n -= i;
2619 if (n < 0 && scrolled > 0)
2620 break;
2621#ifdef FEAT_FOLDING
2622 (void)hasFolding(curwin->w_topline, NULL, &curwin->w_topline);
2623#endif
2624 ++curwin->w_topline;
2625#ifdef FEAT_DIFF
2626 curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline);
2627#endif
2628
2629#ifndef KEEP_SCREEN_LINE
2630 if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
2631 {
2632 ++curwin->w_cursor.lnum;
2633 curwin->w_valid &=
2634 ~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL);
2635 }
2636#endif
2637 }
2638 curwin->w_valid &= ~(VALID_CROW|VALID_WROW);
2639 scrolled += i;
2640
2641 /*
2642 * Correct w_botline for changed w_topline.
2643 * Won't work when there are filler lines.
2644 */
2645#ifdef FEAT_DIFF
2646 if (curwin->w_p_diff)
2647 curwin->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP);
2648 else
2649#endif
2650 {
2651 room += i;
2652 do
2653 {
2654 i = plines(curwin->w_botline);
2655 if (i > room)
2656 break;
2657#ifdef FEAT_FOLDING
2658 (void)hasFolding(curwin->w_botline, NULL,
2659 &curwin->w_botline);
2660#endif
2661 ++curwin->w_botline;
2662 room -= i;
2663 } while (curwin->w_botline <= curbuf->b_ml.ml_line_count);
2664 }
2665 }
2666
2667#ifndef KEEP_SCREEN_LINE
2668 /*
2669 * When hit bottom of the file: move cursor down.
2670 */
2671 if (n > 0)
2672 {
2673# ifdef FEAT_FOLDING
2674 if (hasAnyFolding(curwin))
2675 {
2676 while (--n >= 0
2677 && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
2678 {
2679 (void)hasFolding(curwin->w_cursor.lnum, NULL,
2680 &curwin->w_cursor.lnum);
2681 ++curwin->w_cursor.lnum;
2682 }
2683 }
2684 else
2685# endif
2686 curwin->w_cursor.lnum += n;
2687 check_cursor_lnum();
2688 }
2689#else
2690 /* try to put the cursor in the same screen line */
2691 while ((curwin->w_cursor.lnum < curwin->w_topline || scrolled > 0)
2692 && curwin->w_cursor.lnum < curwin->w_botline - 1)
2693 {
2694 scrolled -= plines(curwin->w_cursor.lnum);
2695 if (scrolled < 0 && curwin->w_cursor.lnum >= curwin->w_topline)
2696 break;
2697# ifdef FEAT_FOLDING
2698 (void)hasFolding(curwin->w_cursor.lnum, NULL,
2699 &curwin->w_cursor.lnum);
2700# endif
2701 ++curwin->w_cursor.lnum;
2702 }
2703#endif
2704 }
2705 else
2706 {
2707 /*
2708 * scroll the text down
2709 */
2710 while (n > 0 && curwin->w_topline > 1)
2711 {
2712#ifdef FEAT_DIFF
2713 if (curwin->w_topfill < diff_check_fill(curwin, curwin->w_topline))
2714 {
2715 i = 1;
2716 if (--n < 0 && scrolled > 0)
2717 break;
2718 ++curwin->w_topfill;
2719 }
2720 else
2721#endif
2722 {
Bram Moolenaar43335ea2015-09-09 20:59:37 +02002723 i = PLINES_NOFILL(curwin->w_topline - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002724 n -= i;
2725 if (n < 0 && scrolled > 0)
2726 break;
2727 --curwin->w_topline;
2728#ifdef FEAT_FOLDING
2729 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
2730#endif
2731#ifdef FEAT_DIFF
2732 curwin->w_topfill = 0;
2733#endif
2734 }
2735 curwin->w_valid &= ~(VALID_CROW|VALID_WROW|
2736 VALID_BOTLINE|VALID_BOTLINE_AP);
2737 scrolled += i;
2738#ifndef KEEP_SCREEN_LINE
2739 if (curwin->w_cursor.lnum > 1)
2740 {
2741 --curwin->w_cursor.lnum;
2742 curwin->w_valid &= ~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL);
2743 }
2744#endif
2745 }
2746#ifndef KEEP_SCREEN_LINE
2747 /*
2748 * When hit top of the file: move cursor up.
2749 */
2750 if (n > 0)
2751 {
2752 if (curwin->w_cursor.lnum <= (linenr_T)n)
2753 curwin->w_cursor.lnum = 1;
2754 else
2755# ifdef FEAT_FOLDING
2756 if (hasAnyFolding(curwin))
2757 {
2758 while (--n >= 0 && curwin->w_cursor.lnum > 1)
2759 {
2760 --curwin->w_cursor.lnum;
2761 (void)hasFolding(curwin->w_cursor.lnum,
2762 &curwin->w_cursor.lnum, NULL);
2763 }
2764 }
2765 else
2766# endif
2767 curwin->w_cursor.lnum -= n;
2768 }
2769#else
2770 /* try to put the cursor in the same screen line */
2771 scrolled += n; /* move cursor when topline is 1 */
2772 while (curwin->w_cursor.lnum > curwin->w_topline
2773 && (scrolled > 0 || curwin->w_cursor.lnum >= curwin->w_botline))
2774 {
2775 scrolled -= plines(curwin->w_cursor.lnum - 1);
2776 if (scrolled < 0 && curwin->w_cursor.lnum < curwin->w_botline)
2777 break;
2778 --curwin->w_cursor.lnum;
2779# ifdef FEAT_FOLDING
2780 foldAdjustCursor();
2781# endif
2782 }
2783#endif
2784 }
2785# ifdef FEAT_FOLDING
2786 /* Move cursor to first line of closed fold. */
2787 foldAdjustCursor();
2788# endif
2789#ifdef FEAT_DIFF
2790 check_topfill(curwin, !flag);
2791#endif
2792 cursor_correct();
2793 beginline(BL_SOL | BL_FIX);
2794 redraw_later(VALID);
2795}
Bram Moolenaar860cae12010-06-05 23:22:07 +02002796
2797#if defined(FEAT_CURSORBIND) || defined(PROTO)
2798 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002799do_check_cursorbind(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002800{
2801 linenr_T line = curwin->w_cursor.lnum;
Bram Moolenaar1ea69b72012-03-16 19:24:26 +01002802 colnr_T col = curwin->w_cursor.col;
2803# ifdef FEAT_VIRTUALEDIT
2804 colnr_T coladd = curwin->w_cursor.coladd;
2805# endif
Bram Moolenaar524780d2012-03-28 14:19:50 +02002806 colnr_T curswant = curwin->w_curswant;
2807 int set_curswant = curwin->w_set_curswant;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002808 win_T *old_curwin = curwin;
2809 buf_T *old_curbuf = curbuf;
Bram Moolenaar61452852011-02-01 18:01:11 +01002810 int restart_edit_save;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002811 int old_VIsual_select = VIsual_select;
2812 int old_VIsual_active = VIsual_active;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002813
2814 /*
2815 * loop through the cursorbound windows
2816 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002817 VIsual_select = VIsual_active = 0;
Bram Moolenaar29323592016-07-24 22:04:11 +02002818 FOR_ALL_WINDOWS(curwin)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002819 {
2820 curbuf = curwin->w_buffer;
2821 /* skip original window and windows with 'noscrollbind' */
2822 if (curwin != old_curwin && curwin->w_p_crb)
2823 {
2824# ifdef FEAT_DIFF
2825 if (curwin->w_p_diff)
2826 curwin->w_cursor.lnum
2827 = diff_get_corresponding_line(old_curbuf,
2828 line,
2829 curbuf,
2830 curwin->w_cursor.lnum);
2831 else
2832# endif
2833 curwin->w_cursor.lnum = line;
2834 curwin->w_cursor.col = col;
Bram Moolenaar1ea69b72012-03-16 19:24:26 +01002835# ifdef FEAT_VIRTUALEDIT
2836 curwin->w_cursor.coladd = coladd;
2837# endif
Bram Moolenaar524780d2012-03-28 14:19:50 +02002838 curwin->w_curswant = curswant;
2839 curwin->w_set_curswant = set_curswant;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002840
Bram Moolenaar61452852011-02-01 18:01:11 +01002841 /* Make sure the cursor is in a valid position. Temporarily set
2842 * "restart_edit" to allow the cursor to be beyond the EOL. */
2843 restart_edit_save = restart_edit;
2844 restart_edit = TRUE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002845 check_cursor();
Bram Moolenaar61452852011-02-01 18:01:11 +01002846 restart_edit = restart_edit_save;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002847# ifdef FEAT_MBYTE
2848 /* Correct cursor for multi-byte character. */
2849 if (has_mbyte)
2850 mb_adjust_cursor();
2851# endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002852 redraw_later(VALID);
Bram Moolenaarf3d419d2011-01-22 21:05:07 +01002853
2854 /* Only scroll when 'scrollbind' hasn't done this. */
2855 if (!curwin->w_p_scb)
2856 update_topline();
Bram Moolenaar860cae12010-06-05 23:22:07 +02002857# ifdef FEAT_WINDOWS
2858 curwin->w_redr_status = TRUE;
2859# endif
2860 }
2861 }
2862
2863 /*
2864 * reset current-window
2865 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002866 VIsual_select = old_VIsual_select;
2867 VIsual_active = old_VIsual_active;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002868 curwin = old_curwin;
2869 curbuf = old_curbuf;
2870}
2871#endif /* FEAT_CURSORBIND */