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