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