1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
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 
22 static int scrolljump_value(void);
23 static int check_top_offset(void);
24 static void curs_rows(win_T *wp);
25 
26 typedef struct
27 {
28     linenr_T	    lnum;	// line number
29 #ifdef FEAT_DIFF
30     int		    fill;	// filler lines
31 #endif
32     int		    height;	// height of added line
33 } lineoff_T;
34 
35 static void topline_back(lineoff_T *lp);
36 static void botline_forw(lineoff_T *lp);
37 
38 /*
39  * Compute wp->w_botline for the current wp->w_topline.  Can be called after
40  * wp->w_topline changed.
41  */
42     static void
comp_botline(win_T * wp)43 comp_botline(win_T *wp)
44 {
45     int		n;
46     linenr_T	lnum;
47     int		done;
48 #ifdef FEAT_FOLDING
49     linenr_T    last;
50     int		folded;
51 #endif
52 
53     /*
54      * If w_cline_row is valid, start there.
55      * Otherwise have to start at w_topline.
56      */
57     check_cursor_moved(wp);
58     if (wp->w_valid & VALID_CROW)
59     {
60 	lnum = wp->w_cursor.lnum;
61 	done = wp->w_cline_row;
62     }
63     else
64     {
65 	lnum = wp->w_topline;
66 	done = 0;
67     }
68 
69     for ( ; lnum <= wp->w_buffer->b_ml.ml_line_count; ++lnum)
70     {
71 #ifdef FEAT_FOLDING
72 	last = lnum;
73 	folded = FALSE;
74 	if (hasFoldingWin(wp, lnum, NULL, &last, TRUE, NULL))
75 	{
76 	    n = 1;
77 	    folded = TRUE;
78 	}
79 	else
80 #endif
81 #ifdef FEAT_DIFF
82 	    if (lnum == wp->w_topline)
83 		n = plines_win_nofill(wp, lnum, TRUE) + wp->w_topfill;
84 	    else
85 #endif
86 		n = plines_win(wp, lnum, TRUE);
87 	if (
88 #ifdef FEAT_FOLDING
89 		lnum <= wp->w_cursor.lnum && last >= wp->w_cursor.lnum
90 #else
91 		lnum == wp->w_cursor.lnum
92 #endif
93 	   )
94 	{
95 	    wp->w_cline_row = done;
96 	    wp->w_cline_height = n;
97 #ifdef FEAT_FOLDING
98 	    wp->w_cline_folded = folded;
99 #endif
100 	    redraw_for_cursorline(wp);
101 	    wp->w_valid |= (VALID_CROW|VALID_CHEIGHT);
102 	}
103 	if (done + n > wp->w_height)
104 	    break;
105 	done += n;
106 #ifdef FEAT_FOLDING
107 	lnum = last;
108 #endif
109     }
110 
111     // wp->w_botline is the line that is just below the window
112     wp->w_botline = lnum;
113     wp->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
114 
115     set_empty_rows(wp, done);
116 }
117 
118 #ifdef FEAT_SYN_HL
119     void
reset_cursorline(void)120 reset_cursorline(void)
121 {
122     curwin->w_last_cursorline = 0;
123 }
124 #endif
125 
126 /*
127  * Redraw when w_cline_row changes and 'relativenumber' or 'cursorline' is
128  * set.
129  */
130     void
redraw_for_cursorline(win_T * wp)131 redraw_for_cursorline(win_T *wp)
132 {
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 	    && !pum_visible())
140     {
141 	if (wp->w_p_rnu)
142 	    // win_line() will redraw the number column only.
143 	    redraw_win_later(wp, VALID);
144 #ifdef FEAT_SYN_HL
145 	if (wp->w_p_cul)
146 	{
147 	    if (wp->w_redr_type <= VALID && wp->w_last_cursorline != 0)
148 	    {
149 		// "w_last_cursorline" may be outdated, worst case we redraw
150 		// too much.  This is optimized for moving the cursor around in
151 		// the current window.
152 		redrawWinline(wp, wp->w_last_cursorline);
153 		redrawWinline(wp, wp->w_cursor.lnum);
154 	    }
155 	    else
156 		redraw_win_later(wp, SOME_VALID);
157 	}
158 #endif
159     }
160 }
161 
162 /*
163  * Update curwin->w_topline and redraw if necessary.
164  * Used to update the screen before printing a message.
165  */
166     void
update_topline_redraw(void)167 update_topline_redraw(void)
168 {
169     update_topline();
170     if (must_redraw)
171 	update_screen(0);
172 }
173 
174 /*
175  * Update curwin->w_topline to move the cursor onto the screen.
176  */
177     void
update_topline(void)178 update_topline(void)
179 {
180     long	line_count;
181     int		halfheight;
182     int		n;
183     linenr_T	old_topline;
184 #ifdef FEAT_DIFF
185     int		old_topfill;
186 #endif
187 #ifdef FEAT_FOLDING
188     linenr_T	lnum;
189 #endif
190     int		check_topline = FALSE;
191     int		check_botline = FALSE;
192     long        *so_ptr = curwin->w_p_so >= 0 ? &curwin->w_p_so : &p_so;
193     int		save_so = *so_ptr;
194 
195     // If there is no valid screen and when the window height is zero just use
196     // the cursor line.
197     if (!screen_valid(TRUE) || curwin->w_height == 0)
198     {
199 	check_cursor_lnum();
200 	curwin->w_topline = curwin->w_cursor.lnum;
201 	curwin->w_botline = curwin->w_topline;
202 	curwin->w_scbind_pos = 1;
203 	return;
204     }
205 
206     check_cursor_moved(curwin);
207     if (curwin->w_valid & VALID_TOPLINE)
208 	return;
209 
210     // When dragging with the mouse, don't scroll that quickly
211     if (mouse_dragging > 0)
212 	*so_ptr = mouse_dragging - 1;
213 
214     old_topline = curwin->w_topline;
215 #ifdef FEAT_DIFF
216     old_topfill = curwin->w_topfill;
217 #endif
218 
219     /*
220      * If the buffer is empty, always set topline to 1.
221      */
222     if (BUFEMPTY())		// special case - file is empty
223     {
224 	if (curwin->w_topline != 1)
225 	    redraw_later(NOT_VALID);
226 	curwin->w_topline = 1;
227 	curwin->w_botline = 2;
228 	curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
229 	curwin->w_scbind_pos = 1;
230     }
231 
232     /*
233      * If the cursor is above or near the top of the window, scroll the window
234      * to show the line the cursor is in, with 'scrolloff' context.
235      */
236     else
237     {
238 	if (curwin->w_topline > 1)
239 	{
240 	    // If the cursor is above topline, scrolling is always needed.
241 	    // If the cursor is far below topline and there is no folding,
242 	    // scrolling down is never needed.
243 	    if (curwin->w_cursor.lnum < curwin->w_topline)
244 		check_topline = TRUE;
245 	    else if (check_top_offset())
246 		check_topline = TRUE;
247 	}
248 #ifdef FEAT_DIFF
249 	    // Check if there are more filler lines than allowed.
250 	if (!check_topline && curwin->w_topfill > diff_check_fill(curwin,
251 							   curwin->w_topline))
252 	    check_topline = TRUE;
253 #endif
254 
255 	if (check_topline)
256 	{
257 	    halfheight = curwin->w_height / 2 - 1;
258 	    if (halfheight < 2)
259 		halfheight = 2;
260 
261 #ifdef FEAT_FOLDING
262 	    if (hasAnyFolding(curwin))
263 	    {
264 		// Count the number of logical lines between the cursor and
265 		// topline + scrolloff (approximation of how much will be
266 		// scrolled).
267 		n = 0;
268 		for (lnum = curwin->w_cursor.lnum;
269 				    lnum < curwin->w_topline + *so_ptr; ++lnum)
270 		{
271 		    ++n;
272 		    // stop at end of file or when we know we are far off
273 		    if (lnum >= curbuf->b_ml.ml_line_count || n >= halfheight)
274 			break;
275 		    (void)hasFolding(lnum, NULL, &lnum);
276 		}
277 	    }
278 	    else
279 #endif
280 		n = curwin->w_topline + *so_ptr - curwin->w_cursor.lnum;
281 
282 	    // If we weren't very close to begin with, we scroll to put the
283 	    // cursor in the middle of the window.  Otherwise put the cursor
284 	    // near the top of the window.
285 	    if (n >= halfheight)
286 		scroll_cursor_halfway(FALSE);
287 	    else
288 	    {
289 		scroll_cursor_top(scrolljump_value(), FALSE);
290 		check_botline = TRUE;
291 	    }
292 	}
293 
294 	else
295 	{
296 #ifdef FEAT_FOLDING
297 	    // Make sure topline is the first line of a fold.
298 	    (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
299 #endif
300 	    check_botline = TRUE;
301 	}
302     }
303 
304     /*
305      * If the cursor is below the bottom of the window, scroll the window
306      * to put the cursor on the window.
307      * When w_botline is invalid, recompute it first, to avoid a redraw later.
308      * If w_botline was approximated, we might need a redraw later in a few
309      * cases, but we don't want to spend (a lot of) time recomputing w_botline
310      * for every small change.
311      */
312     if (check_botline)
313     {
314 	if (!(curwin->w_valid & VALID_BOTLINE_AP))
315 	    validate_botline();
316 
317 	if (curwin->w_botline <= curbuf->b_ml.ml_line_count)
318 	{
319 	    if (curwin->w_cursor.lnum < curwin->w_botline)
320 	    {
321 	      if (((long)curwin->w_cursor.lnum
322 					     >= (long)curwin->w_botline - *so_ptr
323 #ifdef FEAT_FOLDING
324 			|| hasAnyFolding(curwin)
325 #endif
326 			))
327 	      {
328 		lineoff_T	loff;
329 
330 		// Cursor is (a few lines) above botline, check if there are
331 		// 'scrolloff' window lines below the cursor.  If not, need to
332 		// scroll.
333 		n = curwin->w_empty_rows;
334 		loff.lnum = curwin->w_cursor.lnum;
335 #ifdef FEAT_FOLDING
336 		// In a fold go to its last line.
337 		(void)hasFolding(loff.lnum, NULL, &loff.lnum);
338 #endif
339 #ifdef FEAT_DIFF
340 		loff.fill = 0;
341 		n += curwin->w_filler_rows;
342 #endif
343 		loff.height = 0;
344 		while (loff.lnum < curwin->w_botline
345 #ifdef FEAT_DIFF
346 			&& (loff.lnum + 1 < curwin->w_botline || loff.fill == 0)
347 #endif
348 			)
349 		{
350 		    n += loff.height;
351 		    if (n >= *so_ptr)
352 			break;
353 		    botline_forw(&loff);
354 		}
355 		if (n >= *so_ptr)
356 		    // sufficient context, no need to scroll
357 		    check_botline = FALSE;
358 	      }
359 	      else
360 		  // sufficient context, no need to scroll
361 		  check_botline = FALSE;
362 	    }
363 	    if (check_botline)
364 	    {
365 #ifdef FEAT_FOLDING
366 		if (hasAnyFolding(curwin))
367 		{
368 		    // Count the number of logical lines between the cursor and
369 		    // botline - scrolloff (approximation of how much will be
370 		    // scrolled).
371 		    line_count = 0;
372 		    for (lnum = curwin->w_cursor.lnum;
373 				   lnum >= curwin->w_botline - *so_ptr; --lnum)
374 		    {
375 			++line_count;
376 			// stop at end of file or when we know we are far off
377 			if (lnum <= 0 || line_count > curwin->w_height + 1)
378 			    break;
379 			(void)hasFolding(lnum, &lnum, NULL);
380 		    }
381 		}
382 		else
383 #endif
384 		    line_count = curwin->w_cursor.lnum - curwin->w_botline
385 								   + 1 + *so_ptr;
386 		if (line_count <= curwin->w_height + 1)
387 		    scroll_cursor_bot(scrolljump_value(), FALSE);
388 		else
389 		    scroll_cursor_halfway(FALSE);
390 	    }
391 	}
392     }
393     curwin->w_valid |= VALID_TOPLINE;
394 
395     /*
396      * Need to redraw when topline changed.
397      */
398     if (curwin->w_topline != old_topline
399 #ifdef FEAT_DIFF
400 	    || curwin->w_topfill != old_topfill
401 #endif
402 	    )
403     {
404 	dollar_vcol = -1;
405 	if (curwin->w_skipcol != 0)
406 	{
407 	    curwin->w_skipcol = 0;
408 	    redraw_later(NOT_VALID);
409 	}
410 	else
411 	    redraw_later(VALID);
412 	// May need to set w_skipcol when cursor in w_topline.
413 	if (curwin->w_cursor.lnum == curwin->w_topline)
414 	    validate_cursor();
415     }
416 
417     *so_ptr = save_so;
418 }
419 
420 /*
421  * Return the scrolljump value to use for the current window.
422  * When 'scrolljump' is positive use it as-is.
423  * When 'scrolljump' is negative use it as a percentage of the window height.
424  */
425     static int
scrolljump_value(void)426 scrolljump_value(void)
427 {
428     if (p_sj >= 0)
429 	return (int)p_sj;
430     return (curwin->w_height * -p_sj) / 100;
431 }
432 
433 /*
434  * Return TRUE when there are not 'scrolloff' lines above the cursor for the
435  * current window.
436  */
437     static int
check_top_offset(void)438 check_top_offset(void)
439 {
440     lineoff_T	loff;
441     int		n;
442     long        so = get_scrolloff_value();
443 
444     if (curwin->w_cursor.lnum < curwin->w_topline + so
445 #ifdef FEAT_FOLDING
446 		    || hasAnyFolding(curwin)
447 #endif
448 	    )
449     {
450 	loff.lnum = curwin->w_cursor.lnum;
451 #ifdef FEAT_DIFF
452 	loff.fill = 0;
453 	n = curwin->w_topfill;	    // always have this context
454 #else
455 	n = 0;
456 #endif
457 	// Count the visible screen lines above the cursor line.
458 	while (n < so)
459 	{
460 	    topline_back(&loff);
461 	    // Stop when included a line above the window.
462 	    if (loff.lnum < curwin->w_topline
463 #ifdef FEAT_DIFF
464 		    || (loff.lnum == curwin->w_topline && loff.fill > 0)
465 #endif
466 		    )
467 		break;
468 	    n += loff.height;
469 	}
470 	if (n < so)
471 	    return TRUE;
472     }
473     return FALSE;
474 }
475 
476     void
update_curswant(void)477 update_curswant(void)
478 {
479     if (curwin->w_set_curswant)
480     {
481 	validate_virtcol();
482 	curwin->w_curswant = curwin->w_virtcol;
483 	curwin->w_set_curswant = FALSE;
484     }
485 }
486 
487 /*
488  * Check if the cursor has moved.  Set the w_valid flag accordingly.
489  */
490     void
check_cursor_moved(win_T * wp)491 check_cursor_moved(win_T *wp)
492 {
493     if (wp->w_cursor.lnum != wp->w_valid_cursor.lnum)
494     {
495 	wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL
496 				     |VALID_CHEIGHT|VALID_CROW|VALID_TOPLINE);
497 	wp->w_valid_cursor = wp->w_cursor;
498 	wp->w_valid_leftcol = wp->w_leftcol;
499     }
500     else if (wp->w_cursor.col != wp->w_valid_cursor.col
501 	     || wp->w_leftcol != wp->w_valid_leftcol
502 	     || wp->w_cursor.coladd != wp->w_valid_cursor.coladd)
503     {
504 	wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL);
505 	wp->w_valid_cursor.col = wp->w_cursor.col;
506 	wp->w_valid_leftcol = wp->w_leftcol;
507 	wp->w_valid_cursor.coladd = wp->w_cursor.coladd;
508     }
509 }
510 
511 /*
512  * Call this function when some window settings have changed, which require
513  * the cursor position, botline and topline to be recomputed and the window to
514  * be redrawn.  E.g, when changing the 'wrap' option or folding.
515  */
516     void
changed_window_setting(void)517 changed_window_setting(void)
518 {
519     changed_window_setting_win(curwin);
520 }
521 
522     void
changed_window_setting_win(win_T * wp)523 changed_window_setting_win(win_T *wp)
524 {
525     wp->w_lines_valid = 0;
526     changed_line_abv_curs_win(wp);
527     wp->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP|VALID_TOPLINE);
528     redraw_win_later(wp, NOT_VALID);
529 }
530 
531 /*
532  * Set wp->w_topline to a certain number.
533  */
534     void
set_topline(win_T * wp,linenr_T lnum)535 set_topline(win_T *wp, linenr_T lnum)
536 {
537 #ifdef FEAT_DIFF
538     linenr_T prev_topline = wp->w_topline;
539 #endif
540 
541 #ifdef FEAT_FOLDING
542     // go to first of folded lines
543     (void)hasFoldingWin(wp, lnum, &lnum, NULL, TRUE, NULL);
544 #endif
545     // Approximate the value of w_botline
546     wp->w_botline += lnum - wp->w_topline;
547     if (wp->w_botline > wp->w_buffer->b_ml.ml_line_count + 1)
548 	wp->w_botline = wp->w_buffer->b_ml.ml_line_count + 1;
549     wp->w_topline = lnum;
550     wp->w_topline_was_set = TRUE;
551 #ifdef FEAT_DIFF
552     if (lnum != prev_topline)
553 	// Keep the filler lines when the topline didn't change.
554 	wp->w_topfill = 0;
555 #endif
556     wp->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_TOPLINE);
557     // Don't set VALID_TOPLINE here, 'scrolloff' needs to be checked.
558     redraw_later(VALID);
559 }
560 
561 /*
562  * Call this function when the length of the cursor line (in screen
563  * characters) has changed, and the change is before the cursor.
564  * Need to take care of w_botline separately!
565  */
566     void
changed_cline_bef_curs(void)567 changed_cline_bef_curs(void)
568 {
569     curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL
570 						|VALID_CHEIGHT|VALID_TOPLINE);
571 }
572 
573     void
changed_cline_bef_curs_win(win_T * wp)574 changed_cline_bef_curs_win(win_T *wp)
575 {
576     wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL
577 						|VALID_CHEIGHT|VALID_TOPLINE);
578 }
579 
580 /*
581  * Call this function when the length of a line (in screen characters) above
582  * the cursor have changed.
583  * Need to take care of w_botline separately!
584  */
585     void
changed_line_abv_curs(void)586 changed_line_abv_curs(void)
587 {
588     curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL|VALID_CROW
589 						|VALID_CHEIGHT|VALID_TOPLINE);
590 }
591 
592     void
changed_line_abv_curs_win(win_T * wp)593 changed_line_abv_curs_win(win_T *wp)
594 {
595     wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL|VALID_CROW
596 						|VALID_CHEIGHT|VALID_TOPLINE);
597 }
598 
599 /*
600  * Make sure the value of curwin->w_botline is valid.
601  */
602     void
validate_botline(void)603 validate_botline(void)
604 {
605     validate_botline_win(curwin);
606 }
607 
608 /*
609  * Make sure the value of wp->w_botline is valid.
610  */
611     void
validate_botline_win(win_T * wp)612 validate_botline_win(win_T *wp)
613 {
614     if (!(wp->w_valid & VALID_BOTLINE))
615 	comp_botline(wp);
616 }
617 
618 /*
619  * Mark curwin->w_botline as invalid (because of some change in the buffer).
620  */
621     void
invalidate_botline(void)622 invalidate_botline(void)
623 {
624     curwin->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP);
625 }
626 
627     void
invalidate_botline_win(win_T * wp)628 invalidate_botline_win(win_T *wp)
629 {
630     wp->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP);
631 }
632 
633     void
approximate_botline_win(win_T * wp)634 approximate_botline_win(
635     win_T	*wp)
636 {
637     wp->w_valid &= ~VALID_BOTLINE;
638 }
639 
640 /*
641  * Return TRUE if curwin->w_wrow and curwin->w_wcol are valid.
642  */
643     int
cursor_valid(void)644 cursor_valid(void)
645 {
646     check_cursor_moved(curwin);
647     return ((curwin->w_valid & (VALID_WROW|VALID_WCOL)) ==
648 						      (VALID_WROW|VALID_WCOL));
649 }
650 
651 /*
652  * Validate cursor position.  Makes sure w_wrow and w_wcol are valid.
653  * w_topline must be valid, you may need to call update_topline() first!
654  */
655     void
validate_cursor(void)656 validate_cursor(void)
657 {
658     check_cursor_moved(curwin);
659     if ((curwin->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW))
660 	curs_columns(TRUE);
661 }
662 
663 #if defined(FEAT_GUI) || defined(PROTO)
664 /*
665  * validate w_cline_row.
666  */
667     void
validate_cline_row(void)668 validate_cline_row(void)
669 {
670     /*
671      * First make sure that w_topline is valid (after moving the cursor).
672      */
673     update_topline();
674     check_cursor_moved(curwin);
675     if (!(curwin->w_valid & VALID_CROW))
676 	curs_rows(curwin);
677 }
678 #endif
679 
680 /*
681  * Compute wp->w_cline_row and wp->w_cline_height, based on the current value
682  * of wp->w_topline.
683  */
684     static void
curs_rows(win_T * wp)685 curs_rows(win_T *wp)
686 {
687     linenr_T	lnum;
688     int		i;
689     int		all_invalid;
690     int		valid;
691 #ifdef FEAT_FOLDING
692     long	fold_count;
693 #endif
694 
695     // Check if wp->w_lines[].wl_size is invalid
696     all_invalid = (!redrawing()
697 			|| wp->w_lines_valid == 0
698 			|| wp->w_lines[0].wl_lnum > wp->w_topline);
699     i = 0;
700     wp->w_cline_row = 0;
701     for (lnum = wp->w_topline; lnum < wp->w_cursor.lnum; ++i)
702     {
703 	valid = FALSE;
704 	if (!all_invalid && i < wp->w_lines_valid)
705 	{
706 	    if (wp->w_lines[i].wl_lnum < lnum || !wp->w_lines[i].wl_valid)
707 		continue;		// skip changed or deleted lines
708 	    if (wp->w_lines[i].wl_lnum == lnum)
709 	    {
710 #ifdef FEAT_FOLDING
711 		// Check for newly inserted lines below this row, in which
712 		// case we need to check for folded lines.
713 		if (!wp->w_buffer->b_mod_set
714 			|| wp->w_lines[i].wl_lastlnum < wp->w_cursor.lnum
715 			|| wp->w_buffer->b_mod_top
716 					     > wp->w_lines[i].wl_lastlnum + 1)
717 #endif
718 		valid = TRUE;
719 	    }
720 	    else if (wp->w_lines[i].wl_lnum > lnum)
721 		--i;			// hold at inserted lines
722 	}
723 	if (valid
724 #ifdef FEAT_DIFF
725 		&& (lnum != wp->w_topline || !wp->w_p_diff)
726 #endif
727 		)
728 	{
729 #ifdef FEAT_FOLDING
730 	    lnum = wp->w_lines[i].wl_lastlnum + 1;
731 	    // Cursor inside folded lines, don't count this row
732 	    if (lnum > wp->w_cursor.lnum)
733 		break;
734 #else
735 	    ++lnum;
736 #endif
737 	    wp->w_cline_row += wp->w_lines[i].wl_size;
738 	}
739 	else
740 	{
741 #ifdef FEAT_FOLDING
742 	    fold_count = foldedCount(wp, lnum, NULL);
743 	    if (fold_count)
744 	    {
745 		lnum += fold_count;
746 		if (lnum > wp->w_cursor.lnum)
747 		    break;
748 		++wp->w_cline_row;
749 	    }
750 	    else
751 #endif
752 #ifdef FEAT_DIFF
753 		if (lnum == wp->w_topline)
754 		    wp->w_cline_row += plines_win_nofill(wp, lnum++, TRUE)
755 							      + wp->w_topfill;
756 		else
757 #endif
758 		    wp->w_cline_row += plines_win(wp, lnum++, TRUE);
759 	}
760     }
761 
762     check_cursor_moved(wp);
763     if (!(wp->w_valid & VALID_CHEIGHT))
764     {
765 	if (all_invalid
766 		|| i == wp->w_lines_valid
767 		|| (i < wp->w_lines_valid
768 		    && (!wp->w_lines[i].wl_valid
769 			|| wp->w_lines[i].wl_lnum != wp->w_cursor.lnum)))
770 	{
771 #ifdef FEAT_DIFF
772 	    if (wp->w_cursor.lnum == wp->w_topline)
773 		wp->w_cline_height = plines_win_nofill(wp, wp->w_cursor.lnum,
774 							TRUE) + wp->w_topfill;
775 	    else
776 #endif
777 		wp->w_cline_height = plines_win(wp, wp->w_cursor.lnum, TRUE);
778 #ifdef FEAT_FOLDING
779 	    wp->w_cline_folded = hasFoldingWin(wp, wp->w_cursor.lnum,
780 						      NULL, NULL, TRUE, NULL);
781 #endif
782 	}
783 	else if (i > wp->w_lines_valid)
784 	{
785 	    // a line that is too long to fit on the last screen line
786 	    wp->w_cline_height = 0;
787 #ifdef FEAT_FOLDING
788 	    wp->w_cline_folded = hasFoldingWin(wp, wp->w_cursor.lnum,
789 						      NULL, NULL, TRUE, NULL);
790 #endif
791 	}
792 	else
793 	{
794 	    wp->w_cline_height = wp->w_lines[i].wl_size;
795 #ifdef FEAT_FOLDING
796 	    wp->w_cline_folded = wp->w_lines[i].wl_folded;
797 #endif
798 	}
799     }
800 
801     redraw_for_cursorline(curwin);
802     wp->w_valid |= VALID_CROW|VALID_CHEIGHT;
803 
804 }
805 
806 /*
807  * Validate curwin->w_virtcol only.
808  */
809     void
validate_virtcol(void)810 validate_virtcol(void)
811 {
812     validate_virtcol_win(curwin);
813 }
814 
815 /*
816  * Validate wp->w_virtcol only.
817  */
818     void
validate_virtcol_win(win_T * wp)819 validate_virtcol_win(win_T *wp)
820 {
821     check_cursor_moved(wp);
822     if (!(wp->w_valid & VALID_VIRTCOL))
823     {
824 	getvvcol(wp, &wp->w_cursor, NULL, &(wp->w_virtcol), NULL);
825 	wp->w_valid |= VALID_VIRTCOL;
826 #ifdef FEAT_SYN_HL
827 	if (wp->w_p_cuc && !pum_visible())
828 	    redraw_win_later(wp, SOME_VALID);
829 #endif
830     }
831 }
832 
833 /*
834  * Validate curwin->w_cline_height only.
835  */
836     void
validate_cheight(void)837 validate_cheight(void)
838 {
839     check_cursor_moved(curwin);
840     if (!(curwin->w_valid & VALID_CHEIGHT))
841     {
842 #ifdef FEAT_DIFF
843 	if (curwin->w_cursor.lnum == curwin->w_topline)
844 	    curwin->w_cline_height = plines_nofill(curwin->w_cursor.lnum)
845 							  + curwin->w_topfill;
846 	else
847 #endif
848 	    curwin->w_cline_height = plines(curwin->w_cursor.lnum);
849 #ifdef FEAT_FOLDING
850 	curwin->w_cline_folded = hasFolding(curwin->w_cursor.lnum, NULL, NULL);
851 #endif
852 	curwin->w_valid |= VALID_CHEIGHT;
853     }
854 }
855 
856 /*
857  * Validate w_wcol and w_virtcol only.
858  */
859     void
validate_cursor_col(void)860 validate_cursor_col(void)
861 {
862     colnr_T off;
863     colnr_T col;
864     int     width;
865 
866     validate_virtcol();
867     if (!(curwin->w_valid & VALID_WCOL))
868     {
869 	col = curwin->w_virtcol;
870 	off = curwin_col_off();
871 	col += off;
872 	width = curwin->w_width - off + curwin_col_off2();
873 
874 	// long line wrapping, adjust curwin->w_wrow
875 	if (curwin->w_p_wrap
876 		&& col >= (colnr_T)curwin->w_width
877 		&& width > 0)
878 	    // use same formula as what is used in curs_columns()
879 	    col -= ((col - curwin->w_width) / width + 1) * width;
880 	if (col > (int)curwin->w_leftcol)
881 	    col -= curwin->w_leftcol;
882 	else
883 	    col = 0;
884 	curwin->w_wcol = col;
885 
886 	curwin->w_valid |= VALID_WCOL;
887 #ifdef FEAT_PROP_POPUP
888 	curwin->w_flags &= ~WFLAG_WCOL_OFF_ADDED;
889 #endif
890     }
891 }
892 
893 /*
894  * Compute offset of a window, occupied by absolute or relative line number,
895  * fold column and sign column (these don't move when scrolling horizontally).
896  */
897     int
win_col_off(win_T * wp)898 win_col_off(win_T *wp)
899 {
900     return (((wp->w_p_nu || wp->w_p_rnu) ? number_width(wp) + 1 : 0)
901 #ifdef FEAT_CMDWIN
902 	    + (cmdwin_type == 0 || wp != curwin ? 0 : 1)
903 #endif
904 #ifdef FEAT_FOLDING
905 	    + wp->w_p_fdc
906 #endif
907 #ifdef FEAT_SIGNS
908 	    + (signcolumn_on(wp) ? 2 : 0)
909 #endif
910 	   );
911 }
912 
913     int
curwin_col_off(void)914 curwin_col_off(void)
915 {
916     return win_col_off(curwin);
917 }
918 
919 /*
920  * Return the difference in column offset for the second screen line of a
921  * wrapped line.  It's 8 if 'number' or 'relativenumber' is on and 'n' is in
922  * 'cpoptions'.
923  */
924     int
win_col_off2(win_T * wp)925 win_col_off2(win_T *wp)
926 {
927     if ((wp->w_p_nu || wp->w_p_rnu) && vim_strchr(p_cpo, CPO_NUMCOL) != NULL)
928 	return number_width(wp) + 1;
929     return 0;
930 }
931 
932     int
curwin_col_off2(void)933 curwin_col_off2(void)
934 {
935     return win_col_off2(curwin);
936 }
937 
938 /*
939  * Compute curwin->w_wcol and curwin->w_virtcol.
940  * Also updates curwin->w_wrow and curwin->w_cline_row.
941  * Also updates curwin->w_leftcol.
942  */
943     void
curs_columns(int may_scroll)944 curs_columns(
945     int		may_scroll)	// when TRUE, may scroll horizontally
946 {
947     int		diff;
948     int		extra;		// offset for first screen line
949     int		off_left, off_right;
950     int		n;
951     int		p_lines;
952     int		width = 0;
953     int		textwidth;
954     int		new_leftcol;
955     colnr_T	startcol;
956     colnr_T	endcol;
957     colnr_T	prev_skipcol;
958     long        so = get_scrolloff_value();
959     long        siso = get_sidescrolloff_value();
960 
961     /*
962      * First make sure that w_topline is valid (after moving the cursor).
963      */
964     update_topline();
965 
966     /*
967      * Next make sure that w_cline_row is valid.
968      */
969     if (!(curwin->w_valid & VALID_CROW))
970 	curs_rows(curwin);
971 
972     /*
973      * Compute the number of virtual columns.
974      */
975 #ifdef FEAT_FOLDING
976     if (curwin->w_cline_folded)
977 	// In a folded line the cursor is always in the first column
978 	startcol = curwin->w_virtcol = endcol = curwin->w_leftcol;
979     else
980 #endif
981 	getvvcol(curwin, &curwin->w_cursor,
982 				&startcol, &(curwin->w_virtcol), &endcol);
983 
984     // remove '$' from change command when cursor moves onto it
985     if (startcol > dollar_vcol)
986 	dollar_vcol = -1;
987 
988     extra = curwin_col_off();
989     curwin->w_wcol = curwin->w_virtcol + extra;
990     endcol += extra;
991 
992     /*
993      * Now compute w_wrow, counting screen lines from w_cline_row.
994      */
995     curwin->w_wrow = curwin->w_cline_row;
996 
997     textwidth = curwin->w_width - extra;
998     if (textwidth <= 0)
999     {
1000 	// No room for text, put cursor in last char of window.
1001 	// If not wrapping, the last non-empty line.
1002 	curwin->w_wcol = curwin->w_width - 1;
1003 	if (curwin->w_p_wrap)
1004 	    curwin->w_wrow = curwin->w_height - 1;
1005 	else
1006 	    curwin->w_wrow = curwin->w_height - 1 - curwin->w_empty_rows;
1007     }
1008     else if (curwin->w_p_wrap && curwin->w_width != 0)
1009     {
1010 	width = textwidth + curwin_col_off2();
1011 
1012 	// long line wrapping, adjust curwin->w_wrow
1013 	if (curwin->w_wcol >= curwin->w_width)
1014 	{
1015 #ifdef FEAT_LINEBREAK
1016 	    char_u *sbr;
1017 #endif
1018 
1019 	    // this same formula is used in validate_cursor_col()
1020 	    n = (curwin->w_wcol - curwin->w_width) / width + 1;
1021 	    curwin->w_wcol -= n * width;
1022 	    curwin->w_wrow += n;
1023 
1024 #ifdef FEAT_LINEBREAK
1025 	    // When cursor wraps to first char of next line in Insert
1026 	    // mode, the 'showbreak' string isn't shown, backup to first
1027 	    // column
1028 	    sbr = get_showbreak_value(curwin);
1029 	    if (*sbr && *ml_get_cursor() == NUL
1030 				    && curwin->w_wcol == vim_strsize(sbr))
1031 		curwin->w_wcol = 0;
1032 #endif
1033 	}
1034     }
1035 
1036     // No line wrapping: compute curwin->w_leftcol if scrolling is on and line
1037     // is not folded.
1038     // If scrolling is off, curwin->w_leftcol is assumed to be 0
1039     else if (may_scroll
1040 #ifdef FEAT_FOLDING
1041 	    && !curwin->w_cline_folded
1042 #endif
1043 	    )
1044     {
1045 	/*
1046 	 * If Cursor is left of the screen, scroll rightwards.
1047 	 * If Cursor is right of the screen, scroll leftwards
1048 	 * If we get closer to the edge than 'sidescrolloff', scroll a little
1049 	 * extra
1050 	 */
1051 	off_left = (int)startcol - (int)curwin->w_leftcol - siso;
1052 	off_right = (int)endcol - (int)(curwin->w_leftcol + curwin->w_width
1053 								- siso) + 1;
1054 	if (off_left < 0 || off_right > 0)
1055 	{
1056 	    if (off_left < 0)
1057 		diff = -off_left;
1058 	    else
1059 		diff = off_right;
1060 
1061 	    // When far off or not enough room on either side, put cursor in
1062 	    // middle of window.
1063 	    if (p_ss == 0 || diff >= textwidth / 2 || off_right >= off_left)
1064 		new_leftcol = curwin->w_wcol - extra - textwidth / 2;
1065 	    else
1066 	    {
1067 		if (diff < p_ss)
1068 		    diff = p_ss;
1069 		if (off_left < 0)
1070 		    new_leftcol = curwin->w_leftcol - diff;
1071 		else
1072 		    new_leftcol = curwin->w_leftcol + diff;
1073 	    }
1074 	    if (new_leftcol < 0)
1075 		new_leftcol = 0;
1076 	    if (new_leftcol != (int)curwin->w_leftcol)
1077 	    {
1078 		curwin->w_leftcol = new_leftcol;
1079 		// screen has to be redrawn with new curwin->w_leftcol
1080 		redraw_later(NOT_VALID);
1081 	    }
1082 	}
1083 	curwin->w_wcol -= curwin->w_leftcol;
1084     }
1085     else if (curwin->w_wcol > (int)curwin->w_leftcol)
1086 	curwin->w_wcol -= curwin->w_leftcol;
1087     else
1088 	curwin->w_wcol = 0;
1089 
1090 #ifdef FEAT_DIFF
1091     // Skip over filler lines.  At the top use w_topfill, there
1092     // may be some filler lines above the window.
1093     if (curwin->w_cursor.lnum == curwin->w_topline)
1094 	curwin->w_wrow += curwin->w_topfill;
1095     else
1096 	curwin->w_wrow += diff_check_fill(curwin, curwin->w_cursor.lnum);
1097 #endif
1098 
1099     prev_skipcol = curwin->w_skipcol;
1100 
1101     p_lines = 0;
1102 
1103     if ((curwin->w_wrow >= curwin->w_height
1104 		|| ((prev_skipcol > 0
1105 			|| curwin->w_wrow + so >= curwin->w_height)
1106 		    && (p_lines =
1107 #ifdef FEAT_DIFF
1108 			plines_win_nofill
1109 #else
1110 			plines_win
1111 #endif
1112 			(curwin, curwin->w_cursor.lnum, FALSE))
1113 						    - 1 >= curwin->w_height))
1114 	    && curwin->w_height != 0
1115 	    && curwin->w_cursor.lnum == curwin->w_topline
1116 	    && width > 0
1117 	    && curwin->w_width != 0)
1118     {
1119 	// Cursor past end of screen.  Happens with a single line that does
1120 	// not fit on screen.  Find a skipcol to show the text around the
1121 	// cursor.  Avoid scrolling all the time. compute value of "extra":
1122 	// 1: Less than 'scrolloff' lines above
1123 	// 2: Less than 'scrolloff' lines below
1124 	// 3: both of them
1125 	extra = 0;
1126 	if (curwin->w_skipcol + so * width > curwin->w_virtcol)
1127 	    extra = 1;
1128 	// Compute last display line of the buffer line that we want at the
1129 	// bottom of the window.
1130 	if (p_lines == 0)
1131 	    p_lines = plines_win(curwin, curwin->w_cursor.lnum, FALSE);
1132 	--p_lines;
1133 	if (p_lines > curwin->w_wrow + so)
1134 	    n = curwin->w_wrow + so;
1135 	else
1136 	    n = p_lines;
1137 	if ((colnr_T)n >= curwin->w_height + curwin->w_skipcol / width - so)
1138 	    extra += 2;
1139 
1140 	if (extra == 3 || p_lines <= so * 2)
1141 	{
1142 	    // not enough room for 'scrolloff', put cursor in the middle
1143 	    n = curwin->w_virtcol / width;
1144 	    if (n > curwin->w_height / 2)
1145 		n -= curwin->w_height / 2;
1146 	    else
1147 		n = 0;
1148 	    // don't skip more than necessary
1149 	    if (n > p_lines - curwin->w_height + 1)
1150 		n = p_lines - curwin->w_height + 1;
1151 	    curwin->w_skipcol = n * width;
1152 	}
1153 	else if (extra == 1)
1154 	{
1155 	    // less then 'scrolloff' lines above, decrease skipcol
1156 	    extra = (curwin->w_skipcol + so * width - curwin->w_virtcol
1157 				     + width - 1) / width;
1158 	    if (extra > 0)
1159 	    {
1160 		if ((colnr_T)(extra * width) > curwin->w_skipcol)
1161 		    extra = curwin->w_skipcol / width;
1162 		curwin->w_skipcol -= extra * width;
1163 	    }
1164 	}
1165 	else if (extra == 2)
1166 	{
1167 	    // less then 'scrolloff' lines below, increase skipcol
1168 	    endcol = (n - curwin->w_height + 1) * width;
1169 	    while (endcol > curwin->w_virtcol)
1170 		endcol -= width;
1171 	    if (endcol > curwin->w_skipcol)
1172 		curwin->w_skipcol = endcol;
1173 	}
1174 
1175 	curwin->w_wrow -= curwin->w_skipcol / width;
1176 	if (curwin->w_wrow >= curwin->w_height)
1177 	{
1178 	    // small window, make sure cursor is in it
1179 	    extra = curwin->w_wrow - curwin->w_height + 1;
1180 	    curwin->w_skipcol += extra * width;
1181 	    curwin->w_wrow -= extra;
1182 	}
1183 
1184 	extra = ((int)prev_skipcol - (int)curwin->w_skipcol) / width;
1185 	if (extra > 0)
1186 	    win_ins_lines(curwin, 0, extra, FALSE, FALSE);
1187 	else if (extra < 0)
1188 	    win_del_lines(curwin, 0, -extra, FALSE, FALSE, 0);
1189     }
1190     else
1191 	curwin->w_skipcol = 0;
1192     if (prev_skipcol != curwin->w_skipcol)
1193 	redraw_later(NOT_VALID);
1194 
1195 #ifdef FEAT_SYN_HL
1196     // Redraw when w_virtcol changes and 'cursorcolumn' is set
1197     if (curwin->w_p_cuc && (curwin->w_valid & VALID_VIRTCOL) == 0
1198 	    && !pum_visible())
1199 	redraw_later(SOME_VALID);
1200 #endif
1201 #if defined(FEAT_PROP_POPUP) && defined(FEAT_TERMINAL)
1202     if (popup_is_popup(curwin) && curbuf->b_term != NULL)
1203     {
1204 	curwin->w_wrow += popup_top_extra(curwin);
1205 	curwin->w_wcol += popup_left_extra(curwin);
1206 	curwin->w_flags |= WFLAG_WCOL_OFF_ADDED + WFLAG_WROW_OFF_ADDED;
1207     }
1208     else
1209 	curwin->w_flags &= ~(WFLAG_WCOL_OFF_ADDED + WFLAG_WROW_OFF_ADDED);
1210 #endif
1211 
1212     // now w_leftcol is valid, avoid check_cursor_moved() thinking otherwise
1213     curwin->w_valid_leftcol = curwin->w_leftcol;
1214 
1215     curwin->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL;
1216 }
1217 
1218 #if (defined(FEAT_EVAL) || defined(FEAT_PROP_POPUP)) || defined(PROTO)
1219 /*
1220  * Compute the screen position of text character at "pos" in window "wp"
1221  * The resulting values are one-based, zero when character is not visible.
1222  */
1223     void
textpos2screenpos(win_T * wp,pos_T * pos,int * rowp,int * scolp,int * ccolp,int * ecolp)1224 textpos2screenpos(
1225 	win_T	*wp,
1226 	pos_T	*pos,
1227 	int	*rowp,	// screen row
1228 	int	*scolp,	// start screen column
1229 	int	*ccolp,	// cursor screen column
1230 	int	*ecolp)	// end screen column
1231 {
1232     colnr_T	scol = 0, ccol = 0, ecol = 0;
1233     int		row = 0;
1234     int		rowoff = 0;
1235     colnr_T	coloff = 0;
1236 
1237     if (pos->lnum >= wp->w_topline && pos->lnum <= wp->w_botline)
1238     {
1239 	colnr_T off;
1240 	colnr_T col;
1241 	int     width;
1242 
1243 	row = plines_m_win(wp, wp->w_topline, pos->lnum - 1) + 1;
1244 	getvcol(wp, pos, &scol, &ccol, &ecol);
1245 
1246 	// similar to what is done in validate_cursor_col()
1247 	col = scol;
1248 	off = win_col_off(wp);
1249 	col += off;
1250 	width = wp->w_width - off + win_col_off2(wp);
1251 
1252 	// long line wrapping, adjust row
1253 	if (wp->w_p_wrap
1254 		&& col >= (colnr_T)wp->w_width
1255 		&& width > 0)
1256 	{
1257 	    // use same formula as what is used in curs_columns()
1258 	    rowoff = ((col - wp->w_width) / width + 1);
1259 	    col -= rowoff * width;
1260 	}
1261 	col -= wp->w_leftcol;
1262 	if (col >= wp->w_width)
1263 	    col = -1;
1264 	if (col >= 0 && row + rowoff <= wp->w_height)
1265 	    coloff = col - scol + wp->w_wincol + 1;
1266 	else
1267 	    // character is left, right or below of the window
1268 	    row = rowoff = scol = ccol = ecol = 0;
1269     }
1270     *rowp = W_WINROW(wp) + row + rowoff;
1271     *scolp = scol + coloff;
1272     *ccolp = ccol + coloff;
1273     *ecolp = ecol + coloff;
1274 }
1275 #endif
1276 
1277 #if defined(FEAT_EVAL) || defined(PROTO)
1278 /*
1279  * "screenpos({winid}, {lnum}, {col})" function
1280  */
1281     void
f_screenpos(typval_T * argvars UNUSED,typval_T * rettv)1282 f_screenpos(typval_T *argvars UNUSED, typval_T *rettv)
1283 {
1284     dict_T	*dict;
1285     win_T	*wp;
1286     pos_T	pos;
1287     int		row = 0;
1288     int		scol = 0, ccol = 0, ecol = 0;
1289 
1290     if (rettv_dict_alloc(rettv) != OK)
1291 	return;
1292     dict = rettv->vval.v_dict;
1293 
1294     if (in_vim9script()
1295 	    && (check_for_number_arg(argvars, 0) == FAIL
1296 		|| check_for_number_arg(argvars, 1) == FAIL
1297 		|| check_for_number_arg(argvars, 2) == FAIL))
1298 	return;
1299 
1300     wp = find_win_by_nr_or_id(&argvars[0]);
1301     if (wp == NULL)
1302 	return;
1303 
1304     pos.lnum = tv_get_number(&argvars[1]);
1305     pos.col = tv_get_number(&argvars[2]) - 1;
1306     pos.coladd = 0;
1307     textpos2screenpos(wp, &pos, &row, &scol, &ccol, &ecol);
1308 
1309     dict_add_number(dict, "row", row);
1310     dict_add_number(dict, "col", scol);
1311     dict_add_number(dict, "curscol", ccol);
1312     dict_add_number(dict, "endcol", ecol);
1313 }
1314 #endif
1315 
1316 /*
1317  * Scroll the current window down by "line_count" logical lines.  "CTRL-Y"
1318  */
1319     void
scrolldown(long line_count,int byfold UNUSED)1320 scrolldown(
1321     long	line_count,
1322     int		byfold UNUSED)	// TRUE: count a closed fold as one line
1323 {
1324     long	done = 0;	// total # of physical lines done
1325     int		wrow;
1326     int		moved = FALSE;
1327 
1328 #ifdef FEAT_FOLDING
1329     linenr_T	first;
1330 
1331     // Make sure w_topline is at the first of a sequence of folded lines.
1332     (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
1333 #endif
1334     validate_cursor();		// w_wrow needs to be valid
1335     while (line_count-- > 0)
1336     {
1337 #ifdef FEAT_DIFF
1338 	if (curwin->w_topfill < diff_check(curwin, curwin->w_topline)
1339 		&& curwin->w_topfill < curwin->w_height - 1)
1340 	{
1341 	    ++curwin->w_topfill;
1342 	    ++done;
1343 	}
1344 	else
1345 #endif
1346 	{
1347 	    if (curwin->w_topline == 1)
1348 		break;
1349 	    --curwin->w_topline;
1350 #ifdef FEAT_DIFF
1351 	    curwin->w_topfill = 0;
1352 #endif
1353 #ifdef FEAT_FOLDING
1354 	    // A sequence of folded lines only counts for one logical line
1355 	    if (hasFolding(curwin->w_topline, &first, NULL))
1356 	    {
1357 		++done;
1358 		if (!byfold)
1359 		    line_count -= curwin->w_topline - first - 1;
1360 		curwin->w_botline -= curwin->w_topline - first;
1361 		curwin->w_topline = first;
1362 	    }
1363 	    else
1364 #endif
1365 		done += PLINES_NOFILL(curwin->w_topline);
1366 	}
1367 	--curwin->w_botline;		// approximate w_botline
1368 	invalidate_botline();
1369     }
1370     curwin->w_wrow += done;		// keep w_wrow updated
1371     curwin->w_cline_row += done;	// keep w_cline_row updated
1372 
1373 #ifdef FEAT_DIFF
1374     if (curwin->w_cursor.lnum == curwin->w_topline)
1375 	curwin->w_cline_row = 0;
1376     check_topfill(curwin, TRUE);
1377 #endif
1378 
1379     /*
1380      * Compute the row number of the last row of the cursor line
1381      * and move the cursor onto the displayed part of the window.
1382      */
1383     wrow = curwin->w_wrow;
1384     if (curwin->w_p_wrap && curwin->w_width != 0)
1385     {
1386 	validate_virtcol();
1387 	validate_cheight();
1388 	wrow += curwin->w_cline_height - 1 -
1389 	    curwin->w_virtcol / curwin->w_width;
1390     }
1391     while (wrow >= curwin->w_height && curwin->w_cursor.lnum > 1)
1392     {
1393 #ifdef FEAT_FOLDING
1394 	if (hasFolding(curwin->w_cursor.lnum, &first, NULL))
1395 	{
1396 	    --wrow;
1397 	    if (first == 1)
1398 		curwin->w_cursor.lnum = 1;
1399 	    else
1400 		curwin->w_cursor.lnum = first - 1;
1401 	}
1402 	else
1403 #endif
1404 	    wrow -= plines(curwin->w_cursor.lnum--);
1405 	curwin->w_valid &=
1406 	      ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL);
1407 	moved = TRUE;
1408     }
1409     if (moved)
1410     {
1411 #ifdef FEAT_FOLDING
1412 	// Move cursor to first line of closed fold.
1413 	foldAdjustCursor();
1414 #endif
1415 	coladvance(curwin->w_curswant);
1416     }
1417 }
1418 
1419 /*
1420  * Scroll the current window up by "line_count" logical lines.  "CTRL-E"
1421  */
1422     void
scrollup(long line_count,int byfold UNUSED)1423 scrollup(
1424     long	line_count,
1425     int		byfold UNUSED)	// TRUE: count a closed fold as one line
1426 {
1427 #if defined(FEAT_FOLDING) || defined(FEAT_DIFF)
1428     linenr_T	lnum;
1429 
1430     if (
1431 # ifdef FEAT_FOLDING
1432 	    (byfold && hasAnyFolding(curwin))
1433 #  ifdef FEAT_DIFF
1434 	    ||
1435 #  endif
1436 # endif
1437 # ifdef FEAT_DIFF
1438 	    curwin->w_p_diff
1439 # endif
1440 	    )
1441     {
1442 	// count each sequence of folded lines as one logical line
1443 	lnum = curwin->w_topline;
1444 	while (line_count--)
1445 	{
1446 # ifdef FEAT_DIFF
1447 	    if (curwin->w_topfill > 0)
1448 		--curwin->w_topfill;
1449 	    else
1450 # endif
1451 	    {
1452 # ifdef FEAT_FOLDING
1453 		if (byfold)
1454 		    (void)hasFolding(lnum, NULL, &lnum);
1455 # endif
1456 		if (lnum >= curbuf->b_ml.ml_line_count)
1457 		    break;
1458 		++lnum;
1459 # ifdef FEAT_DIFF
1460 		curwin->w_topfill = diff_check_fill(curwin, lnum);
1461 # endif
1462 	    }
1463 	}
1464 	// approximate w_botline
1465 	curwin->w_botline += lnum - curwin->w_topline;
1466 	curwin->w_topline = lnum;
1467     }
1468     else
1469 #endif
1470     {
1471 	curwin->w_topline += line_count;
1472 	curwin->w_botline += line_count;	// approximate w_botline
1473     }
1474 
1475     if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1476 	curwin->w_topline = curbuf->b_ml.ml_line_count;
1477     if (curwin->w_botline > curbuf->b_ml.ml_line_count + 1)
1478 	curwin->w_botline = curbuf->b_ml.ml_line_count + 1;
1479 
1480 #ifdef FEAT_DIFF
1481     check_topfill(curwin, FALSE);
1482 #endif
1483 
1484 #ifdef FEAT_FOLDING
1485     if (hasAnyFolding(curwin))
1486 	// Make sure w_topline is at the first of a sequence of folded lines.
1487 	(void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
1488 #endif
1489 
1490     curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
1491     if (curwin->w_cursor.lnum < curwin->w_topline)
1492     {
1493 	curwin->w_cursor.lnum = curwin->w_topline;
1494 	curwin->w_valid &=
1495 	      ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL);
1496 	coladvance(curwin->w_curswant);
1497     }
1498 }
1499 
1500 #ifdef FEAT_DIFF
1501 /*
1502  * Don't end up with too many filler lines in the window.
1503  */
1504     void
check_topfill(win_T * wp,int down)1505 check_topfill(
1506     win_T	*wp,
1507     int		down)	// when TRUE scroll down when not enough space
1508 {
1509     int		n;
1510 
1511     if (wp->w_topfill > 0)
1512     {
1513 	n = plines_win_nofill(wp, wp->w_topline, TRUE);
1514 	if (wp->w_topfill + n > wp->w_height)
1515 	{
1516 	    if (down && wp->w_topline > 1)
1517 	    {
1518 		--wp->w_topline;
1519 		wp->w_topfill = 0;
1520 	    }
1521 	    else
1522 	    {
1523 		wp->w_topfill = wp->w_height - n;
1524 		if (wp->w_topfill < 0)
1525 		    wp->w_topfill = 0;
1526 	    }
1527 	}
1528     }
1529 }
1530 
1531 /*
1532  * Use as many filler lines as possible for w_topline.  Make sure w_topline
1533  * is still visible.
1534  */
1535     static void
max_topfill(void)1536 max_topfill(void)
1537 {
1538     int		n;
1539 
1540     n = plines_nofill(curwin->w_topline);
1541     if (n >= curwin->w_height)
1542 	curwin->w_topfill = 0;
1543     else
1544     {
1545 	curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline);
1546 	if (curwin->w_topfill + n > curwin->w_height)
1547 	    curwin->w_topfill = curwin->w_height - n;
1548     }
1549 }
1550 #endif
1551 
1552 /*
1553  * Scroll the screen one line down, but don't do it if it would move the
1554  * cursor off the screen.
1555  */
1556     void
scrolldown_clamp(void)1557 scrolldown_clamp(void)
1558 {
1559     int		end_row;
1560 #ifdef FEAT_DIFF
1561     int		can_fill = (curwin->w_topfill
1562 				< diff_check_fill(curwin, curwin->w_topline));
1563 #endif
1564 
1565     if (curwin->w_topline <= 1
1566 #ifdef FEAT_DIFF
1567 	    && !can_fill
1568 #endif
1569 	    )
1570 	return;
1571 
1572     validate_cursor();	    // w_wrow needs to be valid
1573 
1574     /*
1575      * Compute the row number of the last row of the cursor line
1576      * and make sure it doesn't go off the screen. Make sure the cursor
1577      * doesn't go past 'scrolloff' lines from the screen end.
1578      */
1579     end_row = curwin->w_wrow;
1580 #ifdef FEAT_DIFF
1581     if (can_fill)
1582 	++end_row;
1583     else
1584 	end_row += plines_nofill(curwin->w_topline - 1);
1585 #else
1586     end_row += plines(curwin->w_topline - 1);
1587 #endif
1588     if (curwin->w_p_wrap && curwin->w_width != 0)
1589     {
1590 	validate_cheight();
1591 	validate_virtcol();
1592 	end_row += curwin->w_cline_height - 1 -
1593 	    curwin->w_virtcol / curwin->w_width;
1594     }
1595     if (end_row < curwin->w_height - get_scrolloff_value())
1596     {
1597 #ifdef FEAT_DIFF
1598 	if (can_fill)
1599 	{
1600 	    ++curwin->w_topfill;
1601 	    check_topfill(curwin, TRUE);
1602 	}
1603 	else
1604 	{
1605 	    --curwin->w_topline;
1606 	    curwin->w_topfill = 0;
1607 	}
1608 #else
1609 	--curwin->w_topline;
1610 #endif
1611 #ifdef FEAT_FOLDING
1612 	(void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
1613 #endif
1614 	--curwin->w_botline;	    // approximate w_botline
1615 	curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
1616     }
1617 }
1618 
1619 /*
1620  * Scroll the screen one line up, but don't do it if it would move the cursor
1621  * off the screen.
1622  */
1623     void
scrollup_clamp(void)1624 scrollup_clamp(void)
1625 {
1626     int	    start_row;
1627 
1628     if (curwin->w_topline == curbuf->b_ml.ml_line_count
1629 #ifdef FEAT_DIFF
1630 	    && curwin->w_topfill == 0
1631 #endif
1632 	    )
1633 	return;
1634 
1635     validate_cursor();	    // w_wrow needs to be valid
1636 
1637     /*
1638      * Compute the row number of the first row of the cursor line
1639      * and make sure it doesn't go off the screen. Make sure the cursor
1640      * doesn't go before 'scrolloff' lines from the screen start.
1641      */
1642 #ifdef FEAT_DIFF
1643     start_row = curwin->w_wrow - plines_nofill(curwin->w_topline)
1644 							  - curwin->w_topfill;
1645 #else
1646     start_row = curwin->w_wrow - plines(curwin->w_topline);
1647 #endif
1648     if (curwin->w_p_wrap && curwin->w_width != 0)
1649     {
1650 	validate_virtcol();
1651 	start_row -= curwin->w_virtcol / curwin->w_width;
1652     }
1653     if (start_row >= get_scrolloff_value())
1654     {
1655 #ifdef FEAT_DIFF
1656 	if (curwin->w_topfill > 0)
1657 	    --curwin->w_topfill;
1658 	else
1659 #endif
1660 	{
1661 #ifdef FEAT_FOLDING
1662 	    (void)hasFolding(curwin->w_topline, NULL, &curwin->w_topline);
1663 #endif
1664 	    ++curwin->w_topline;
1665 	}
1666 	++curwin->w_botline;		// approximate w_botline
1667 	curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
1668     }
1669 }
1670 
1671 /*
1672  * Add one line above "lp->lnum".  This can be a filler line, a closed fold or
1673  * a (wrapped) text line.  Uses and sets "lp->fill".
1674  * Returns the height of the added line in "lp->height".
1675  * Lines above the first one are incredibly high: MAXCOL.
1676  */
1677     static void
topline_back(lineoff_T * lp)1678 topline_back(lineoff_T *lp)
1679 {
1680 #ifdef FEAT_DIFF
1681     if (lp->fill < diff_check_fill(curwin, lp->lnum))
1682     {
1683 	// Add a filler line.
1684 	++lp->fill;
1685 	lp->height = 1;
1686     }
1687     else
1688 #endif
1689     {
1690 	--lp->lnum;
1691 #ifdef FEAT_DIFF
1692 	lp->fill = 0;
1693 #endif
1694 	if (lp->lnum < 1)
1695 	    lp->height = MAXCOL;
1696 	else
1697 #ifdef FEAT_FOLDING
1698 	    if (hasFolding(lp->lnum, &lp->lnum, NULL))
1699 	    // Add a closed fold
1700 	    lp->height = 1;
1701 	else
1702 #endif
1703 	    lp->height = PLINES_NOFILL(lp->lnum);
1704     }
1705 }
1706 
1707 /*
1708  * Add one line below "lp->lnum".  This can be a filler line, a closed fold or
1709  * a (wrapped) text line.  Uses and sets "lp->fill".
1710  * Returns the height of the added line in "lp->height".
1711  * Lines below the last one are incredibly high.
1712  */
1713     static void
botline_forw(lineoff_T * lp)1714 botline_forw(lineoff_T *lp)
1715 {
1716 #ifdef FEAT_DIFF
1717     if (lp->fill < diff_check_fill(curwin, lp->lnum + 1))
1718     {
1719 	// Add a filler line.
1720 	++lp->fill;
1721 	lp->height = 1;
1722     }
1723     else
1724 #endif
1725     {
1726 	++lp->lnum;
1727 #ifdef FEAT_DIFF
1728 	lp->fill = 0;
1729 #endif
1730 	if (lp->lnum > curbuf->b_ml.ml_line_count)
1731 	    lp->height = MAXCOL;
1732 	else
1733 #ifdef FEAT_FOLDING
1734 	    if (hasFolding(lp->lnum, NULL, &lp->lnum))
1735 	    // Add a closed fold
1736 	    lp->height = 1;
1737 	else
1738 #endif
1739 	    lp->height = PLINES_NOFILL(lp->lnum);
1740     }
1741 }
1742 
1743 #ifdef FEAT_DIFF
1744 /*
1745  * Switch from including filler lines below lp->lnum to including filler
1746  * lines above loff.lnum + 1.  This keeps pointing to the same line.
1747  * When there are no filler lines nothing changes.
1748  */
1749     static void
botline_topline(lineoff_T * lp)1750 botline_topline(lineoff_T *lp)
1751 {
1752     if (lp->fill > 0)
1753     {
1754 	++lp->lnum;
1755 	lp->fill = diff_check_fill(curwin, lp->lnum) - lp->fill + 1;
1756     }
1757 }
1758 
1759 /*
1760  * Switch from including filler lines above lp->lnum to including filler
1761  * lines below loff.lnum - 1.  This keeps pointing to the same line.
1762  * When there are no filler lines nothing changes.
1763  */
1764     static void
topline_botline(lineoff_T * lp)1765 topline_botline(lineoff_T *lp)
1766 {
1767     if (lp->fill > 0)
1768     {
1769 	lp->fill = diff_check_fill(curwin, lp->lnum) - lp->fill + 1;
1770 	--lp->lnum;
1771     }
1772 }
1773 #endif
1774 
1775 /*
1776  * Recompute topline to put the cursor at the top of the window.
1777  * Scroll at least "min_scroll" lines.
1778  * If "always" is TRUE, always set topline (for "zt").
1779  */
1780     void
scroll_cursor_top(int min_scroll,int always)1781 scroll_cursor_top(int min_scroll, int always)
1782 {
1783     int		scrolled = 0;
1784     int		extra = 0;
1785     int		used;
1786     int		i;
1787     linenr_T	top;		// just above displayed lines
1788     linenr_T	bot;		// just below displayed lines
1789     linenr_T	old_topline = curwin->w_topline;
1790 #ifdef FEAT_DIFF
1791     linenr_T	old_topfill = curwin->w_topfill;
1792 #endif
1793     linenr_T	new_topline;
1794     int		off = get_scrolloff_value();
1795 
1796     if (mouse_dragging > 0)
1797 	off = mouse_dragging - 1;
1798 
1799     /*
1800      * Decrease topline until:
1801      * - it has become 1
1802      * - (part of) the cursor line is moved off the screen or
1803      * - moved at least 'scrolljump' lines and
1804      * - at least 'scrolloff' lines above and below the cursor
1805      */
1806     validate_cheight();
1807     used = curwin->w_cline_height; // includes filler lines above
1808     if (curwin->w_cursor.lnum < curwin->w_topline)
1809 	scrolled = used;
1810 
1811 #ifdef FEAT_FOLDING
1812     if (hasFolding(curwin->w_cursor.lnum, &top, &bot))
1813     {
1814 	--top;
1815 	++bot;
1816     }
1817     else
1818 #endif
1819     {
1820 	top = curwin->w_cursor.lnum - 1;
1821 	bot = curwin->w_cursor.lnum + 1;
1822     }
1823     new_topline = top + 1;
1824 
1825 #ifdef FEAT_DIFF
1826     // "used" already contains the number of filler lines above, don't add it
1827     // again.
1828     // Hide filler lines above cursor line by adding them to "extra".
1829     extra += diff_check_fill(curwin, curwin->w_cursor.lnum);
1830 #endif
1831 
1832     /*
1833      * Check if the lines from "top" to "bot" fit in the window.  If they do,
1834      * set new_topline and advance "top" and "bot" to include more lines.
1835      */
1836     while (top > 0)
1837     {
1838 #ifdef FEAT_FOLDING
1839 	if (hasFolding(top, &top, NULL))
1840 	    // count one logical line for a sequence of folded lines
1841 	    i = 1;
1842 	else
1843 #endif
1844 	    i = PLINES_NOFILL(top);
1845 	used += i;
1846 	if (extra + i <= off && bot < curbuf->b_ml.ml_line_count)
1847 	{
1848 #ifdef FEAT_FOLDING
1849 	    if (hasFolding(bot, NULL, &bot))
1850 		// count one logical line for a sequence of folded lines
1851 		++used;
1852 	    else
1853 #endif
1854 		used += plines(bot);
1855 	}
1856 	if (used > curwin->w_height)
1857 	    break;
1858 	if (top < curwin->w_topline)
1859 	    scrolled += i;
1860 
1861 	/*
1862 	 * If scrolling is needed, scroll at least 'sj' lines.
1863 	 */
1864 	if ((new_topline >= curwin->w_topline || scrolled > min_scroll)
1865 		&& extra >= off)
1866 	    break;
1867 
1868 	extra += i;
1869 	new_topline = top;
1870 	--top;
1871 	++bot;
1872     }
1873 
1874     /*
1875      * If we don't have enough space, put cursor in the middle.
1876      * This makes sure we get the same position when using "k" and "j"
1877      * in a small window.
1878      */
1879     if (used > curwin->w_height)
1880 	scroll_cursor_halfway(FALSE);
1881     else
1882     {
1883 	/*
1884 	 * If "always" is FALSE, only adjust topline to a lower value, higher
1885 	 * value may happen with wrapping lines
1886 	 */
1887 	if (new_topline < curwin->w_topline || always)
1888 	    curwin->w_topline = new_topline;
1889 	if (curwin->w_topline > curwin->w_cursor.lnum)
1890 	    curwin->w_topline = curwin->w_cursor.lnum;
1891 #ifdef FEAT_DIFF
1892 	curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline);
1893 	if (curwin->w_topfill > 0 && extra > off)
1894 	{
1895 	    curwin->w_topfill -= extra - off;
1896 	    if (curwin->w_topfill < 0)
1897 		curwin->w_topfill = 0;
1898 	}
1899 	check_topfill(curwin, FALSE);
1900 #endif
1901 	if (curwin->w_topline != old_topline
1902 #ifdef FEAT_DIFF
1903 		|| curwin->w_topfill != old_topfill
1904 #endif
1905 		)
1906 	    curwin->w_valid &=
1907 		      ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
1908 	curwin->w_valid |= VALID_TOPLINE;
1909     }
1910 }
1911 
1912 /*
1913  * Set w_empty_rows and w_filler_rows for window "wp", having used up "used"
1914  * screen lines for text lines.
1915  */
1916     void
set_empty_rows(win_T * wp,int used)1917 set_empty_rows(win_T *wp, int used)
1918 {
1919 #ifdef FEAT_DIFF
1920     wp->w_filler_rows = 0;
1921 #endif
1922     if (used == 0)
1923 	wp->w_empty_rows = 0;	// single line that doesn't fit
1924     else
1925     {
1926 	wp->w_empty_rows = wp->w_height - used;
1927 #ifdef FEAT_DIFF
1928 	if (wp->w_botline <= wp->w_buffer->b_ml.ml_line_count)
1929 	{
1930 	    wp->w_filler_rows = diff_check_fill(wp, wp->w_botline);
1931 	    if (wp->w_empty_rows > wp->w_filler_rows)
1932 		wp->w_empty_rows -= wp->w_filler_rows;
1933 	    else
1934 	    {
1935 		wp->w_filler_rows = wp->w_empty_rows;
1936 		wp->w_empty_rows = 0;
1937 	    }
1938 	}
1939 #endif
1940     }
1941 }
1942 
1943 /*
1944  * Recompute topline to put the cursor at the bottom of the window.
1945  * Scroll at least "min_scroll" lines.
1946  * If "set_topbot" is TRUE, set topline and botline first (for "zb").
1947  * This is messy stuff!!!
1948  */
1949     void
scroll_cursor_bot(int min_scroll,int set_topbot)1950 scroll_cursor_bot(int min_scroll, int set_topbot)
1951 {
1952     int		used;
1953     int		scrolled = 0;
1954     int		extra = 0;
1955     int		i;
1956     linenr_T	line_count;
1957     linenr_T	old_topline = curwin->w_topline;
1958     lineoff_T	loff;
1959     lineoff_T	boff;
1960 #ifdef FEAT_DIFF
1961     int		old_topfill = curwin->w_topfill;
1962     int		fill_below_window;
1963 #endif
1964     linenr_T	old_botline = curwin->w_botline;
1965     linenr_T	old_valid = curwin->w_valid;
1966     int		old_empty_rows = curwin->w_empty_rows;
1967     linenr_T	cln;		    // Cursor Line Number
1968     long        so = get_scrolloff_value();
1969 
1970     cln = curwin->w_cursor.lnum;
1971     if (set_topbot)
1972     {
1973 	used = 0;
1974 	curwin->w_botline = cln + 1;
1975 #ifdef FEAT_DIFF
1976 	loff.fill = 0;
1977 #endif
1978 	for (curwin->w_topline = curwin->w_botline;
1979 		curwin->w_topline > 1;
1980 		curwin->w_topline = loff.lnum)
1981 	{
1982 	    loff.lnum = curwin->w_topline;
1983 	    topline_back(&loff);
1984 	    if (loff.height == MAXCOL || used + loff.height > curwin->w_height)
1985 		break;
1986 	    used += loff.height;
1987 #ifdef FEAT_DIFF
1988 	    curwin->w_topfill = loff.fill;
1989 #endif
1990 	}
1991 	set_empty_rows(curwin, used);
1992 	curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
1993 	if (curwin->w_topline != old_topline
1994 #ifdef FEAT_DIFF
1995 		|| curwin->w_topfill != old_topfill
1996 #endif
1997 		)
1998 	    curwin->w_valid &= ~(VALID_WROW|VALID_CROW);
1999     }
2000     else
2001 	validate_botline();
2002 
2003     // The lines of the cursor line itself are always used.
2004 #ifdef FEAT_DIFF
2005     used = plines_nofill(cln);
2006 #else
2007     validate_cheight();
2008     used = curwin->w_cline_height;
2009 #endif
2010 
2011     // If the cursor is below botline, we will at least scroll by the height
2012     // of the cursor line.  Correct for empty lines, which are really part of
2013     // botline.
2014     if (cln >= curwin->w_botline)
2015     {
2016 	scrolled = used;
2017 	if (cln == curwin->w_botline)
2018 	    scrolled -= curwin->w_empty_rows;
2019     }
2020 
2021     /*
2022      * Stop counting lines to scroll when
2023      * - hitting start of the file
2024      * - scrolled nothing or at least 'sj' lines
2025      * - at least 'scrolloff' lines below the cursor
2026      * - lines between botline and cursor have been counted
2027      */
2028 #ifdef FEAT_FOLDING
2029     if (!hasFolding(curwin->w_cursor.lnum, &loff.lnum, &boff.lnum))
2030 #endif
2031     {
2032 	loff.lnum = cln;
2033 	boff.lnum = cln;
2034     }
2035 #ifdef FEAT_DIFF
2036     loff.fill = 0;
2037     boff.fill = 0;
2038     fill_below_window = diff_check_fill(curwin, curwin->w_botline)
2039 						      - curwin->w_filler_rows;
2040 #endif
2041 
2042     while (loff.lnum > 1)
2043     {
2044 	// Stop when scrolled nothing or at least "min_scroll", found "extra"
2045 	// context for 'scrolloff' and counted all lines below the window.
2046 	if ((((scrolled <= 0 || scrolled >= min_scroll)
2047 		    && extra >= (mouse_dragging > 0 ? mouse_dragging - 1 : so))
2048 		    || boff.lnum + 1 > curbuf->b_ml.ml_line_count)
2049 		&& loff.lnum <= curwin->w_botline
2050 #ifdef FEAT_DIFF
2051 		&& (loff.lnum < curwin->w_botline
2052 		    || loff.fill >= fill_below_window)
2053 #endif
2054 		)
2055 	    break;
2056 
2057 	// Add one line above
2058 	topline_back(&loff);
2059 	if (loff.height == MAXCOL)
2060 	    used = MAXCOL;
2061 	else
2062 	    used += loff.height;
2063 	if (used > curwin->w_height)
2064 	    break;
2065 	if (loff.lnum >= curwin->w_botline
2066 #ifdef FEAT_DIFF
2067 		&& (loff.lnum > curwin->w_botline
2068 		    || loff.fill <= fill_below_window)
2069 #endif
2070 		)
2071 	{
2072 	    // Count screen lines that are below the window.
2073 	    scrolled += loff.height;
2074 	    if (loff.lnum == curwin->w_botline
2075 #ifdef FEAT_DIFF
2076 			    && loff.fill == 0
2077 #endif
2078 		    )
2079 		scrolled -= curwin->w_empty_rows;
2080 	}
2081 
2082 	if (boff.lnum < curbuf->b_ml.ml_line_count)
2083 	{
2084 	    // Add one line below
2085 	    botline_forw(&boff);
2086 	    used += boff.height;
2087 	    if (used > curwin->w_height)
2088 		break;
2089 	    if (extra < ( mouse_dragging > 0 ? mouse_dragging - 1 : so)
2090 		    || scrolled < min_scroll)
2091 	    {
2092 		extra += boff.height;
2093 		if (boff.lnum >= curwin->w_botline
2094 #ifdef FEAT_DIFF
2095 			|| (boff.lnum + 1 == curwin->w_botline
2096 			    && boff.fill > curwin->w_filler_rows)
2097 #endif
2098 		   )
2099 		{
2100 		    // Count screen lines that are below the window.
2101 		    scrolled += boff.height;
2102 		    if (boff.lnum == curwin->w_botline
2103 #ifdef FEAT_DIFF
2104 			    && boff.fill == 0
2105 #endif
2106 			    )
2107 			scrolled -= curwin->w_empty_rows;
2108 		}
2109 	    }
2110 	}
2111     }
2112 
2113     // curwin->w_empty_rows is larger, no need to scroll
2114     if (scrolled <= 0)
2115 	line_count = 0;
2116     // more than a screenfull, don't scroll but redraw
2117     else if (used > curwin->w_height)
2118 	line_count = used;
2119     // scroll minimal number of lines
2120     else
2121     {
2122 	line_count = 0;
2123 #ifdef FEAT_DIFF
2124 	boff.fill = curwin->w_topfill;
2125 #endif
2126 	boff.lnum = curwin->w_topline - 1;
2127 	for (i = 0; i < scrolled && boff.lnum < curwin->w_botline; )
2128 	{
2129 	    botline_forw(&boff);
2130 	    i += boff.height;
2131 	    ++line_count;
2132 	}
2133 	if (i < scrolled)	// below curwin->w_botline, don't scroll
2134 	    line_count = 9999;
2135     }
2136 
2137     /*
2138      * Scroll up if the cursor is off the bottom of the screen a bit.
2139      * Otherwise put it at 1/2 of the screen.
2140      */
2141     if (line_count >= curwin->w_height && line_count > min_scroll)
2142 	scroll_cursor_halfway(FALSE);
2143     else
2144 	scrollup(line_count, TRUE);
2145 
2146     /*
2147      * If topline didn't change we need to restore w_botline and w_empty_rows
2148      * (we changed them).
2149      * If topline did change, update_screen() will set botline.
2150      */
2151     if (curwin->w_topline == old_topline && set_topbot)
2152     {
2153 	curwin->w_botline = old_botline;
2154 	curwin->w_empty_rows = old_empty_rows;
2155 	curwin->w_valid = old_valid;
2156     }
2157     curwin->w_valid |= VALID_TOPLINE;
2158 }
2159 
2160 /*
2161  * Recompute topline to put the cursor halfway the window
2162  * If "atend" is TRUE, also put it halfway at the end of the file.
2163  */
2164     void
scroll_cursor_halfway(int atend)2165 scroll_cursor_halfway(int atend)
2166 {
2167     int		above = 0;
2168     linenr_T	topline;
2169 #ifdef FEAT_DIFF
2170     int		topfill = 0;
2171 #endif
2172     int		below = 0;
2173     int		used;
2174     lineoff_T	loff;
2175     lineoff_T	boff;
2176 #ifdef FEAT_DIFF
2177     linenr_T	old_topline = curwin->w_topline;
2178 #endif
2179 
2180 #ifdef FEAT_PROP_POPUP
2181     // if the width changed this needs to be updated first
2182     may_update_popup_position();
2183 #endif
2184     loff.lnum = boff.lnum = curwin->w_cursor.lnum;
2185 #ifdef FEAT_FOLDING
2186     (void)hasFolding(loff.lnum, &loff.lnum, &boff.lnum);
2187 #endif
2188 #ifdef FEAT_DIFF
2189     used = plines_nofill(loff.lnum);
2190     loff.fill = 0;
2191     boff.fill = 0;
2192 #else
2193     used = plines(loff.lnum);
2194 #endif
2195     topline = loff.lnum;
2196     while (topline > 1)
2197     {
2198 	if (below <= above)	    // add a line below the cursor first
2199 	{
2200 	    if (boff.lnum < curbuf->b_ml.ml_line_count)
2201 	    {
2202 		botline_forw(&boff);
2203 		used += boff.height;
2204 		if (used > curwin->w_height)
2205 		    break;
2206 		below += boff.height;
2207 	    }
2208 	    else
2209 	    {
2210 		++below;	    // count a "~" line
2211 		if (atend)
2212 		    ++used;
2213 	    }
2214 	}
2215 
2216 	if (below > above)	    // add a line above the cursor
2217 	{
2218 	    topline_back(&loff);
2219 	    if (loff.height == MAXCOL)
2220 		used = MAXCOL;
2221 	    else
2222 		used += loff.height;
2223 	    if (used > curwin->w_height)
2224 		break;
2225 	    above += loff.height;
2226 	    topline = loff.lnum;
2227 #ifdef FEAT_DIFF
2228 	    topfill = loff.fill;
2229 #endif
2230 	}
2231     }
2232 #ifdef FEAT_FOLDING
2233     if (!hasFolding(topline, &curwin->w_topline, NULL))
2234 #endif
2235 	curwin->w_topline = topline;
2236 #ifdef FEAT_DIFF
2237     curwin->w_topfill = topfill;
2238     if (old_topline > curwin->w_topline + curwin->w_height)
2239 	curwin->w_botfill = FALSE;
2240     check_topfill(curwin, FALSE);
2241 #endif
2242     curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
2243     curwin->w_valid |= VALID_TOPLINE;
2244 }
2245 
2246 /*
2247  * Correct the cursor position so that it is in a part of the screen at least
2248  * 'scrolloff' lines from the top and bottom, if possible.
2249  * If not possible, put it at the same position as scroll_cursor_halfway().
2250  * When called topline must be valid!
2251  */
2252     void
cursor_correct(void)2253 cursor_correct(void)
2254 {
2255     int		above = 0;	    // screen lines above topline
2256     linenr_T	topline;
2257     int		below = 0;	    // screen lines below botline
2258     linenr_T	botline;
2259     int		above_wanted, below_wanted;
2260     linenr_T	cln;		    // Cursor Line Number
2261     int		max_off;
2262     long        so = get_scrolloff_value();
2263 
2264     /*
2265      * How many lines we would like to have above/below the cursor depends on
2266      * whether the first/last line of the file is on screen.
2267      */
2268     above_wanted = so;
2269     below_wanted = so;
2270     if (mouse_dragging > 0)
2271     {
2272 	above_wanted = mouse_dragging - 1;
2273 	below_wanted = mouse_dragging - 1;
2274     }
2275     if (curwin->w_topline == 1)
2276     {
2277 	above_wanted = 0;
2278 	max_off = curwin->w_height / 2;
2279 	if (below_wanted > max_off)
2280 	    below_wanted = max_off;
2281     }
2282     validate_botline();
2283     if (curwin->w_botline == curbuf->b_ml.ml_line_count + 1
2284 	    && mouse_dragging == 0)
2285     {
2286 	below_wanted = 0;
2287 	max_off = (curwin->w_height - 1) / 2;
2288 	if (above_wanted > max_off)
2289 	    above_wanted = max_off;
2290     }
2291 
2292     /*
2293      * If there are sufficient file-lines above and below the cursor, we can
2294      * return now.
2295      */
2296     cln = curwin->w_cursor.lnum;
2297     if (cln >= curwin->w_topline + above_wanted
2298 	    && cln < curwin->w_botline - below_wanted
2299 #ifdef FEAT_FOLDING
2300 	    && !hasAnyFolding(curwin)
2301 #endif
2302 	    )
2303 	return;
2304 
2305     /*
2306      * Narrow down the area where the cursor can be put by taking lines from
2307      * the top and the bottom until:
2308      * - the desired context lines are found
2309      * - the lines from the top is past the lines from the bottom
2310      */
2311     topline = curwin->w_topline;
2312     botline = curwin->w_botline - 1;
2313 #ifdef FEAT_DIFF
2314     // count filler lines as context
2315     above = curwin->w_topfill;
2316     below = curwin->w_filler_rows;
2317 #endif
2318     while ((above < above_wanted || below < below_wanted) && topline < botline)
2319     {
2320 	if (below < below_wanted && (below <= above || above >= above_wanted))
2321 	{
2322 #ifdef FEAT_FOLDING
2323 	    if (hasFolding(botline, &botline, NULL))
2324 		++below;
2325 	    else
2326 #endif
2327 		below += plines(botline);
2328 	    --botline;
2329 	}
2330 	if (above < above_wanted && (above < below || below >= below_wanted))
2331 	{
2332 #ifdef FEAT_FOLDING
2333 	    if (hasFolding(topline, NULL, &topline))
2334 		++above;
2335 	    else
2336 #endif
2337 		above += PLINES_NOFILL(topline);
2338 #ifdef FEAT_DIFF
2339 	    // Count filler lines below this line as context.
2340 	    if (topline < botline)
2341 		above += diff_check_fill(curwin, topline + 1);
2342 #endif
2343 	    ++topline;
2344 	}
2345     }
2346     if (topline == botline || botline == 0)
2347 	curwin->w_cursor.lnum = topline;
2348     else if (topline > botline)
2349 	curwin->w_cursor.lnum = botline;
2350     else
2351     {
2352 	if (cln < topline && curwin->w_topline > 1)
2353 	{
2354 	    curwin->w_cursor.lnum = topline;
2355 	    curwin->w_valid &=
2356 			    ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW);
2357 	}
2358 	if (cln > botline && curwin->w_botline <= curbuf->b_ml.ml_line_count)
2359 	{
2360 	    curwin->w_cursor.lnum = botline;
2361 	    curwin->w_valid &=
2362 			    ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW);
2363 	}
2364     }
2365     curwin->w_valid |= VALID_TOPLINE;
2366 }
2367 
2368 static void get_scroll_overlap(lineoff_T *lp, int dir);
2369 
2370 /*
2371  * move screen 'count' pages up or down and update screen
2372  *
2373  * return FAIL for failure, OK otherwise
2374  */
2375     int
onepage(int dir,long count)2376 onepage(int dir, long count)
2377 {
2378     long	n;
2379     int		retval = OK;
2380     lineoff_T	loff;
2381     linenr_T	old_topline = curwin->w_topline;
2382     long        so = get_scrolloff_value();
2383 
2384     if (curbuf->b_ml.ml_line_count == 1)    // nothing to do
2385     {
2386 	beep_flush();
2387 	return FAIL;
2388     }
2389 
2390     for ( ; count > 0; --count)
2391     {
2392 	validate_botline();
2393 	/*
2394 	 * It's an error to move a page up when the first line is already on
2395 	 * the screen.	It's an error to move a page down when the last line
2396 	 * is on the screen and the topline is 'scrolloff' lines from the
2397 	 * last line.
2398 	 */
2399 	if (dir == FORWARD
2400 		? ((curwin->w_topline >= curbuf->b_ml.ml_line_count - so)
2401 		    && curwin->w_botline > curbuf->b_ml.ml_line_count)
2402 		: (curwin->w_topline == 1
2403 #ifdef FEAT_DIFF
2404 		    && curwin->w_topfill ==
2405 				    diff_check_fill(curwin, curwin->w_topline)
2406 #endif
2407 		    ))
2408 	{
2409 	    beep_flush();
2410 	    retval = FAIL;
2411 	    break;
2412 	}
2413 
2414 #ifdef FEAT_DIFF
2415 	loff.fill = 0;
2416 #endif
2417 	if (dir == FORWARD)
2418 	{
2419 	    if (ONE_WINDOW && p_window > 0 && p_window < Rows - 1)
2420 	    {
2421 		// Vi compatible scrolling
2422 		if (p_window <= 2)
2423 		    ++curwin->w_topline;
2424 		else
2425 		    curwin->w_topline += p_window - 2;
2426 		if (curwin->w_topline > curbuf->b_ml.ml_line_count)
2427 		    curwin->w_topline = curbuf->b_ml.ml_line_count;
2428 		curwin->w_cursor.lnum = curwin->w_topline;
2429 	    }
2430 	    else if (curwin->w_botline > curbuf->b_ml.ml_line_count)
2431 	    {
2432 		// at end of file
2433 		curwin->w_topline = curbuf->b_ml.ml_line_count;
2434 #ifdef FEAT_DIFF
2435 		curwin->w_topfill = 0;
2436 #endif
2437 		curwin->w_valid &= ~(VALID_WROW|VALID_CROW);
2438 	    }
2439 	    else
2440 	    {
2441 		// For the overlap, start with the line just below the window
2442 		// and go upwards.
2443 		loff.lnum = curwin->w_botline;
2444 #ifdef FEAT_DIFF
2445 		loff.fill = diff_check_fill(curwin, loff.lnum)
2446 						      - curwin->w_filler_rows;
2447 #endif
2448 		get_scroll_overlap(&loff, -1);
2449 		curwin->w_topline = loff.lnum;
2450 #ifdef FEAT_DIFF
2451 		curwin->w_topfill = loff.fill;
2452 		check_topfill(curwin, FALSE);
2453 #endif
2454 		curwin->w_cursor.lnum = curwin->w_topline;
2455 		curwin->w_valid &= ~(VALID_WCOL|VALID_CHEIGHT|VALID_WROW|
2456 				   VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
2457 	    }
2458 	}
2459 	else	// dir == BACKWARDS
2460 	{
2461 #ifdef FEAT_DIFF
2462 	    if (curwin->w_topline == 1)
2463 	    {
2464 		// Include max number of filler lines
2465 		max_topfill();
2466 		continue;
2467 	    }
2468 #endif
2469 	    if (ONE_WINDOW && p_window > 0 && p_window < Rows - 1)
2470 	    {
2471 		// Vi compatible scrolling (sort of)
2472 		if (p_window <= 2)
2473 		    --curwin->w_topline;
2474 		else
2475 		    curwin->w_topline -= p_window - 2;
2476 		if (curwin->w_topline < 1)
2477 		    curwin->w_topline = 1;
2478 		curwin->w_cursor.lnum = curwin->w_topline + p_window - 1;
2479 		if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
2480 		    curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
2481 		continue;
2482 	    }
2483 
2484 	    // Find the line at the top of the window that is going to be the
2485 	    // line at the bottom of the window.  Make sure this results in
2486 	    // the same line as before doing CTRL-F.
2487 	    loff.lnum = curwin->w_topline - 1;
2488 #ifdef FEAT_DIFF
2489 	    loff.fill = diff_check_fill(curwin, loff.lnum + 1)
2490 							  - curwin->w_topfill;
2491 #endif
2492 	    get_scroll_overlap(&loff, 1);
2493 
2494 	    if (loff.lnum >= curbuf->b_ml.ml_line_count)
2495 	    {
2496 		loff.lnum = curbuf->b_ml.ml_line_count;
2497 #ifdef FEAT_DIFF
2498 		loff.fill = 0;
2499 	    }
2500 	    else
2501 	    {
2502 		botline_topline(&loff);
2503 #endif
2504 	    }
2505 	    curwin->w_cursor.lnum = loff.lnum;
2506 
2507 	    // Find the line just above the new topline to get the right line
2508 	    // at the bottom of the window.
2509 	    n = 0;
2510 	    while (n <= curwin->w_height && loff.lnum >= 1)
2511 	    {
2512 		topline_back(&loff);
2513 		if (loff.height == MAXCOL)
2514 		    n = MAXCOL;
2515 		else
2516 		    n += loff.height;
2517 	    }
2518 	    if (loff.lnum < 1)			// at begin of file
2519 	    {
2520 		curwin->w_topline = 1;
2521 #ifdef FEAT_DIFF
2522 		max_topfill();
2523 #endif
2524 		curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
2525 	    }
2526 	    else
2527 	    {
2528 		// Go two lines forward again.
2529 #ifdef FEAT_DIFF
2530 		topline_botline(&loff);
2531 #endif
2532 		botline_forw(&loff);
2533 		botline_forw(&loff);
2534 #ifdef FEAT_DIFF
2535 		botline_topline(&loff);
2536 #endif
2537 #ifdef FEAT_FOLDING
2538 		// We're at the wrong end of a fold now.
2539 		(void)hasFolding(loff.lnum, &loff.lnum, NULL);
2540 #endif
2541 
2542 		// Always scroll at least one line.  Avoid getting stuck on
2543 		// very long lines.
2544 		if (loff.lnum >= curwin->w_topline
2545 #ifdef FEAT_DIFF
2546 			&& (loff.lnum > curwin->w_topline
2547 			    || loff.fill >= curwin->w_topfill)
2548 #endif
2549 			)
2550 		{
2551 #ifdef FEAT_DIFF
2552 		    // First try using the maximum number of filler lines.  If
2553 		    // that's not enough, backup one line.
2554 		    loff.fill = curwin->w_topfill;
2555 		    if (curwin->w_topfill < diff_check_fill(curwin,
2556 							   curwin->w_topline))
2557 			max_topfill();
2558 		    if (curwin->w_topfill == loff.fill)
2559 #endif
2560 		    {
2561 			--curwin->w_topline;
2562 #ifdef FEAT_DIFF
2563 			curwin->w_topfill = 0;
2564 #endif
2565 		    }
2566 		    comp_botline(curwin);
2567 		    curwin->w_cursor.lnum = curwin->w_botline - 1;
2568 		    curwin->w_valid &=
2569 			    ~(VALID_WCOL|VALID_CHEIGHT|VALID_WROW|VALID_CROW);
2570 		}
2571 		else
2572 		{
2573 		    curwin->w_topline = loff.lnum;
2574 #ifdef FEAT_DIFF
2575 		    curwin->w_topfill = loff.fill;
2576 		    check_topfill(curwin, FALSE);
2577 #endif
2578 		    curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
2579 		}
2580 	    }
2581 	}
2582     }
2583 #ifdef FEAT_FOLDING
2584     foldAdjustCursor();
2585 #endif
2586     cursor_correct();
2587     check_cursor_col();
2588     if (retval == OK)
2589 	beginline(BL_SOL | BL_FIX);
2590     curwin->w_valid &= ~(VALID_WCOL|VALID_WROW|VALID_VIRTCOL);
2591 
2592     if (retval == OK && dir == FORWARD)
2593     {
2594 	// Avoid the screen jumping up and down when 'scrolloff' is non-zero.
2595 	// But make sure we scroll at least one line (happens with mix of long
2596 	// wrapping lines and non-wrapping line).
2597 	if (check_top_offset())
2598 	{
2599 	    scroll_cursor_top(1, FALSE);
2600 	    if (curwin->w_topline <= old_topline
2601 				  && old_topline < curbuf->b_ml.ml_line_count)
2602 	    {
2603 		curwin->w_topline = old_topline + 1;
2604 #ifdef FEAT_FOLDING
2605 		(void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
2606 #endif
2607 	    }
2608 	}
2609 #ifdef FEAT_FOLDING
2610 	else if (curwin->w_botline > curbuf->b_ml.ml_line_count)
2611 	    (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
2612 #endif
2613     }
2614 
2615     redraw_later(VALID);
2616     return retval;
2617 }
2618 
2619 /*
2620  * Decide how much overlap to use for page-up or page-down scrolling.
2621  * This is symmetric, so that doing both keeps the same lines displayed.
2622  * Three lines are examined:
2623  *
2624  *  before CTRL-F	    after CTRL-F / before CTRL-B
2625  *     etc.			l1
2626  *  l1 last but one line	------------
2627  *  l2 last text line		l2 top text line
2628  *  -------------		l3 second text line
2629  *  l3				   etc.
2630  */
2631     static void
get_scroll_overlap(lineoff_T * lp,int dir)2632 get_scroll_overlap(lineoff_T *lp, int dir)
2633 {
2634     int		h1, h2, h3, h4;
2635     int		min_height = curwin->w_height - 2;
2636     lineoff_T	loff0, loff1, loff2;
2637 
2638 #ifdef FEAT_DIFF
2639     if (lp->fill > 0)
2640 	lp->height = 1;
2641     else
2642 	lp->height = plines_nofill(lp->lnum);
2643 #else
2644     lp->height = plines(lp->lnum);
2645 #endif
2646     h1 = lp->height;
2647     if (h1 > min_height)
2648 	return;		// no overlap
2649 
2650     loff0 = *lp;
2651     if (dir > 0)
2652 	botline_forw(lp);
2653     else
2654 	topline_back(lp);
2655     h2 = lp->height;
2656     if (h2 == MAXCOL || h2 + h1 > min_height)
2657     {
2658 	*lp = loff0;	// no overlap
2659 	return;
2660     }
2661 
2662     loff1 = *lp;
2663     if (dir > 0)
2664 	botline_forw(lp);
2665     else
2666 	topline_back(lp);
2667     h3 = lp->height;
2668     if (h3 == MAXCOL || h3 + h2 > min_height)
2669     {
2670 	*lp = loff0;	// no overlap
2671 	return;
2672     }
2673 
2674     loff2 = *lp;
2675     if (dir > 0)
2676 	botline_forw(lp);
2677     else
2678 	topline_back(lp);
2679     h4 = lp->height;
2680     if (h4 == MAXCOL || h4 + h3 + h2 > min_height || h3 + h2 + h1 > min_height)
2681 	*lp = loff1;	// 1 line overlap
2682     else
2683 	*lp = loff2;	// 2 lines overlap
2684 }
2685 
2686 /*
2687  * Scroll 'scroll' lines up or down.
2688  */
2689     void
halfpage(int flag,linenr_T Prenum)2690 halfpage(int flag, linenr_T Prenum)
2691 {
2692     long	scrolled = 0;
2693     int		i;
2694     int		n;
2695     int		room;
2696 
2697     if (Prenum)
2698 	curwin->w_p_scr = (Prenum > curwin->w_height) ?
2699 						curwin->w_height : Prenum;
2700     n = (curwin->w_p_scr <= curwin->w_height) ?
2701 				    curwin->w_p_scr : curwin->w_height;
2702 
2703     update_topline();
2704     validate_botline();
2705     room = curwin->w_empty_rows;
2706 #ifdef FEAT_DIFF
2707     room += curwin->w_filler_rows;
2708 #endif
2709     if (flag)
2710     {
2711 	/*
2712 	 * scroll the text up
2713 	 */
2714 	while (n > 0 && curwin->w_botline <= curbuf->b_ml.ml_line_count)
2715 	{
2716 #ifdef FEAT_DIFF
2717 	    if (curwin->w_topfill > 0)
2718 	    {
2719 		i = 1;
2720 		--n;
2721 		--curwin->w_topfill;
2722 	    }
2723 	    else
2724 #endif
2725 	    {
2726 		i = PLINES_NOFILL(curwin->w_topline);
2727 		n -= i;
2728 		if (n < 0 && scrolled > 0)
2729 		    break;
2730 #ifdef FEAT_FOLDING
2731 		(void)hasFolding(curwin->w_topline, NULL, &curwin->w_topline);
2732 #endif
2733 		++curwin->w_topline;
2734 #ifdef FEAT_DIFF
2735 		curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline);
2736 #endif
2737 
2738 		if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
2739 		{
2740 		    ++curwin->w_cursor.lnum;
2741 		    curwin->w_valid &=
2742 				    ~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL);
2743 		}
2744 	    }
2745 	    curwin->w_valid &= ~(VALID_CROW|VALID_WROW);
2746 	    scrolled += i;
2747 
2748 	    /*
2749 	     * Correct w_botline for changed w_topline.
2750 	     * Won't work when there are filler lines.
2751 	     */
2752 #ifdef FEAT_DIFF
2753 	    if (curwin->w_p_diff)
2754 		curwin->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP);
2755 	    else
2756 #endif
2757 	    {
2758 		room += i;
2759 		do
2760 		{
2761 		    i = plines(curwin->w_botline);
2762 		    if (i > room)
2763 			break;
2764 #ifdef FEAT_FOLDING
2765 		    (void)hasFolding(curwin->w_botline, NULL,
2766 							  &curwin->w_botline);
2767 #endif
2768 		    ++curwin->w_botline;
2769 		    room -= i;
2770 		} while (curwin->w_botline <= curbuf->b_ml.ml_line_count);
2771 	    }
2772 	}
2773 
2774 	/*
2775 	 * When hit bottom of the file: move cursor down.
2776 	 */
2777 	if (n > 0)
2778 	{
2779 # ifdef FEAT_FOLDING
2780 	    if (hasAnyFolding(curwin))
2781 	    {
2782 		while (--n >= 0
2783 			&& curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
2784 		{
2785 		    (void)hasFolding(curwin->w_cursor.lnum, NULL,
2786 						      &curwin->w_cursor.lnum);
2787 		    ++curwin->w_cursor.lnum;
2788 		}
2789 	    }
2790 	    else
2791 # endif
2792 		curwin->w_cursor.lnum += n;
2793 	    check_cursor_lnum();
2794 	}
2795     }
2796     else
2797     {
2798 	/*
2799 	 * scroll the text down
2800 	 */
2801 	while (n > 0 && curwin->w_topline > 1)
2802 	{
2803 #ifdef FEAT_DIFF
2804 	    if (curwin->w_topfill < diff_check_fill(curwin, curwin->w_topline))
2805 	    {
2806 		i = 1;
2807 		--n;
2808 		++curwin->w_topfill;
2809 	    }
2810 	    else
2811 #endif
2812 	    {
2813 		i = PLINES_NOFILL(curwin->w_topline - 1);
2814 		n -= i;
2815 		if (n < 0 && scrolled > 0)
2816 		    break;
2817 		--curwin->w_topline;
2818 #ifdef FEAT_FOLDING
2819 		(void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
2820 #endif
2821 #ifdef FEAT_DIFF
2822 		curwin->w_topfill = 0;
2823 #endif
2824 	    }
2825 	    curwin->w_valid &= ~(VALID_CROW|VALID_WROW|
2826 					      VALID_BOTLINE|VALID_BOTLINE_AP);
2827 	    scrolled += i;
2828 	    if (curwin->w_cursor.lnum > 1)
2829 	    {
2830 		--curwin->w_cursor.lnum;
2831 		curwin->w_valid &= ~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL);
2832 	    }
2833 	}
2834 
2835 	/*
2836 	 * When hit top of the file: move cursor up.
2837 	 */
2838 	if (n > 0)
2839 	{
2840 	    if (curwin->w_cursor.lnum <= (linenr_T)n)
2841 		curwin->w_cursor.lnum = 1;
2842 	    else
2843 # ifdef FEAT_FOLDING
2844 	    if (hasAnyFolding(curwin))
2845 	    {
2846 		while (--n >= 0 && curwin->w_cursor.lnum > 1)
2847 		{
2848 		    --curwin->w_cursor.lnum;
2849 		    (void)hasFolding(curwin->w_cursor.lnum,
2850 						&curwin->w_cursor.lnum, NULL);
2851 		}
2852 	    }
2853 	    else
2854 # endif
2855 		curwin->w_cursor.lnum -= n;
2856 	}
2857     }
2858 # ifdef FEAT_FOLDING
2859     // Move cursor to first line of closed fold.
2860     foldAdjustCursor();
2861 # endif
2862 #ifdef FEAT_DIFF
2863     check_topfill(curwin, !flag);
2864 #endif
2865     cursor_correct();
2866     beginline(BL_SOL | BL_FIX);
2867     redraw_later(VALID);
2868 }
2869 
2870     void
do_check_cursorbind(void)2871 do_check_cursorbind(void)
2872 {
2873     linenr_T	line = curwin->w_cursor.lnum;
2874     colnr_T	col = curwin->w_cursor.col;
2875     colnr_T	coladd = curwin->w_cursor.coladd;
2876     colnr_T	curswant = curwin->w_curswant;
2877     int		set_curswant = curwin->w_set_curswant;
2878     win_T	*old_curwin = curwin;
2879     buf_T	*old_curbuf = curbuf;
2880     int		restart_edit_save;
2881     int		old_VIsual_select = VIsual_select;
2882     int		old_VIsual_active = VIsual_active;
2883 
2884     /*
2885      * loop through the cursorbound windows
2886      */
2887     VIsual_select = VIsual_active = 0;
2888     FOR_ALL_WINDOWS(curwin)
2889     {
2890 	curbuf = curwin->w_buffer;
2891 	// skip original window  and windows with 'noscrollbind'
2892 	if (curwin != old_curwin && curwin->w_p_crb)
2893 	{
2894 # ifdef FEAT_DIFF
2895 	    if (curwin->w_p_diff)
2896 		curwin->w_cursor.lnum =
2897 				 diff_get_corresponding_line(old_curbuf, line);
2898 	    else
2899 # endif
2900 		curwin->w_cursor.lnum = line;
2901 	    curwin->w_cursor.col = col;
2902 	    curwin->w_cursor.coladd = coladd;
2903 	    curwin->w_curswant = curswant;
2904 	    curwin->w_set_curswant = set_curswant;
2905 
2906 	    // Make sure the cursor is in a valid position.  Temporarily set
2907 	    // "restart_edit" to allow the cursor to be beyond the EOL.
2908 	    restart_edit_save = restart_edit;
2909 	    restart_edit = TRUE;
2910 	    check_cursor();
2911 # ifdef FEAT_SYN_HL
2912 	    if (curwin->w_p_cul || curwin->w_p_cuc)
2913 		validate_cursor();
2914 # endif
2915 	    restart_edit = restart_edit_save;
2916 	    // Correct cursor for multi-byte character.
2917 	    if (has_mbyte)
2918 		mb_adjust_cursor();
2919 	    redraw_later(VALID);
2920 
2921 	    // Only scroll when 'scrollbind' hasn't done this.
2922 	    if (!curwin->w_p_scb)
2923 		update_topline();
2924 	    curwin->w_redr_status = TRUE;
2925 	}
2926     }
2927 
2928     /*
2929      * reset current-window
2930      */
2931     VIsual_select = old_VIsual_select;
2932     VIsual_active = old_VIsual_active;
2933     curwin = old_curwin;
2934     curbuf = old_curbuf;
2935 }
2936