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