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