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) 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 } 845 } 846 847 /* 848 * Validate curwin->w_cline_height only. 849 */ 850 static void 851 validate_cheight() 852 { 853 check_cursor_moved(curwin); 854 if (!(curwin->w_valid & VALID_CHEIGHT)) 855 { 856 #ifdef FEAT_DIFF 857 if (curwin->w_cursor.lnum == curwin->w_topline) 858 curwin->w_cline_height = plines_nofill(curwin->w_cursor.lnum) 859 + curwin->w_topfill; 860 else 861 #endif 862 curwin->w_cline_height = plines(curwin->w_cursor.lnum); 863 #ifdef FEAT_FOLDING 864 curwin->w_cline_folded = hasFolding(curwin->w_cursor.lnum, NULL, NULL); 865 #endif 866 curwin->w_valid |= VALID_CHEIGHT; 867 } 868 } 869 870 /* 871 * validate w_wcol and w_virtcol only. Only correct when 'wrap' on! 872 */ 873 void 874 validate_cursor_col() 875 { 876 colnr_T off; 877 colnr_T col; 878 879 validate_virtcol(); 880 if (!(curwin->w_valid & VALID_WCOL)) 881 { 882 col = curwin->w_virtcol; 883 off = curwin_col_off(); 884 col += off; 885 886 /* long line wrapping, adjust curwin->w_wrow */ 887 if (curwin->w_p_wrap && col >= (colnr_T)W_WIDTH(curwin) 888 && W_WIDTH(curwin) - off + curwin_col_off2() > 0) 889 { 890 col -= W_WIDTH(curwin); 891 col = col % (W_WIDTH(curwin) - off + curwin_col_off2()); 892 } 893 curwin->w_wcol = col; 894 curwin->w_valid |= VALID_WCOL; 895 } 896 } 897 898 /* 899 * Compute offset of a window, occupied by line number, fold column and sign 900 * column (these don't move when scrolling horizontally). 901 */ 902 int 903 win_col_off(wp) 904 win_T *wp; 905 { 906 return ((wp->w_p_nu ? number_width(wp) + 1 : 0) 907 #ifdef FEAT_CMDWIN 908 + (cmdwin_type == 0 || wp != curwin ? 0 : 1) 909 #endif 910 #ifdef FEAT_FOLDING 911 + wp->w_p_fdc 912 #endif 913 #ifdef FEAT_SIGNS 914 + ( 915 # ifdef FEAT_NETBEANS_INTG 916 /* always show glyph gutter in netbeans */ 917 usingNetbeans || 918 # endif 919 wp->w_buffer->b_signlist != NULL ? 2 : 0) 920 #endif 921 ); 922 } 923 924 int 925 curwin_col_off() 926 { 927 return win_col_off(curwin); 928 } 929 930 /* 931 * Return the difference in column offset for the second screen line of a 932 * wrapped line. It's 8 if 'number' is on and 'n' is in 'cpoptions'. 933 */ 934 int 935 win_col_off2(wp) 936 win_T *wp; 937 { 938 if (wp->w_p_nu && vim_strchr(p_cpo, CPO_NUMCOL) != NULL) 939 return number_width(wp) + 1; 940 return 0; 941 } 942 943 int 944 curwin_col_off2() 945 { 946 return win_col_off2(curwin); 947 } 948 949 /* 950 * compute curwin->w_wcol and curwin->w_virtcol. 951 * Also updates curwin->w_wrow and curwin->w_cline_row. 952 * Also updates curwin->w_leftcol. 953 */ 954 void 955 curs_columns(scroll) 956 int scroll; /* when TRUE, may scroll horizontally */ 957 { 958 int diff; 959 int extra; /* offset for first screen line */ 960 int off_left, off_right; 961 int n; 962 int p_lines; 963 int width = 0; 964 int textwidth; 965 int new_leftcol; 966 colnr_T startcol; 967 colnr_T endcol; 968 colnr_T prev_skipcol; 969 970 /* 971 * First make sure that w_topline is valid (after moving the cursor). 972 */ 973 update_topline(); 974 975 /* 976 * Next make sure that w_cline_row is valid. 977 */ 978 if (!(curwin->w_valid & VALID_CROW)) 979 curs_rows(curwin, FALSE); 980 981 /* 982 * Compute the number of virtual columns. 983 */ 984 #ifdef FEAT_FOLDING 985 if (curwin->w_cline_folded) 986 /* In a folded line the cursor is always in the first column */ 987 startcol = curwin->w_virtcol = endcol = curwin->w_leftcol; 988 else 989 #endif 990 getvvcol(curwin, &curwin->w_cursor, 991 &startcol, &(curwin->w_virtcol), &endcol); 992 993 /* remove '$' from change command when cursor moves onto it */ 994 if (startcol > dollar_vcol) 995 dollar_vcol = 0; 996 997 extra = curwin_col_off(); 998 curwin->w_wcol = curwin->w_virtcol + extra; 999 endcol += extra; 1000 1001 /* 1002 * Now compute w_wrow, counting screen lines from w_cline_row. 1003 */ 1004 curwin->w_wrow = curwin->w_cline_row; 1005 1006 textwidth = W_WIDTH(curwin) - extra; 1007 if (textwidth <= 0) 1008 { 1009 /* No room for text, put cursor in last char of window. */ 1010 curwin->w_wcol = W_WIDTH(curwin) - 1; 1011 curwin->w_wrow = curwin->w_height - 1; 1012 } 1013 else if (curwin->w_p_wrap 1014 #ifdef FEAT_VERTSPLIT 1015 && curwin->w_width != 0 1016 #endif 1017 ) 1018 { 1019 width = textwidth + curwin_col_off2(); 1020 1021 /* long line wrapping, adjust curwin->w_wrow */ 1022 if (curwin->w_wcol >= W_WIDTH(curwin)) 1023 { 1024 n = (curwin->w_wcol - W_WIDTH(curwin)) / width + 1; 1025 curwin->w_wcol -= n * width; 1026 curwin->w_wrow += n; 1027 1028 #ifdef FEAT_LINEBREAK 1029 /* When cursor wraps to first char of next line in Insert 1030 * mode, the 'showbreak' string isn't shown, backup to first 1031 * column */ 1032 if (*p_sbr && *ml_get_cursor() == NUL 1033 && curwin->w_wcol == (int)vim_strsize(p_sbr)) 1034 curwin->w_wcol = 0; 1035 #endif 1036 } 1037 } 1038 1039 /* No line wrapping: compute curwin->w_leftcol if scrolling is on and line 1040 * is not folded. 1041 * If scrolling is off, curwin->w_leftcol is assumed to be 0 */ 1042 else if (scroll 1043 #ifdef FEAT_FOLDING 1044 && !curwin->w_cline_folded 1045 #endif 1046 ) 1047 { 1048 /* 1049 * If Cursor is left of the screen, scroll rightwards. 1050 * If Cursor is right of the screen, scroll leftwards 1051 * If we get closer to the edge than 'sidescrolloff', scroll a little 1052 * extra 1053 */ 1054 off_left = (int)startcol - (int)curwin->w_leftcol - p_siso; 1055 off_right = (int)endcol - (int)(curwin->w_leftcol + W_WIDTH(curwin) 1056 - p_siso) + 1; 1057 if (off_left < 0 || off_right > 0) 1058 { 1059 if (off_left < 0) 1060 diff = -off_left; 1061 else 1062 diff = off_right; 1063 1064 /* When far off or not enough room on either side, put cursor in 1065 * middle of window. */ 1066 if (p_ss == 0 || diff >= textwidth / 2 || off_right >= off_left) 1067 new_leftcol = curwin->w_wcol - extra - textwidth / 2; 1068 else 1069 { 1070 if (diff < p_ss) 1071 diff = p_ss; 1072 if (off_left < 0) 1073 new_leftcol = curwin->w_leftcol - diff; 1074 else 1075 new_leftcol = curwin->w_leftcol + diff; 1076 } 1077 if (new_leftcol < 0) 1078 new_leftcol = 0; 1079 if (new_leftcol != (int)curwin->w_leftcol) 1080 { 1081 curwin->w_leftcol = new_leftcol; 1082 /* screen has to be redrawn with new curwin->w_leftcol */ 1083 redraw_later(NOT_VALID); 1084 } 1085 } 1086 curwin->w_wcol -= curwin->w_leftcol; 1087 } 1088 else if (curwin->w_wcol > (int)curwin->w_leftcol) 1089 curwin->w_wcol -= curwin->w_leftcol; 1090 else 1091 curwin->w_wcol = 0; 1092 1093 #ifdef FEAT_DIFF 1094 /* Skip over filler lines. At the top use w_topfill, there 1095 * may be some filler lines above the window. */ 1096 if (curwin->w_cursor.lnum == curwin->w_topline) 1097 curwin->w_wrow += curwin->w_topfill; 1098 else 1099 curwin->w_wrow += diff_check_fill(curwin, curwin->w_cursor.lnum); 1100 #endif 1101 1102 prev_skipcol = curwin->w_skipcol; 1103 1104 p_lines = 0; 1105 if ((curwin->w_wrow >= curwin->w_height 1106 || ((prev_skipcol > 0 1107 || curwin->w_wrow + p_so >= curwin->w_height) 1108 && (p_lines = 1109 #ifdef FEAT_DIFF 1110 plines_win_nofill 1111 #else 1112 plines_win 1113 #endif 1114 (curwin, curwin->w_cursor.lnum, FALSE)) 1115 - 1 >= curwin->w_height)) 1116 && curwin->w_height != 0 1117 && curwin->w_cursor.lnum == curwin->w_topline 1118 && width > 0 1119 #ifdef FEAT_VERTSPLIT 1120 && curwin->w_width != 0 1121 #endif 1122 ) 1123 { 1124 /* Cursor past end of screen. Happens with a single line that does 1125 * not fit on screen. Find a skipcol to show the text around the 1126 * cursor. Avoid scrolling all the time. compute value of "extra": 1127 * 1: Less than "p_so" lines above 1128 * 2: Less than "p_so" lines below 1129 * 3: both of them */ 1130 extra = 0; 1131 if (curwin->w_skipcol + p_so * width > curwin->w_virtcol) 1132 extra = 1; 1133 /* Compute last display line of the buffer line that we want at the 1134 * bottom of the window. */ 1135 if (p_lines == 0) 1136 p_lines = plines_win(curwin, curwin->w_cursor.lnum, FALSE); 1137 --p_lines; 1138 if (p_lines > curwin->w_wrow + p_so) 1139 n = curwin->w_wrow + p_so; 1140 else 1141 n = p_lines; 1142 if ((colnr_T)n >= curwin->w_height + curwin->w_skipcol / width) 1143 extra += 2; 1144 1145 if (extra == 3 || p_lines < p_so * 2) 1146 { 1147 /* not enough room for 'scrolloff', put cursor in the middle */ 1148 n = curwin->w_virtcol / width; 1149 if (n > curwin->w_height / 2) 1150 n -= curwin->w_height / 2; 1151 else 1152 n = 0; 1153 /* don't skip more than necessary */ 1154 if (n > p_lines - curwin->w_height + 1) 1155 n = p_lines - curwin->w_height + 1; 1156 curwin->w_skipcol = n * width; 1157 } 1158 else if (extra == 1) 1159 { 1160 /* less then 'scrolloff' lines above, decrease skipcol */ 1161 extra = (curwin->w_skipcol + p_so * width - curwin->w_virtcol 1162 + width - 1) / width; 1163 if (extra > 0) 1164 { 1165 if ((colnr_T)(extra * width) > curwin->w_skipcol) 1166 extra = curwin->w_skipcol / width; 1167 curwin->w_skipcol -= extra * width; 1168 } 1169 } 1170 else if (extra == 2) 1171 { 1172 /* less then 'scrolloff' lines below, increase skipcol */ 1173 endcol = (n - curwin->w_height + 1) * width; 1174 while (endcol > curwin->w_virtcol) 1175 endcol -= width; 1176 if (endcol > curwin->w_skipcol) 1177 curwin->w_skipcol = endcol; 1178 } 1179 1180 curwin->w_wrow -= curwin->w_skipcol / width; 1181 if (curwin->w_wrow >= curwin->w_height) 1182 { 1183 /* small window, make sure cursor is in it */ 1184 extra = curwin->w_wrow - curwin->w_height + 1; 1185 curwin->w_skipcol += extra * width; 1186 curwin->w_wrow -= extra; 1187 } 1188 1189 extra = ((int)prev_skipcol - (int)curwin->w_skipcol) / width; 1190 if (extra > 0) 1191 win_ins_lines(curwin, 0, extra, FALSE, FALSE); 1192 else if (extra < 0) 1193 win_del_lines(curwin, 0, -extra, FALSE, FALSE); 1194 } 1195 else 1196 curwin->w_skipcol = 0; 1197 if (prev_skipcol != curwin->w_skipcol) 1198 redraw_later(NOT_VALID); 1199 1200 curwin->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL; 1201 } 1202 1203 /* 1204 * Scroll the current window down by "line_count" logical lines. "CTRL-Y" 1205 */ 1206 /*ARGSUSED*/ 1207 void 1208 scrolldown(line_count, byfold) 1209 long line_count; 1210 int byfold; /* TRUE: count a closed fold as one line */ 1211 { 1212 long done = 0; /* total # of physical lines done */ 1213 int wrow; 1214 int moved = FALSE; 1215 1216 #ifdef FEAT_FOLDING 1217 linenr_T first; 1218 1219 /* Make sure w_topline is at the first of a sequence of folded lines. */ 1220 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); 1221 #endif 1222 validate_cursor(); /* w_wrow needs to be valid */ 1223 while (line_count-- > 0) 1224 { 1225 #ifdef FEAT_DIFF 1226 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline)) 1227 { 1228 ++curwin->w_topfill; 1229 ++done; 1230 } 1231 else 1232 #endif 1233 { 1234 if (curwin->w_topline == 1) 1235 break; 1236 --curwin->w_topline; 1237 #ifdef FEAT_DIFF 1238 curwin->w_topfill = 0; 1239 #endif 1240 #ifdef FEAT_FOLDING 1241 /* A sequence of folded lines only counts for one logical line */ 1242 if (hasFolding(curwin->w_topline, &first, NULL)) 1243 { 1244 ++done; 1245 if (!byfold) 1246 line_count -= curwin->w_topline - first - 1; 1247 curwin->w_botline -= curwin->w_topline - first; 1248 curwin->w_topline = first; 1249 } 1250 else 1251 #endif 1252 #ifdef FEAT_DIFF 1253 done += plines_nofill(curwin->w_topline); 1254 #else 1255 done += plines(curwin->w_topline); 1256 #endif 1257 } 1258 --curwin->w_botline; /* approximate w_botline */ 1259 invalidate_botline(); 1260 } 1261 curwin->w_wrow += done; /* keep w_wrow updated */ 1262 curwin->w_cline_row += done; /* keep w_cline_row updated */ 1263 1264 #ifdef FEAT_DIFF 1265 if (curwin->w_cursor.lnum == curwin->w_topline) 1266 curwin->w_cline_row = 0; 1267 check_topfill(curwin, TRUE); 1268 #endif 1269 1270 /* 1271 * Compute the row number of the last row of the cursor line 1272 * and move the cursor onto the displayed part of the window. 1273 */ 1274 wrow = curwin->w_wrow; 1275 if (curwin->w_p_wrap 1276 #ifdef FEAT_VERTSPLIT 1277 && curwin->w_width != 0 1278 #endif 1279 ) 1280 { 1281 validate_virtcol(); 1282 validate_cheight(); 1283 wrow += curwin->w_cline_height - 1 - 1284 curwin->w_virtcol / W_WIDTH(curwin); 1285 } 1286 while (wrow >= curwin->w_height && curwin->w_cursor.lnum > 1) 1287 { 1288 #ifdef FEAT_FOLDING 1289 if (hasFolding(curwin->w_cursor.lnum, &first, NULL)) 1290 { 1291 --wrow; 1292 if (first == 1) 1293 curwin->w_cursor.lnum = 1; 1294 else 1295 curwin->w_cursor.lnum = first - 1; 1296 } 1297 else 1298 #endif 1299 wrow -= plines(curwin->w_cursor.lnum--); 1300 curwin->w_valid &= 1301 ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL); 1302 moved = TRUE; 1303 } 1304 if (moved) 1305 { 1306 #ifdef FEAT_FOLDING 1307 /* Move cursor to first line of closed fold. */ 1308 foldAdjustCursor(); 1309 #endif 1310 coladvance(curwin->w_curswant); 1311 } 1312 } 1313 1314 /* 1315 * Scroll the current window up by "line_count" logical lines. "CTRL-E" 1316 */ 1317 /*ARGSUSED*/ 1318 void 1319 scrollup(line_count, byfold) 1320 long line_count; 1321 int byfold; /* TRUE: count a closed fold as one line */ 1322 { 1323 #if defined(FEAT_FOLDING) || defined(FEAT_DIFF) 1324 linenr_T lnum; 1325 1326 if ( 1327 # ifdef FEAT_FOLDING 1328 (byfold && hasAnyFolding(curwin)) 1329 # ifdef FEAT_DIFF 1330 || 1331 # endif 1332 # endif 1333 # ifdef FEAT_DIFF 1334 curwin->w_p_diff 1335 # endif 1336 ) 1337 { 1338 /* count each sequence of folded lines as one logical line */ 1339 lnum = curwin->w_topline; 1340 while (line_count--) 1341 { 1342 # ifdef FEAT_DIFF 1343 if (curwin->w_topfill > 0) 1344 --curwin->w_topfill; 1345 else 1346 # endif 1347 { 1348 # ifdef FEAT_FOLDING 1349 if (byfold) 1350 (void)hasFolding(lnum, NULL, &lnum); 1351 # endif 1352 if (lnum >= curbuf->b_ml.ml_line_count) 1353 break; 1354 ++lnum; 1355 # ifdef FEAT_DIFF 1356 curwin->w_topfill = diff_check_fill(curwin, lnum); 1357 # endif 1358 } 1359 } 1360 /* approximate w_botline */ 1361 curwin->w_botline += lnum - curwin->w_topline; 1362 curwin->w_topline = lnum; 1363 } 1364 else 1365 #endif 1366 { 1367 curwin->w_topline += line_count; 1368 curwin->w_botline += line_count; /* approximate w_botline */ 1369 } 1370 1371 if (curwin->w_topline > curbuf->b_ml.ml_line_count) 1372 curwin->w_topline = curbuf->b_ml.ml_line_count; 1373 if (curwin->w_botline > curbuf->b_ml.ml_line_count + 1) 1374 curwin->w_botline = curbuf->b_ml.ml_line_count + 1; 1375 1376 #ifdef FEAT_DIFF 1377 check_topfill(curwin, FALSE); 1378 #endif 1379 1380 #ifdef FEAT_FOLDING 1381 if (hasAnyFolding(curwin)) 1382 /* Make sure w_topline is at the first of a sequence of folded lines. */ 1383 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); 1384 #endif 1385 1386 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE); 1387 if (curwin->w_cursor.lnum < curwin->w_topline) 1388 { 1389 curwin->w_cursor.lnum = curwin->w_topline; 1390 curwin->w_valid &= 1391 ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL); 1392 coladvance(curwin->w_curswant); 1393 } 1394 } 1395 1396 #ifdef FEAT_DIFF 1397 /* 1398 * Don't end up with too many filler lines in the window. 1399 */ 1400 void 1401 check_topfill(wp, down) 1402 win_T *wp; 1403 int down; /* when TRUE scroll down when not enough space */ 1404 { 1405 int n; 1406 1407 if (wp->w_topfill > 0) 1408 { 1409 n = plines_win_nofill(wp, wp->w_topline, TRUE); 1410 if (wp->w_topfill + n > wp->w_height) 1411 { 1412 if (down && wp->w_topline > 1) 1413 { 1414 --wp->w_topline; 1415 wp->w_topfill = 0; 1416 } 1417 else 1418 { 1419 wp->w_topfill = wp->w_height - n; 1420 if (wp->w_topfill < 0) 1421 wp->w_topfill = 0; 1422 } 1423 } 1424 } 1425 } 1426 1427 /* 1428 * Use as many filler lines as possible for w_topline. Make sure w_topline 1429 * is still visible. 1430 */ 1431 static void 1432 max_topfill() 1433 { 1434 int n; 1435 1436 n = plines_nofill(curwin->w_topline); 1437 if (n >= curwin->w_height) 1438 curwin->w_topfill = 0; 1439 else 1440 { 1441 curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline); 1442 if (curwin->w_topfill + n > curwin->w_height) 1443 curwin->w_topfill = curwin->w_height - n; 1444 } 1445 } 1446 #endif 1447 1448 #if defined(FEAT_INS_EXPAND) || defined(PROTO) 1449 /* 1450 * Scroll the screen one line down, but don't do it if it would move the 1451 * cursor off the screen. 1452 */ 1453 void 1454 scrolldown_clamp() 1455 { 1456 int end_row; 1457 #ifdef FEAT_DIFF 1458 int can_fill = (curwin->w_topfill 1459 < diff_check_fill(curwin, curwin->w_topline)); 1460 #endif 1461 1462 if (curwin->w_topline <= 1 1463 #ifdef FEAT_DIFF 1464 && !can_fill 1465 #endif 1466 ) 1467 return; 1468 1469 validate_cursor(); /* w_wrow needs to be valid */ 1470 1471 /* 1472 * Compute the row number of the last row of the cursor line 1473 * and make sure it doesn't go off the screen. Make sure the cursor 1474 * doesn't go past 'scrolloff' lines from the screen end. 1475 */ 1476 end_row = curwin->w_wrow; 1477 #ifdef FEAT_DIFF 1478 if (can_fill) 1479 ++end_row; 1480 else 1481 end_row += plines_nofill(curwin->w_topline - 1); 1482 #else 1483 end_row += plines(curwin->w_topline - 1); 1484 #endif 1485 if (curwin->w_p_wrap 1486 #ifdef FEAT_VERTSPLIT 1487 && curwin->w_width != 0 1488 #endif 1489 ) 1490 { 1491 validate_cheight(); 1492 validate_virtcol(); 1493 end_row += curwin->w_cline_height - 1 - 1494 curwin->w_virtcol / W_WIDTH(curwin); 1495 } 1496 if (end_row < curwin->w_height - p_so) 1497 { 1498 #ifdef FEAT_DIFF 1499 if (can_fill) 1500 { 1501 ++curwin->w_topfill; 1502 check_topfill(curwin, TRUE); 1503 } 1504 else 1505 { 1506 --curwin->w_topline; 1507 curwin->w_topfill = 0; 1508 } 1509 #else 1510 --curwin->w_topline; 1511 #endif 1512 #ifdef FEAT_FOLDING 1513 hasFolding(curwin->w_topline, &curwin->w_topline, NULL); 1514 #endif 1515 --curwin->w_botline; /* approximate w_botline */ 1516 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE); 1517 } 1518 } 1519 1520 /* 1521 * Scroll the screen one line up, but don't do it if it would move the cursor 1522 * off the screen. 1523 */ 1524 void 1525 scrollup_clamp() 1526 { 1527 int start_row; 1528 1529 if (curwin->w_topline == curbuf->b_ml.ml_line_count 1530 #ifdef FEAT_DIFF 1531 && curwin->w_topfill == 0 1532 #endif 1533 ) 1534 return; 1535 1536 validate_cursor(); /* w_wrow needs to be valid */ 1537 1538 /* 1539 * Compute the row number of the first row of the cursor line 1540 * and make sure it doesn't go off the screen. Make sure the cursor 1541 * doesn't go before 'scrolloff' lines from the screen start. 1542 */ 1543 #ifdef FEAT_DIFF 1544 start_row = curwin->w_wrow - plines_nofill(curwin->w_topline) 1545 - curwin->w_topfill; 1546 #else 1547 start_row = curwin->w_wrow - plines(curwin->w_topline); 1548 #endif 1549 if (curwin->w_p_wrap 1550 #ifdef FEAT_VERTSPLIT 1551 && curwin->w_width != 0 1552 #endif 1553 ) 1554 { 1555 validate_virtcol(); 1556 start_row -= curwin->w_virtcol / W_WIDTH(curwin); 1557 } 1558 if (start_row >= p_so) 1559 { 1560 #ifdef FEAT_DIFF 1561 if (curwin->w_topfill > 0) 1562 --curwin->w_topfill; 1563 else 1564 #endif 1565 { 1566 #ifdef FEAT_FOLDING 1567 (void)hasFolding(curwin->w_topline, NULL, &curwin->w_topline); 1568 #endif 1569 ++curwin->w_topline; 1570 } 1571 ++curwin->w_botline; /* approximate w_botline */ 1572 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE); 1573 } 1574 } 1575 #endif /* FEAT_INS_EXPAND */ 1576 1577 /* 1578 * Add one line above "lp->lnum". This can be a filler line, a closed fold or 1579 * a (wrapped) text line. Uses and sets "lp->fill". 1580 * Returns the height of the added line in "lp->height". 1581 * Lines above the first one are incredibly high. 1582 */ 1583 static void 1584 topline_back(lp) 1585 lineoff_T *lp; 1586 { 1587 #ifdef FEAT_DIFF 1588 if (lp->fill < diff_check_fill(curwin, lp->lnum)) 1589 { 1590 /* Add a filler line. */ 1591 ++lp->fill; 1592 lp->height = 1; 1593 } 1594 else 1595 #endif 1596 { 1597 --lp->lnum; 1598 #ifdef FEAT_DIFF 1599 lp->fill = 0; 1600 #endif 1601 if (lp->lnum < 1) 1602 lp->height = MAXCOL; 1603 else 1604 #ifdef FEAT_FOLDING 1605 if (hasFolding(lp->lnum, &lp->lnum, NULL)) 1606 /* Add a closed fold */ 1607 lp->height = 1; 1608 else 1609 #endif 1610 { 1611 #ifdef FEAT_DIFF 1612 lp->height = plines_nofill(lp->lnum); 1613 #else 1614 lp->height = plines(lp->lnum); 1615 #endif 1616 } 1617 } 1618 } 1619 1620 /* 1621 * Add one line below "lp->lnum". This can be a filler line, a closed fold or 1622 * a (wrapped) text line. Uses and sets "lp->fill". 1623 * Returns the height of the added line in "lp->height". 1624 * Lines below the last one are incredibly high. 1625 */ 1626 static void 1627 botline_forw(lp) 1628 lineoff_T *lp; 1629 { 1630 #ifdef FEAT_DIFF 1631 if (lp->fill < diff_check_fill(curwin, lp->lnum + 1)) 1632 { 1633 /* Add a filler line. */ 1634 ++lp->fill; 1635 lp->height = 1; 1636 } 1637 else 1638 #endif 1639 { 1640 ++lp->lnum; 1641 #ifdef FEAT_DIFF 1642 lp->fill = 0; 1643 #endif 1644 if (lp->lnum > curbuf->b_ml.ml_line_count) 1645 lp->height = MAXCOL; 1646 else 1647 #ifdef FEAT_FOLDING 1648 if (hasFolding(lp->lnum, NULL, &lp->lnum)) 1649 /* Add a closed fold */ 1650 lp->height = 1; 1651 else 1652 #endif 1653 { 1654 #ifdef FEAT_DIFF 1655 lp->height = plines_nofill(lp->lnum); 1656 #else 1657 lp->height = plines(lp->lnum); 1658 #endif 1659 } 1660 } 1661 } 1662 1663 #ifdef FEAT_DIFF 1664 /* 1665 * Switch from including filler lines below lp->lnum to including filler 1666 * lines above loff.lnum + 1. This keeps pointing to the same line. 1667 * When there are no filler lines nothing changes. 1668 */ 1669 static void 1670 botline_topline(lp) 1671 lineoff_T *lp; 1672 { 1673 if (lp->fill > 0) 1674 { 1675 ++lp->lnum; 1676 lp->fill = diff_check_fill(curwin, lp->lnum) - lp->fill + 1; 1677 } 1678 } 1679 1680 /* 1681 * Switch from including filler lines above lp->lnum to including filler 1682 * lines below loff.lnum - 1. This keeps pointing to the same line. 1683 * When there are no filler lines nothing changes. 1684 */ 1685 static void 1686 topline_botline(lp) 1687 lineoff_T *lp; 1688 { 1689 if (lp->fill > 0) 1690 { 1691 lp->fill = diff_check_fill(curwin, lp->lnum) - lp->fill + 1; 1692 --lp->lnum; 1693 } 1694 } 1695 #endif 1696 1697 /* 1698 * Recompute topline to put the cursor at the top of the window. 1699 * Scroll at least "min_scroll" lines. 1700 * If "always" is TRUE, always set topline (for "zt"). 1701 */ 1702 void 1703 scroll_cursor_top(min_scroll, always) 1704 int min_scroll; 1705 int always; 1706 { 1707 int scrolled = 0; 1708 int extra = 0; 1709 int used; 1710 int i; 1711 linenr_T top; /* just above displayed lines */ 1712 linenr_T bot; /* just below displayed lines */ 1713 linenr_T old_topline = curwin->w_topline; 1714 #ifdef FEAT_DIFF 1715 linenr_T old_topfill = curwin->w_topfill; 1716 #endif 1717 linenr_T new_topline; 1718 int off = p_so; 1719 1720 #ifdef FEAT_MOUSE 1721 if (mouse_dragging > 0) 1722 off = mouse_dragging - 1; 1723 #endif 1724 1725 /* 1726 * Decrease topline until: 1727 * - it has become 1 1728 * - (part of) the cursor line is moved off the screen or 1729 * - moved at least 'scrolljump' lines and 1730 * - at least 'scrolloff' lines above and below the cursor 1731 */ 1732 validate_cheight(); 1733 used = curwin->w_cline_height; 1734 if (curwin->w_cursor.lnum < curwin->w_topline) 1735 scrolled = used; 1736 1737 #ifdef FEAT_FOLDING 1738 if (hasFolding(curwin->w_cursor.lnum, &top, &bot)) 1739 { 1740 --top; 1741 ++bot; 1742 } 1743 else 1744 #endif 1745 { 1746 top = curwin->w_cursor.lnum - 1; 1747 bot = curwin->w_cursor.lnum + 1; 1748 } 1749 new_topline = top + 1; 1750 1751 #ifdef FEAT_DIFF 1752 /* count filler lines of the cursor window as context */ 1753 i = diff_check_fill(curwin, curwin->w_cursor.lnum); 1754 used += i; 1755 extra += i; 1756 #endif 1757 1758 /* 1759 * Check if the lines from "top" to "bot" fit in the window. If they do, 1760 * set new_topline and advance "top" and "bot" to include more lines. 1761 */ 1762 while (top > 0) 1763 { 1764 #ifdef FEAT_FOLDING 1765 if (hasFolding(top, &top, NULL)) 1766 /* count one logical line for a sequence of folded lines */ 1767 i = 1; 1768 else 1769 #endif 1770 i = plines(top); 1771 used += i; 1772 if (extra + i <= off && bot < curbuf->b_ml.ml_line_count) 1773 { 1774 #ifdef FEAT_FOLDING 1775 if (hasFolding(bot, NULL, &bot)) 1776 /* count one logical line for a sequence of folded lines */ 1777 ++used; 1778 else 1779 #endif 1780 used += plines(bot); 1781 } 1782 if (used > curwin->w_height) 1783 break; 1784 if (top < curwin->w_topline) 1785 scrolled += i; 1786 1787 /* 1788 * If scrolling is needed, scroll at least 'sj' lines. 1789 */ 1790 if ((new_topline >= curwin->w_topline || scrolled > min_scroll) 1791 && extra >= off) 1792 break; 1793 1794 extra += i; 1795 new_topline = top; 1796 --top; 1797 ++bot; 1798 } 1799 1800 /* 1801 * If we don't have enough space, put cursor in the middle. 1802 * This makes sure we get the same position when using "k" and "j" 1803 * in a small window. 1804 */ 1805 if (used > curwin->w_height) 1806 scroll_cursor_halfway(FALSE); 1807 else 1808 { 1809 /* 1810 * If "always" is FALSE, only adjust topline to a lower value, higher 1811 * value may happen with wrapping lines 1812 */ 1813 if (new_topline < curwin->w_topline || always) 1814 curwin->w_topline = new_topline; 1815 if (curwin->w_topline > curwin->w_cursor.lnum) 1816 curwin->w_topline = curwin->w_cursor.lnum; 1817 #ifdef FEAT_DIFF 1818 curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline); 1819 if (curwin->w_topfill > 0 && extra > off) 1820 { 1821 curwin->w_topfill -= extra - off; 1822 if (curwin->w_topfill < 0) 1823 curwin->w_topfill = 0; 1824 } 1825 check_topfill(curwin, FALSE); 1826 #endif 1827 if (curwin->w_topline != old_topline 1828 #ifdef FEAT_DIFF 1829 || curwin->w_topfill != old_topfill 1830 #endif 1831 ) 1832 curwin->w_valid &= 1833 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP); 1834 curwin->w_valid |= VALID_TOPLINE; 1835 } 1836 } 1837 1838 /* 1839 * Set w_empty_rows and w_filler_rows for window "wp", having used up "used" 1840 * screen lines for text lines. 1841 */ 1842 void 1843 set_empty_rows(wp, used) 1844 win_T *wp; 1845 int used; 1846 { 1847 #ifdef FEAT_DIFF 1848 wp->w_filler_rows = 0; 1849 #endif 1850 if (used == 0) 1851 wp->w_empty_rows = 0; /* single line that doesn't fit */ 1852 else 1853 { 1854 wp->w_empty_rows = wp->w_height - used; 1855 #ifdef FEAT_DIFF 1856 if (wp->w_botline <= wp->w_buffer->b_ml.ml_line_count) 1857 { 1858 wp->w_filler_rows = diff_check_fill(wp, wp->w_botline); 1859 if (wp->w_empty_rows > wp->w_filler_rows) 1860 wp->w_empty_rows -= wp->w_filler_rows; 1861 else 1862 { 1863 wp->w_filler_rows = wp->w_empty_rows; 1864 wp->w_empty_rows = 0; 1865 } 1866 } 1867 #endif 1868 } 1869 } 1870 1871 /* 1872 * Recompute topline to put the cursor at the bottom of the window. 1873 * Scroll at least "min_scroll" lines. 1874 * If "set_topbot" is TRUE, set topline and botline first (for "zb"). 1875 * This is messy stuff!!! 1876 */ 1877 void 1878 scroll_cursor_bot(min_scroll, set_topbot) 1879 int min_scroll; 1880 int set_topbot; 1881 { 1882 int used; 1883 int scrolled = 0; 1884 int extra = 0; 1885 int i; 1886 linenr_T line_count; 1887 linenr_T old_topline = curwin->w_topline; 1888 lineoff_T loff; 1889 lineoff_T boff; 1890 #ifdef FEAT_DIFF 1891 int old_topfill = curwin->w_topfill; 1892 int fill_below_window; 1893 #endif 1894 linenr_T old_botline = curwin->w_botline; 1895 linenr_T old_valid = curwin->w_valid; 1896 int old_empty_rows = curwin->w_empty_rows; 1897 linenr_T cln; /* Cursor Line Number */ 1898 1899 cln = curwin->w_cursor.lnum; 1900 if (set_topbot) 1901 { 1902 used = 0; 1903 curwin->w_botline = cln + 1; 1904 #ifdef FEAT_DIFF 1905 loff.fill = 0; 1906 #endif 1907 for (curwin->w_topline = curwin->w_botline; 1908 curwin->w_topline > 1; 1909 curwin->w_topline = loff.lnum) 1910 { 1911 loff.lnum = curwin->w_topline; 1912 topline_back(&loff); 1913 if (used + loff.height > curwin->w_height) 1914 break; 1915 used += loff.height; 1916 #ifdef FEAT_DIFF 1917 curwin->w_topfill = loff.fill; 1918 #endif 1919 } 1920 set_empty_rows(curwin, used); 1921 curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP; 1922 if (curwin->w_topline != old_topline 1923 #ifdef FEAT_DIFF 1924 || curwin->w_topfill != old_topfill 1925 #endif 1926 ) 1927 curwin->w_valid &= ~(VALID_WROW|VALID_CROW); 1928 } 1929 else 1930 validate_botline(); 1931 1932 /* The lines of the cursor line itself are always used. */ 1933 #ifdef FEAT_DIFF 1934 used = plines_nofill(cln); 1935 #else 1936 validate_cheight(); 1937 used = curwin->w_cline_height; 1938 #endif 1939 1940 /* If the cursor is below botline, we will at least scroll by the height 1941 * of the cursor line. Correct for empty lines, which are really part of 1942 * botline. */ 1943 if (cln >= curwin->w_botline) 1944 { 1945 scrolled = used; 1946 if (cln == curwin->w_botline) 1947 scrolled -= curwin->w_empty_rows; 1948 } 1949 1950 /* 1951 * Stop counting lines to scroll when 1952 * - hitting start of the file 1953 * - scrolled nothing or at least 'sj' lines 1954 * - at least 'so' lines below the cursor 1955 * - lines between botline and cursor have been counted 1956 */ 1957 #ifdef FEAT_FOLDING 1958 if (!hasFolding(curwin->w_cursor.lnum, &loff.lnum, &boff.lnum)) 1959 #endif 1960 { 1961 loff.lnum = cln; 1962 boff.lnum = cln; 1963 } 1964 #ifdef FEAT_DIFF 1965 loff.fill = 0; 1966 boff.fill = 0; 1967 fill_below_window = diff_check_fill(curwin, curwin->w_botline) 1968 - curwin->w_filler_rows; 1969 #endif 1970 1971 while (loff.lnum > 1) 1972 { 1973 /* Stop when scrolled nothing or at least "min_scroll", found "extra" 1974 * context for 'scrolloff' and counted all lines below the window. */ 1975 if ((((scrolled <= 0 || scrolled >= min_scroll) 1976 && extra >= ( 1977 #ifdef FEAT_MOUSE 1978 mouse_dragging ? mouse_dragging - 1 : 1979 #endif 1980 p_so)) 1981 || boff.lnum + 1 > curbuf->b_ml.ml_line_count) 1982 && loff.lnum <= curwin->w_botline 1983 #ifdef FEAT_DIFF 1984 && (loff.lnum < curwin->w_botline 1985 || loff.fill >= fill_below_window) 1986 #endif 1987 ) 1988 break; 1989 1990 /* Add one line above */ 1991 topline_back(&loff); 1992 used += loff.height; 1993 if (used > curwin->w_height) 1994 break; 1995 if (loff.lnum >= curwin->w_botline 1996 #ifdef FEAT_DIFF 1997 && (loff.lnum > curwin->w_botline 1998 || loff.fill <= fill_below_window) 1999 #endif 2000 ) 2001 { 2002 /* Count screen lines that are below the window. */ 2003 scrolled += loff.height; 2004 if (loff.lnum == curwin->w_botline 2005 #ifdef FEAT_DIFF 2006 && boff.fill == 0 2007 #endif 2008 ) 2009 scrolled -= curwin->w_empty_rows; 2010 } 2011 2012 if (boff.lnum < curbuf->b_ml.ml_line_count) 2013 { 2014 /* Add one line below */ 2015 botline_forw(&boff); 2016 used += boff.height; 2017 if (used > curwin->w_height) 2018 break; 2019 if (extra < ( 2020 #ifdef FEAT_MOUSE 2021 mouse_dragging > 0 ? mouse_dragging - 1 : 2022 #endif 2023 p_so) || scrolled < min_scroll) 2024 { 2025 extra += boff.height; 2026 if (boff.lnum >= curwin->w_botline 2027 #ifdef FEAT_DIFF 2028 || (boff.lnum + 1 == curwin->w_botline 2029 && boff.fill > curwin->w_filler_rows) 2030 #endif 2031 ) 2032 { 2033 /* Count screen lines that are below the window. */ 2034 scrolled += boff.height; 2035 if (boff.lnum == curwin->w_botline 2036 #ifdef FEAT_DIFF 2037 && boff.fill == 0 2038 #endif 2039 ) 2040 scrolled -= curwin->w_empty_rows; 2041 } 2042 } 2043 } 2044 } 2045 2046 /* curwin->w_empty_rows is larger, no need to scroll */ 2047 if (scrolled <= 0) 2048 line_count = 0; 2049 /* more than a screenfull, don't scroll but redraw */ 2050 else if (used > curwin->w_height) 2051 line_count = used; 2052 /* scroll minimal number of lines */ 2053 else 2054 { 2055 line_count = 0; 2056 #ifdef FEAT_DIFF 2057 boff.fill = curwin->w_topfill; 2058 #endif 2059 boff.lnum = curwin->w_topline - 1; 2060 for (i = 0; i < scrolled && boff.lnum < curwin->w_botline; ) 2061 { 2062 botline_forw(&boff); 2063 i += boff.height; 2064 ++line_count; 2065 } 2066 if (i < scrolled) /* below curwin->w_botline, don't scroll */ 2067 line_count = 9999; 2068 } 2069 2070 /* 2071 * Scroll up if the cursor is off the bottom of the screen a bit. 2072 * Otherwise put it at 1/2 of the screen. 2073 */ 2074 if (line_count >= curwin->w_height && line_count > min_scroll) 2075 scroll_cursor_halfway(FALSE); 2076 else 2077 scrollup(line_count, TRUE); 2078 2079 /* 2080 * If topline didn't change we need to restore w_botline and w_empty_rows 2081 * (we changed them). 2082 * If topline did change, update_screen() will set botline. 2083 */ 2084 if (curwin->w_topline == old_topline && set_topbot) 2085 { 2086 curwin->w_botline = old_botline; 2087 curwin->w_empty_rows = old_empty_rows; 2088 curwin->w_valid = old_valid; 2089 } 2090 curwin->w_valid |= VALID_TOPLINE; 2091 } 2092 2093 /* 2094 * Recompute topline to put the cursor halfway the window 2095 * If "atend" is TRUE, also put it halfway at the end of the file. 2096 */ 2097 void 2098 scroll_cursor_halfway(atend) 2099 int atend; 2100 { 2101 int above = 0; 2102 linenr_T topline; 2103 #ifdef FEAT_DIFF 2104 int topfill = 0; 2105 #endif 2106 int below = 0; 2107 int used; 2108 lineoff_T loff; 2109 lineoff_T boff; 2110 2111 loff.lnum = boff.lnum = curwin->w_cursor.lnum; 2112 #ifdef FEAT_FOLDING 2113 (void)hasFolding(loff.lnum, &loff.lnum, &boff.lnum); 2114 #endif 2115 #ifdef FEAT_DIFF 2116 used = plines_nofill(loff.lnum); 2117 loff.fill = 0; 2118 boff.fill = 0; 2119 #else 2120 used = plines(loff.lnum); 2121 #endif 2122 topline = loff.lnum; 2123 while (topline > 1) 2124 { 2125 if (below <= above) /* add a line below the cursor first */ 2126 { 2127 if (boff.lnum < curbuf->b_ml.ml_line_count) 2128 { 2129 botline_forw(&boff); 2130 used += boff.height; 2131 if (used > curwin->w_height) 2132 break; 2133 below += boff.height; 2134 } 2135 else 2136 { 2137 ++below; /* count a "~" line */ 2138 if (atend) 2139 ++used; 2140 } 2141 } 2142 2143 if (below > above) /* add a line above the cursor */ 2144 { 2145 topline_back(&loff); 2146 used += loff.height; 2147 if (used > curwin->w_height) 2148 break; 2149 above += loff.height; 2150 topline = loff.lnum; 2151 #ifdef FEAT_DIFF 2152 topfill = loff.fill; 2153 #endif 2154 } 2155 } 2156 #ifdef FEAT_FOLDING 2157 if (!hasFolding(topline, &curwin->w_topline, NULL)) 2158 #endif 2159 curwin->w_topline = topline; 2160 #ifdef FEAT_DIFF 2161 curwin->w_topfill = topfill; 2162 check_topfill(curwin, FALSE); 2163 #endif 2164 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP); 2165 curwin->w_valid |= VALID_TOPLINE; 2166 } 2167 2168 /* 2169 * Correct the cursor position so that it is in a part of the screen at least 2170 * 'so' lines from the top and bottom, if possible. 2171 * If not possible, put it at the same position as scroll_cursor_halfway(). 2172 * When called topline must be valid! 2173 */ 2174 void 2175 cursor_correct() 2176 { 2177 int above = 0; /* screen lines above topline */ 2178 linenr_T topline; 2179 int below = 0; /* screen lines below botline */ 2180 linenr_T botline; 2181 int above_wanted, below_wanted; 2182 linenr_T cln; /* Cursor Line Number */ 2183 int max_off; 2184 2185 /* 2186 * How many lines we would like to have above/below the cursor depends on 2187 * whether the first/last line of the file is on screen. 2188 */ 2189 above_wanted = p_so; 2190 below_wanted = p_so; 2191 #ifdef FEAT_MOUSE 2192 if (mouse_dragging) 2193 { 2194 above_wanted = mouse_dragging - 1; 2195 below_wanted = mouse_dragging - 1; 2196 } 2197 #endif 2198 if (curwin->w_topline == 1) 2199 { 2200 above_wanted = 0; 2201 max_off = curwin->w_height / 2; 2202 if (below_wanted > max_off) 2203 below_wanted = max_off; 2204 } 2205 validate_botline(); 2206 if (curwin->w_botline == curbuf->b_ml.ml_line_count + 1 2207 #ifdef FEAT_MOUSE 2208 && !mouse_dragging 2209 #endif 2210 ) 2211 { 2212 below_wanted = 0; 2213 max_off = (curwin->w_height - 1) / 2; 2214 if (above_wanted > max_off) 2215 above_wanted = max_off; 2216 } 2217 2218 /* 2219 * If there are sufficient file-lines above and below the cursor, we can 2220 * return now. 2221 */ 2222 cln = curwin->w_cursor.lnum; 2223 if (cln >= curwin->w_topline + above_wanted 2224 && cln < curwin->w_botline - below_wanted 2225 #ifdef FEAT_FOLDING 2226 && !hasAnyFolding(curwin) 2227 #endif 2228 ) 2229 return; 2230 2231 /* 2232 * Narrow down the area where the cursor can be put by taking lines from 2233 * the top and the bottom until: 2234 * - the desired context lines are found 2235 * - the lines from the top is past the lines from the bottom 2236 */ 2237 topline = curwin->w_topline; 2238 botline = curwin->w_botline - 1; 2239 #ifdef FEAT_DIFF 2240 /* count filler lines as context */ 2241 above = curwin->w_topfill; 2242 below = curwin->w_filler_rows; 2243 #endif 2244 while ((above < above_wanted || below < below_wanted) && topline < botline) 2245 { 2246 if (below < below_wanted && (below <= above || above >= above_wanted)) 2247 { 2248 #ifdef FEAT_FOLDING 2249 if (hasFolding(botline, &botline, NULL)) 2250 ++below; 2251 else 2252 #endif 2253 below += plines(botline); 2254 --botline; 2255 } 2256 if (above < above_wanted && (above < below || below >= below_wanted)) 2257 { 2258 #ifdef FEAT_FOLDING 2259 if (hasFolding(topline, NULL, &topline)) 2260 ++above; 2261 else 2262 #endif 2263 #ifndef FEAT_DIFF 2264 above += plines(topline); 2265 #else 2266 above += plines_nofill(topline); 2267 2268 /* Count filler lines below this line as context. */ 2269 if (topline < botline) 2270 above += diff_check_fill(curwin, topline + 1); 2271 #endif 2272 ++topline; 2273 } 2274 } 2275 if (topline == botline || botline == 0) 2276 curwin->w_cursor.lnum = topline; 2277 else if (topline > botline) 2278 curwin->w_cursor.lnum = botline; 2279 else 2280 { 2281 if (cln < topline && curwin->w_topline > 1) 2282 { 2283 curwin->w_cursor.lnum = topline; 2284 curwin->w_valid &= 2285 ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW); 2286 } 2287 if (cln > botline && curwin->w_botline <= curbuf->b_ml.ml_line_count) 2288 { 2289 curwin->w_cursor.lnum = botline; 2290 curwin->w_valid &= 2291 ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW); 2292 } 2293 } 2294 curwin->w_valid |= VALID_TOPLINE; 2295 } 2296 2297 static void get_scroll_overlap __ARGS((lineoff_T *lp, int dir)); 2298 2299 /* 2300 * move screen 'count' pages up or down and update screen 2301 * 2302 * return FAIL for failure, OK otherwise 2303 */ 2304 int 2305 onepage(dir, count) 2306 int dir; 2307 long count; 2308 { 2309 long n; 2310 int retval = OK; 2311 lineoff_T loff; 2312 linenr_T old_topline = curwin->w_topline; 2313 2314 if (curbuf->b_ml.ml_line_count == 1) /* nothing to do */ 2315 { 2316 beep_flush(); 2317 return FAIL; 2318 } 2319 2320 for ( ; count > 0; --count) 2321 { 2322 validate_botline(); 2323 /* 2324 * It's an error to move a page up when the first line is already on 2325 * the screen. It's an error to move a page down when the last line 2326 * is on the screen and the topline is 'scrolloff' lines from the 2327 * last line. 2328 */ 2329 if (dir == FORWARD 2330 ? ((curwin->w_topline >= curbuf->b_ml.ml_line_count - p_so) 2331 && curwin->w_botline > curbuf->b_ml.ml_line_count) 2332 : (curwin->w_topline == 1 2333 #ifdef FEAT_DIFF 2334 && curwin->w_topfill == 2335 diff_check_fill(curwin, curwin->w_topline) 2336 #endif 2337 )) 2338 { 2339 beep_flush(); 2340 retval = FAIL; 2341 break; 2342 } 2343 2344 #ifdef FEAT_DIFF 2345 loff.fill = 0; 2346 #endif 2347 if (dir == FORWARD) 2348 { 2349 if (firstwin == lastwin && p_window > 0 && p_window < Rows - 1) 2350 { 2351 /* Vi compatible scrolling */ 2352 if (p_window <= 2) 2353 ++curwin->w_topline; 2354 else 2355 curwin->w_topline += p_window - 2; 2356 if (curwin->w_topline > curbuf->b_ml.ml_line_count) 2357 curwin->w_topline = curbuf->b_ml.ml_line_count; 2358 curwin->w_cursor.lnum = curwin->w_topline; 2359 } 2360 else if (curwin->w_botline > curbuf->b_ml.ml_line_count) 2361 { 2362 /* at end of file */ 2363 curwin->w_topline = curbuf->b_ml.ml_line_count; 2364 #ifdef FEAT_DIFF 2365 curwin->w_topfill = 0; 2366 #endif 2367 curwin->w_valid &= ~(VALID_WROW|VALID_CROW); 2368 } 2369 else 2370 { 2371 /* For the overlap, start with the line just below the window 2372 * and go upwards. */ 2373 loff.lnum = curwin->w_botline; 2374 #ifdef FEAT_DIFF 2375 loff.fill = diff_check_fill(curwin, loff.lnum) 2376 - curwin->w_filler_rows; 2377 #endif 2378 get_scroll_overlap(&loff, -1); 2379 curwin->w_topline = loff.lnum; 2380 #ifdef FEAT_DIFF 2381 curwin->w_topfill = loff.fill; 2382 check_topfill(curwin, FALSE); 2383 #endif 2384 curwin->w_cursor.lnum = curwin->w_topline; 2385 curwin->w_valid &= ~(VALID_WCOL|VALID_CHEIGHT|VALID_WROW| 2386 VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP); 2387 } 2388 } 2389 else /* dir == BACKWARDS */ 2390 { 2391 #ifdef FEAT_DIFF 2392 if (curwin->w_topline == 1) 2393 { 2394 /* Include max number of filler lines */ 2395 max_topfill(); 2396 continue; 2397 } 2398 #endif 2399 if (firstwin == lastwin && p_window > 0 && p_window < Rows - 1) 2400 { 2401 /* Vi compatible scrolling (sort of) */ 2402 if (p_window <= 2) 2403 --curwin->w_topline; 2404 else 2405 curwin->w_topline -= p_window - 2; 2406 if (curwin->w_topline < 1) 2407 curwin->w_topline = 1; 2408 curwin->w_cursor.lnum = curwin->w_topline + p_window - 1; 2409 if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) 2410 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; 2411 continue; 2412 } 2413 2414 /* Find the line at the top of the window that is going to be the 2415 * line at the bottom of the window. Make sure this results in 2416 * the same line as before doing CTRL-F. */ 2417 loff.lnum = curwin->w_topline - 1; 2418 #ifdef FEAT_DIFF 2419 loff.fill = diff_check_fill(curwin, loff.lnum + 1) 2420 - curwin->w_topfill; 2421 #endif 2422 get_scroll_overlap(&loff, 1); 2423 2424 if (loff.lnum >= curbuf->b_ml.ml_line_count) 2425 { 2426 loff.lnum = curbuf->b_ml.ml_line_count; 2427 #ifdef FEAT_DIFF 2428 loff.fill = 0; 2429 } 2430 else 2431 { 2432 botline_topline(&loff); 2433 #endif 2434 } 2435 curwin->w_cursor.lnum = loff.lnum; 2436 2437 /* Find the line just above the new topline to get the right line 2438 * at the bottom of the window. */ 2439 n = 0; 2440 while (n <= curwin->w_height && loff.lnum >= 1) 2441 { 2442 topline_back(&loff); 2443 n += loff.height; 2444 } 2445 if (n <= curwin->w_height) /* at begin of file */ 2446 { 2447 curwin->w_topline = 1; 2448 #ifdef FEAT_DIFF 2449 max_topfill(); 2450 #endif 2451 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE); 2452 } 2453 else 2454 { 2455 /* Go two lines forward again. */ 2456 #ifdef FEAT_DIFF 2457 topline_botline(&loff); 2458 #endif 2459 botline_forw(&loff); 2460 botline_forw(&loff); 2461 #ifdef FEAT_DIFF 2462 botline_topline(&loff); 2463 #endif 2464 #ifdef FEAT_FOLDING 2465 /* We're at the wrong end of a fold now. */ 2466 (void)hasFolding(loff.lnum, &loff.lnum, NULL); 2467 #endif 2468 2469 /* Always scroll at least one line. Avoid getting stuck on 2470 * very long lines. */ 2471 if (loff.lnum >= curwin->w_topline 2472 #ifdef FEAT_DIFF 2473 && (loff.lnum > curwin->w_topline 2474 || loff.fill >= curwin->w_topfill) 2475 #endif 2476 ) 2477 { 2478 #ifdef FEAT_DIFF 2479 /* First try using the maximum number of filler lines. If 2480 * that's not enough, backup one line. */ 2481 loff.fill = curwin->w_topfill; 2482 if (curwin->w_topfill < diff_check_fill(curwin, 2483 curwin->w_topline)) 2484 max_topfill(); 2485 if (curwin->w_topfill == loff.fill) 2486 #endif 2487 { 2488 --curwin->w_topline; 2489 #ifdef FEAT_DIFF 2490 curwin->w_topfill = 0; 2491 #endif 2492 } 2493 comp_botline(curwin); 2494 curwin->w_cursor.lnum = curwin->w_botline - 1; 2495 curwin->w_valid &= ~(VALID_WCOL|VALID_CHEIGHT| 2496 VALID_WROW|VALID_CROW); 2497 } 2498 else 2499 { 2500 curwin->w_topline = loff.lnum; 2501 #ifdef FEAT_DIFF 2502 curwin->w_topfill = loff.fill; 2503 check_topfill(curwin, FALSE); 2504 #endif 2505 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE); 2506 } 2507 } 2508 } 2509 } 2510 #ifdef FEAT_FOLDING 2511 foldAdjustCursor(); 2512 #endif 2513 cursor_correct(); 2514 if (retval == OK) 2515 beginline(BL_SOL | BL_FIX); 2516 curwin->w_valid &= ~(VALID_WCOL|VALID_WROW|VALID_VIRTCOL); 2517 2518 /* 2519 * Avoid the screen jumping up and down when 'scrolloff' is non-zero. 2520 * But make sure we scroll at least one line (happens with mix of long 2521 * wrapping lines and non-wrapping line). 2522 */ 2523 if (retval == OK && dir == FORWARD && check_top_offset()) 2524 { 2525 scroll_cursor_top(1, FALSE); 2526 if (curwin->w_topline <= old_topline 2527 && old_topline < curbuf->b_ml.ml_line_count) 2528 { 2529 curwin->w_topline = old_topline + 1; 2530 #ifdef FEAT_FOLDING 2531 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); 2532 #endif 2533 } 2534 } 2535 2536 redraw_later(VALID); 2537 return retval; 2538 } 2539 2540 /* 2541 * Decide how much overlap to use for page-up or page-down scrolling. 2542 * This is symmetric, so that doing both keeps the same lines displayed. 2543 * Three lines are examined: 2544 * 2545 * before CTRL-F after CTRL-F / before CTRL-B 2546 * etc. l1 2547 * l1 last but one line ------------ 2548 * l2 last text line l2 top text line 2549 * ------------- l3 second text line 2550 * l3 etc. 2551 */ 2552 static void 2553 get_scroll_overlap(lp, dir) 2554 lineoff_T *lp; 2555 int dir; 2556 { 2557 int h1, h2, h3, h4; 2558 int min_height = curwin->w_height - 2; 2559 lineoff_T loff0, loff1, loff2; 2560 2561 #ifdef FEAT_DIFF 2562 if (lp->fill > 0) 2563 lp->height = 1; 2564 else 2565 lp->height = plines_nofill(lp->lnum); 2566 #else 2567 lp->height = plines(lp->lnum); 2568 #endif 2569 h1 = lp->height; 2570 if (h1 > min_height) 2571 return; /* no overlap */ 2572 2573 loff0 = *lp; 2574 if (dir > 0) 2575 botline_forw(lp); 2576 else 2577 topline_back(lp); 2578 h2 = lp->height; 2579 if (h2 + h1 > min_height) 2580 { 2581 *lp = loff0; /* no overlap */ 2582 return; 2583 } 2584 2585 loff1 = *lp; 2586 if (dir > 0) 2587 botline_forw(lp); 2588 else 2589 topline_back(lp); 2590 h3 = lp->height; 2591 if (h3 + h2 > min_height) 2592 { 2593 *lp = loff0; /* no overlap */ 2594 return; 2595 } 2596 2597 loff2 = *lp; 2598 if (dir > 0) 2599 botline_forw(lp); 2600 else 2601 topline_back(lp); 2602 h4 = lp->height; 2603 if (h4 + h3 + h2 > min_height || h3 + h2 + h1 > min_height) 2604 *lp = loff1; /* 1 line overlap */ 2605 else 2606 *lp = loff2; /* 2 lines overlap */ 2607 return; 2608 } 2609 2610 /* #define KEEP_SCREEN_LINE */ 2611 /* 2612 * Scroll 'scroll' lines up or down. 2613 */ 2614 void 2615 halfpage(flag, Prenum) 2616 int flag; 2617 linenr_T Prenum; 2618 { 2619 long scrolled = 0; 2620 int i; 2621 int n; 2622 int room; 2623 2624 if (Prenum) 2625 curwin->w_p_scr = (Prenum > curwin->w_height) ? 2626 curwin->w_height : Prenum; 2627 n = (curwin->w_p_scr <= curwin->w_height) ? 2628 curwin->w_p_scr : curwin->w_height; 2629 2630 validate_botline(); 2631 room = curwin->w_empty_rows; 2632 #ifdef FEAT_DIFF 2633 room += curwin->w_filler_rows; 2634 #endif 2635 if (flag) 2636 { 2637 /* 2638 * scroll the text up 2639 */ 2640 while (n > 0 && curwin->w_botline <= curbuf->b_ml.ml_line_count) 2641 { 2642 #ifdef FEAT_DIFF 2643 if (curwin->w_topfill > 0) 2644 { 2645 i = 1; 2646 if (--n < 0 && scrolled > 0) 2647 break; 2648 --curwin->w_topfill; 2649 } 2650 else 2651 #endif 2652 { 2653 #ifdef FEAT_DIFF 2654 i = plines_nofill(curwin->w_topline); 2655 #else 2656 i = plines(curwin->w_topline); 2657 #endif 2658 n -= i; 2659 if (n < 0 && scrolled > 0) 2660 break; 2661 #ifdef FEAT_FOLDING 2662 (void)hasFolding(curwin->w_topline, NULL, &curwin->w_topline); 2663 #endif 2664 ++curwin->w_topline; 2665 #ifdef FEAT_DIFF 2666 curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline); 2667 #endif 2668 2669 #ifndef KEEP_SCREEN_LINE 2670 if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) 2671 { 2672 ++curwin->w_cursor.lnum; 2673 curwin->w_valid &= 2674 ~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL); 2675 } 2676 #endif 2677 } 2678 curwin->w_valid &= ~(VALID_CROW|VALID_WROW); 2679 scrolled += i; 2680 2681 /* 2682 * Correct w_botline for changed w_topline. 2683 * Won't work when there are filler lines. 2684 */ 2685 #ifdef FEAT_DIFF 2686 if (curwin->w_p_diff) 2687 curwin->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP); 2688 else 2689 #endif 2690 { 2691 room += i; 2692 do 2693 { 2694 i = plines(curwin->w_botline); 2695 if (i > room) 2696 break; 2697 #ifdef FEAT_FOLDING 2698 (void)hasFolding(curwin->w_botline, NULL, 2699 &curwin->w_botline); 2700 #endif 2701 ++curwin->w_botline; 2702 room -= i; 2703 } while (curwin->w_botline <= curbuf->b_ml.ml_line_count); 2704 } 2705 } 2706 2707 #ifndef KEEP_SCREEN_LINE 2708 /* 2709 * When hit bottom of the file: move cursor down. 2710 */ 2711 if (n > 0) 2712 { 2713 # ifdef FEAT_FOLDING 2714 if (hasAnyFolding(curwin)) 2715 { 2716 while (--n >= 0 2717 && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) 2718 { 2719 (void)hasFolding(curwin->w_cursor.lnum, NULL, 2720 &curwin->w_cursor.lnum); 2721 ++curwin->w_cursor.lnum; 2722 } 2723 } 2724 else 2725 # endif 2726 curwin->w_cursor.lnum += n; 2727 check_cursor_lnum(); 2728 } 2729 #else 2730 /* try to put the cursor in the same screen line */ 2731 while ((curwin->w_cursor.lnum < curwin->w_topline || scrolled > 0) 2732 && curwin->w_cursor.lnum < curwin->w_botline - 1) 2733 { 2734 scrolled -= plines(curwin->w_cursor.lnum); 2735 if (scrolled < 0 && curwin->w_cursor.lnum >= curwin->w_topline) 2736 break; 2737 # ifdef FEAT_FOLDING 2738 (void)hasFolding(curwin->w_cursor.lnum, NULL, 2739 &curwin->w_cursor.lnum); 2740 # endif 2741 ++curwin->w_cursor.lnum; 2742 } 2743 #endif 2744 } 2745 else 2746 { 2747 /* 2748 * scroll the text down 2749 */ 2750 while (n > 0 && curwin->w_topline > 1) 2751 { 2752 #ifdef FEAT_DIFF 2753 if (curwin->w_topfill < diff_check_fill(curwin, curwin->w_topline)) 2754 { 2755 i = 1; 2756 if (--n < 0 && scrolled > 0) 2757 break; 2758 ++curwin->w_topfill; 2759 } 2760 else 2761 #endif 2762 { 2763 #ifdef FEAT_DIFF 2764 i = plines_nofill(curwin->w_topline - 1); 2765 #else 2766 i = plines(curwin->w_topline - 1); 2767 #endif 2768 n -= i; 2769 if (n < 0 && scrolled > 0) 2770 break; 2771 --curwin->w_topline; 2772 #ifdef FEAT_FOLDING 2773 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); 2774 #endif 2775 #ifdef FEAT_DIFF 2776 curwin->w_topfill = 0; 2777 #endif 2778 } 2779 curwin->w_valid &= ~(VALID_CROW|VALID_WROW| 2780 VALID_BOTLINE|VALID_BOTLINE_AP); 2781 scrolled += i; 2782 #ifndef KEEP_SCREEN_LINE 2783 if (curwin->w_cursor.lnum > 1) 2784 { 2785 --curwin->w_cursor.lnum; 2786 curwin->w_valid &= ~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL); 2787 } 2788 #endif 2789 } 2790 #ifndef KEEP_SCREEN_LINE 2791 /* 2792 * When hit top of the file: move cursor up. 2793 */ 2794 if (n > 0) 2795 { 2796 if (curwin->w_cursor.lnum <= (linenr_T)n) 2797 curwin->w_cursor.lnum = 1; 2798 else 2799 # ifdef FEAT_FOLDING 2800 if (hasAnyFolding(curwin)) 2801 { 2802 while (--n >= 0 && curwin->w_cursor.lnum > 1) 2803 { 2804 --curwin->w_cursor.lnum; 2805 (void)hasFolding(curwin->w_cursor.lnum, 2806 &curwin->w_cursor.lnum, NULL); 2807 } 2808 } 2809 else 2810 # endif 2811 curwin->w_cursor.lnum -= n; 2812 } 2813 #else 2814 /* try to put the cursor in the same screen line */ 2815 scrolled += n; /* move cursor when topline is 1 */ 2816 while (curwin->w_cursor.lnum > curwin->w_topline 2817 && (scrolled > 0 || curwin->w_cursor.lnum >= curwin->w_botline)) 2818 { 2819 scrolled -= plines(curwin->w_cursor.lnum - 1); 2820 if (scrolled < 0 && curwin->w_cursor.lnum < curwin->w_botline) 2821 break; 2822 --curwin->w_cursor.lnum; 2823 # ifdef FEAT_FOLDING 2824 foldAdjustCursor(); 2825 # endif 2826 } 2827 #endif 2828 } 2829 # ifdef FEAT_FOLDING 2830 /* Move cursor to first line of closed fold. */ 2831 foldAdjustCursor(); 2832 # endif 2833 #ifdef FEAT_DIFF 2834 check_topfill(curwin, !flag); 2835 #endif 2836 cursor_correct(); 2837 beginline(BL_SOL | BL_FIX); 2838 redraw_later(VALID); 2839 } 2840