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