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 #if defined(FEAT_EVAL) || defined(PROTO) 1193 /* 1194 * Compute the screen position of text character at "pos" in window "wp" 1195 * The resulting values are one-based, zero when character is not visible. 1196 */ 1197 static void 1198 textpos2screenpos( 1199 win_T *wp, 1200 pos_T *pos, 1201 int *rowp, // screen row 1202 int *scolp, // start screen column 1203 int *ccolp, // cursor screen column 1204 int *ecolp) // end screen column 1205 { 1206 colnr_T scol = 0, ccol = 0, ecol = 0; 1207 int row = 0; 1208 int rowoff = 0; 1209 colnr_T coloff = 0; 1210 1211 if (pos->lnum >= wp->w_topline && pos->lnum < wp->w_botline) 1212 { 1213 colnr_T off; 1214 colnr_T col; 1215 int width; 1216 1217 row = plines_m_win(wp, wp->w_topline, pos->lnum - 1) + 1; 1218 getvcol(wp, pos, &scol, &ccol, &ecol); 1219 1220 // similar to what is done in validate_cursor_col() 1221 col = scol; 1222 off = win_col_off(wp); 1223 col += off; 1224 width = wp->w_width - off + win_col_off2(wp); 1225 1226 /* long line wrapping, adjust row */ 1227 if (wp->w_p_wrap 1228 && col >= (colnr_T)wp->w_width 1229 && width > 0) 1230 { 1231 /* use same formula as what is used in curs_columns() */ 1232 rowoff = ((col - wp->w_width) / width + 1); 1233 col -= rowoff * width; 1234 } 1235 col -= wp->w_leftcol; 1236 if (col >= width) 1237 col = -1; 1238 if (col >= 0) 1239 coloff = col - scol + wp->w_wincol + 1; 1240 else 1241 // character is left or right of the window 1242 row = scol = ccol = ecol = 0; 1243 } 1244 *rowp = wp->w_winrow + row + rowoff; 1245 *scolp = scol + coloff; 1246 *ccolp = ccol + coloff; 1247 *ecolp = ecol + coloff; 1248 } 1249 1250 /* 1251 * "screenpos({winid}, {lnum}, {col})" function 1252 */ 1253 void 1254 f_screenpos(typval_T *argvars UNUSED, typval_T *rettv) 1255 { 1256 dict_T *dict; 1257 win_T *wp; 1258 pos_T pos; 1259 int row = 0; 1260 int scol = 0, ccol = 0, ecol = 0; 1261 1262 if (rettv_dict_alloc(rettv) != OK) 1263 return; 1264 dict = rettv->vval.v_dict; 1265 1266 wp = find_win_by_nr_or_id(&argvars[0]); 1267 if (wp == NULL) 1268 return; 1269 1270 pos.lnum = tv_get_number(&argvars[1]); 1271 pos.col = tv_get_number(&argvars[2]) - 1; 1272 pos.coladd = 0; 1273 textpos2screenpos(wp, &pos, &row, &scol, &ccol, &ecol); 1274 1275 dict_add_number(dict, "row", row); 1276 dict_add_number(dict, "col", scol); 1277 dict_add_number(dict, "curscol", ccol); 1278 dict_add_number(dict, "endcol", ecol); 1279 } 1280 #endif 1281 1282 /* 1283 * Scroll the current window down by "line_count" logical lines. "CTRL-Y" 1284 */ 1285 void 1286 scrolldown( 1287 long line_count, 1288 int byfold UNUSED) /* TRUE: count a closed fold as one line */ 1289 { 1290 long done = 0; /* total # of physical lines done */ 1291 int wrow; 1292 int moved = FALSE; 1293 1294 #ifdef FEAT_FOLDING 1295 linenr_T first; 1296 1297 /* Make sure w_topline is at the first of a sequence of folded lines. */ 1298 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); 1299 #endif 1300 validate_cursor(); /* w_wrow needs to be valid */ 1301 while (line_count-- > 0) 1302 { 1303 #ifdef FEAT_DIFF 1304 if (curwin->w_topfill < diff_check(curwin, curwin->w_topline) 1305 && curwin->w_topfill < curwin->w_height - 1) 1306 { 1307 ++curwin->w_topfill; 1308 ++done; 1309 } 1310 else 1311 #endif 1312 { 1313 if (curwin->w_topline == 1) 1314 break; 1315 --curwin->w_topline; 1316 #ifdef FEAT_DIFF 1317 curwin->w_topfill = 0; 1318 #endif 1319 #ifdef FEAT_FOLDING 1320 /* A sequence of folded lines only counts for one logical line */ 1321 if (hasFolding(curwin->w_topline, &first, NULL)) 1322 { 1323 ++done; 1324 if (!byfold) 1325 line_count -= curwin->w_topline - first - 1; 1326 curwin->w_botline -= curwin->w_topline - first; 1327 curwin->w_topline = first; 1328 } 1329 else 1330 #endif 1331 done += PLINES_NOFILL(curwin->w_topline); 1332 } 1333 --curwin->w_botline; /* approximate w_botline */ 1334 invalidate_botline(); 1335 } 1336 curwin->w_wrow += done; /* keep w_wrow updated */ 1337 curwin->w_cline_row += done; /* keep w_cline_row updated */ 1338 1339 #ifdef FEAT_DIFF 1340 if (curwin->w_cursor.lnum == curwin->w_topline) 1341 curwin->w_cline_row = 0; 1342 check_topfill(curwin, TRUE); 1343 #endif 1344 1345 /* 1346 * Compute the row number of the last row of the cursor line 1347 * and move the cursor onto the displayed part of the window. 1348 */ 1349 wrow = curwin->w_wrow; 1350 if (curwin->w_p_wrap && curwin->w_width != 0) 1351 { 1352 validate_virtcol(); 1353 validate_cheight(); 1354 wrow += curwin->w_cline_height - 1 - 1355 curwin->w_virtcol / curwin->w_width; 1356 } 1357 while (wrow >= curwin->w_height && curwin->w_cursor.lnum > 1) 1358 { 1359 #ifdef FEAT_FOLDING 1360 if (hasFolding(curwin->w_cursor.lnum, &first, NULL)) 1361 { 1362 --wrow; 1363 if (first == 1) 1364 curwin->w_cursor.lnum = 1; 1365 else 1366 curwin->w_cursor.lnum = first - 1; 1367 } 1368 else 1369 #endif 1370 wrow -= plines(curwin->w_cursor.lnum--); 1371 curwin->w_valid &= 1372 ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL); 1373 moved = TRUE; 1374 } 1375 if (moved) 1376 { 1377 #ifdef FEAT_FOLDING 1378 /* Move cursor to first line of closed fold. */ 1379 foldAdjustCursor(); 1380 #endif 1381 coladvance(curwin->w_curswant); 1382 } 1383 } 1384 1385 /* 1386 * Scroll the current window up by "line_count" logical lines. "CTRL-E" 1387 */ 1388 void 1389 scrollup( 1390 long line_count, 1391 int byfold UNUSED) /* TRUE: count a closed fold as one line */ 1392 { 1393 #if defined(FEAT_FOLDING) || defined(FEAT_DIFF) 1394 linenr_T lnum; 1395 1396 if ( 1397 # ifdef FEAT_FOLDING 1398 (byfold && hasAnyFolding(curwin)) 1399 # ifdef FEAT_DIFF 1400 || 1401 # endif 1402 # endif 1403 # ifdef FEAT_DIFF 1404 curwin->w_p_diff 1405 # endif 1406 ) 1407 { 1408 /* count each sequence of folded lines as one logical line */ 1409 lnum = curwin->w_topline; 1410 while (line_count--) 1411 { 1412 # ifdef FEAT_DIFF 1413 if (curwin->w_topfill > 0) 1414 --curwin->w_topfill; 1415 else 1416 # endif 1417 { 1418 # ifdef FEAT_FOLDING 1419 if (byfold) 1420 (void)hasFolding(lnum, NULL, &lnum); 1421 # endif 1422 if (lnum >= curbuf->b_ml.ml_line_count) 1423 break; 1424 ++lnum; 1425 # ifdef FEAT_DIFF 1426 curwin->w_topfill = diff_check_fill(curwin, lnum); 1427 # endif 1428 } 1429 } 1430 /* approximate w_botline */ 1431 curwin->w_botline += lnum - curwin->w_topline; 1432 curwin->w_topline = lnum; 1433 } 1434 else 1435 #endif 1436 { 1437 curwin->w_topline += line_count; 1438 curwin->w_botline += line_count; /* approximate w_botline */ 1439 } 1440 1441 if (curwin->w_topline > curbuf->b_ml.ml_line_count) 1442 curwin->w_topline = curbuf->b_ml.ml_line_count; 1443 if (curwin->w_botline > curbuf->b_ml.ml_line_count + 1) 1444 curwin->w_botline = curbuf->b_ml.ml_line_count + 1; 1445 1446 #ifdef FEAT_DIFF 1447 check_topfill(curwin, FALSE); 1448 #endif 1449 1450 #ifdef FEAT_FOLDING 1451 if (hasAnyFolding(curwin)) 1452 /* Make sure w_topline is at the first of a sequence of folded lines. */ 1453 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); 1454 #endif 1455 1456 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE); 1457 if (curwin->w_cursor.lnum < curwin->w_topline) 1458 { 1459 curwin->w_cursor.lnum = curwin->w_topline; 1460 curwin->w_valid &= 1461 ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL); 1462 coladvance(curwin->w_curswant); 1463 } 1464 } 1465 1466 #ifdef FEAT_DIFF 1467 /* 1468 * Don't end up with too many filler lines in the window. 1469 */ 1470 void 1471 check_topfill( 1472 win_T *wp, 1473 int down) /* when TRUE scroll down when not enough space */ 1474 { 1475 int n; 1476 1477 if (wp->w_topfill > 0) 1478 { 1479 n = plines_win_nofill(wp, wp->w_topline, TRUE); 1480 if (wp->w_topfill + n > wp->w_height) 1481 { 1482 if (down && wp->w_topline > 1) 1483 { 1484 --wp->w_topline; 1485 wp->w_topfill = 0; 1486 } 1487 else 1488 { 1489 wp->w_topfill = wp->w_height - n; 1490 if (wp->w_topfill < 0) 1491 wp->w_topfill = 0; 1492 } 1493 } 1494 } 1495 } 1496 1497 /* 1498 * Use as many filler lines as possible for w_topline. Make sure w_topline 1499 * is still visible. 1500 */ 1501 static void 1502 max_topfill(void) 1503 { 1504 int n; 1505 1506 n = plines_nofill(curwin->w_topline); 1507 if (n >= curwin->w_height) 1508 curwin->w_topfill = 0; 1509 else 1510 { 1511 curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline); 1512 if (curwin->w_topfill + n > curwin->w_height) 1513 curwin->w_topfill = curwin->w_height - n; 1514 } 1515 } 1516 #endif 1517 1518 #if defined(FEAT_INS_EXPAND) || defined(PROTO) 1519 /* 1520 * Scroll the screen one line down, but don't do it if it would move the 1521 * cursor off the screen. 1522 */ 1523 void 1524 scrolldown_clamp(void) 1525 { 1526 int end_row; 1527 #ifdef FEAT_DIFF 1528 int can_fill = (curwin->w_topfill 1529 < diff_check_fill(curwin, curwin->w_topline)); 1530 #endif 1531 1532 if (curwin->w_topline <= 1 1533 #ifdef FEAT_DIFF 1534 && !can_fill 1535 #endif 1536 ) 1537 return; 1538 1539 validate_cursor(); /* w_wrow needs to be valid */ 1540 1541 /* 1542 * Compute the row number of the last row of the cursor line 1543 * and make sure it doesn't go off the screen. Make sure the cursor 1544 * doesn't go past 'scrolloff' lines from the screen end. 1545 */ 1546 end_row = curwin->w_wrow; 1547 #ifdef FEAT_DIFF 1548 if (can_fill) 1549 ++end_row; 1550 else 1551 end_row += plines_nofill(curwin->w_topline - 1); 1552 #else 1553 end_row += plines(curwin->w_topline - 1); 1554 #endif 1555 if (curwin->w_p_wrap && curwin->w_width != 0) 1556 { 1557 validate_cheight(); 1558 validate_virtcol(); 1559 end_row += curwin->w_cline_height - 1 - 1560 curwin->w_virtcol / curwin->w_width; 1561 } 1562 if (end_row < curwin->w_height - get_scrolloff_value()) 1563 { 1564 #ifdef FEAT_DIFF 1565 if (can_fill) 1566 { 1567 ++curwin->w_topfill; 1568 check_topfill(curwin, TRUE); 1569 } 1570 else 1571 { 1572 --curwin->w_topline; 1573 curwin->w_topfill = 0; 1574 } 1575 #else 1576 --curwin->w_topline; 1577 #endif 1578 #ifdef FEAT_FOLDING 1579 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); 1580 #endif 1581 --curwin->w_botline; /* approximate w_botline */ 1582 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE); 1583 } 1584 } 1585 1586 /* 1587 * Scroll the screen one line up, but don't do it if it would move the cursor 1588 * off the screen. 1589 */ 1590 void 1591 scrollup_clamp(void) 1592 { 1593 int start_row; 1594 1595 if (curwin->w_topline == curbuf->b_ml.ml_line_count 1596 #ifdef FEAT_DIFF 1597 && curwin->w_topfill == 0 1598 #endif 1599 ) 1600 return; 1601 1602 validate_cursor(); /* w_wrow needs to be valid */ 1603 1604 /* 1605 * Compute the row number of the first row of the cursor line 1606 * and make sure it doesn't go off the screen. Make sure the cursor 1607 * doesn't go before 'scrolloff' lines from the screen start. 1608 */ 1609 #ifdef FEAT_DIFF 1610 start_row = curwin->w_wrow - plines_nofill(curwin->w_topline) 1611 - curwin->w_topfill; 1612 #else 1613 start_row = curwin->w_wrow - plines(curwin->w_topline); 1614 #endif 1615 if (curwin->w_p_wrap && curwin->w_width != 0) 1616 { 1617 validate_virtcol(); 1618 start_row -= curwin->w_virtcol / curwin->w_width; 1619 } 1620 if (start_row >= get_scrolloff_value()) 1621 { 1622 #ifdef FEAT_DIFF 1623 if (curwin->w_topfill > 0) 1624 --curwin->w_topfill; 1625 else 1626 #endif 1627 { 1628 #ifdef FEAT_FOLDING 1629 (void)hasFolding(curwin->w_topline, NULL, &curwin->w_topline); 1630 #endif 1631 ++curwin->w_topline; 1632 } 1633 ++curwin->w_botline; /* approximate w_botline */ 1634 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE); 1635 } 1636 } 1637 #endif /* FEAT_INS_EXPAND */ 1638 1639 /* 1640 * Add one line above "lp->lnum". This can be a filler line, a closed fold or 1641 * a (wrapped) text line. Uses and sets "lp->fill". 1642 * Returns the height of the added line in "lp->height". 1643 * Lines above the first one are incredibly high: MAXCOL. 1644 */ 1645 static void 1646 topline_back(lineoff_T *lp) 1647 { 1648 #ifdef FEAT_DIFF 1649 if (lp->fill < diff_check_fill(curwin, lp->lnum)) 1650 { 1651 /* Add a filler line. */ 1652 ++lp->fill; 1653 lp->height = 1; 1654 } 1655 else 1656 #endif 1657 { 1658 --lp->lnum; 1659 #ifdef FEAT_DIFF 1660 lp->fill = 0; 1661 #endif 1662 if (lp->lnum < 1) 1663 lp->height = MAXCOL; 1664 else 1665 #ifdef FEAT_FOLDING 1666 if (hasFolding(lp->lnum, &lp->lnum, NULL)) 1667 /* Add a closed fold */ 1668 lp->height = 1; 1669 else 1670 #endif 1671 lp->height = PLINES_NOFILL(lp->lnum); 1672 } 1673 } 1674 1675 /* 1676 * Add one line below "lp->lnum". This can be a filler line, a closed fold or 1677 * a (wrapped) text line. Uses and sets "lp->fill". 1678 * Returns the height of the added line in "lp->height". 1679 * Lines below the last one are incredibly high. 1680 */ 1681 static void 1682 botline_forw(lineoff_T *lp) 1683 { 1684 #ifdef FEAT_DIFF 1685 if (lp->fill < diff_check_fill(curwin, lp->lnum + 1)) 1686 { 1687 /* Add a filler line. */ 1688 ++lp->fill; 1689 lp->height = 1; 1690 } 1691 else 1692 #endif 1693 { 1694 ++lp->lnum; 1695 #ifdef FEAT_DIFF 1696 lp->fill = 0; 1697 #endif 1698 if (lp->lnum > curbuf->b_ml.ml_line_count) 1699 lp->height = MAXCOL; 1700 else 1701 #ifdef FEAT_FOLDING 1702 if (hasFolding(lp->lnum, NULL, &lp->lnum)) 1703 /* Add a closed fold */ 1704 lp->height = 1; 1705 else 1706 #endif 1707 lp->height = PLINES_NOFILL(lp->lnum); 1708 } 1709 } 1710 1711 #ifdef FEAT_DIFF 1712 /* 1713 * Switch from including filler lines below lp->lnum to including filler 1714 * lines above loff.lnum + 1. This keeps pointing to the same line. 1715 * When there are no filler lines nothing changes. 1716 */ 1717 static void 1718 botline_topline(lineoff_T *lp) 1719 { 1720 if (lp->fill > 0) 1721 { 1722 ++lp->lnum; 1723 lp->fill = diff_check_fill(curwin, lp->lnum) - lp->fill + 1; 1724 } 1725 } 1726 1727 /* 1728 * Switch from including filler lines above lp->lnum to including filler 1729 * lines below loff.lnum - 1. This keeps pointing to the same line. 1730 * When there are no filler lines nothing changes. 1731 */ 1732 static void 1733 topline_botline(lineoff_T *lp) 1734 { 1735 if (lp->fill > 0) 1736 { 1737 lp->fill = diff_check_fill(curwin, lp->lnum) - lp->fill + 1; 1738 --lp->lnum; 1739 } 1740 } 1741 #endif 1742 1743 /* 1744 * Recompute topline to put the cursor at the top of the window. 1745 * Scroll at least "min_scroll" lines. 1746 * If "always" is TRUE, always set topline (for "zt"). 1747 */ 1748 void 1749 scroll_cursor_top(int min_scroll, int always) 1750 { 1751 int scrolled = 0; 1752 int extra = 0; 1753 int used; 1754 int i; 1755 linenr_T top; /* just above displayed lines */ 1756 linenr_T bot; /* just below displayed lines */ 1757 linenr_T old_topline = curwin->w_topline; 1758 #ifdef FEAT_DIFF 1759 linenr_T old_topfill = curwin->w_topfill; 1760 #endif 1761 linenr_T new_topline; 1762 int off = get_scrolloff_value(); 1763 1764 #ifdef FEAT_MOUSE 1765 if (mouse_dragging > 0) 1766 off = mouse_dragging - 1; 1767 #endif 1768 1769 /* 1770 * Decrease topline until: 1771 * - it has become 1 1772 * - (part of) the cursor line is moved off the screen or 1773 * - moved at least 'scrolljump' lines and 1774 * - at least 'scrolloff' lines above and below the cursor 1775 */ 1776 validate_cheight(); 1777 used = curwin->w_cline_height; /* includes filler lines above */ 1778 if (curwin->w_cursor.lnum < curwin->w_topline) 1779 scrolled = used; 1780 1781 #ifdef FEAT_FOLDING 1782 if (hasFolding(curwin->w_cursor.lnum, &top, &bot)) 1783 { 1784 --top; 1785 ++bot; 1786 } 1787 else 1788 #endif 1789 { 1790 top = curwin->w_cursor.lnum - 1; 1791 bot = curwin->w_cursor.lnum + 1; 1792 } 1793 new_topline = top + 1; 1794 1795 #ifdef FEAT_DIFF 1796 /* "used" already contains the number of filler lines above, don't add it 1797 * again. 1798 * Hide filler lines above cursor line by adding them to "extra". */ 1799 extra += diff_check_fill(curwin, curwin->w_cursor.lnum); 1800 #endif 1801 1802 /* 1803 * Check if the lines from "top" to "bot" fit in the window. If they do, 1804 * set new_topline and advance "top" and "bot" to include more lines. 1805 */ 1806 while (top > 0) 1807 { 1808 #ifdef FEAT_FOLDING 1809 if (hasFolding(top, &top, NULL)) 1810 /* count one logical line for a sequence of folded lines */ 1811 i = 1; 1812 else 1813 #endif 1814 i = PLINES_NOFILL(top); 1815 used += i; 1816 if (extra + i <= off && bot < curbuf->b_ml.ml_line_count) 1817 { 1818 #ifdef FEAT_FOLDING 1819 if (hasFolding(bot, NULL, &bot)) 1820 /* count one logical line for a sequence of folded lines */ 1821 ++used; 1822 else 1823 #endif 1824 used += plines(bot); 1825 } 1826 if (used > curwin->w_height) 1827 break; 1828 if (top < curwin->w_topline) 1829 scrolled += i; 1830 1831 /* 1832 * If scrolling is needed, scroll at least 'sj' lines. 1833 */ 1834 if ((new_topline >= curwin->w_topline || scrolled > min_scroll) 1835 && extra >= off) 1836 break; 1837 1838 extra += i; 1839 new_topline = top; 1840 --top; 1841 ++bot; 1842 } 1843 1844 /* 1845 * If we don't have enough space, put cursor in the middle. 1846 * This makes sure we get the same position when using "k" and "j" 1847 * in a small window. 1848 */ 1849 if (used > curwin->w_height) 1850 scroll_cursor_halfway(FALSE); 1851 else 1852 { 1853 /* 1854 * If "always" is FALSE, only adjust topline to a lower value, higher 1855 * value may happen with wrapping lines 1856 */ 1857 if (new_topline < curwin->w_topline || always) 1858 curwin->w_topline = new_topline; 1859 if (curwin->w_topline > curwin->w_cursor.lnum) 1860 curwin->w_topline = curwin->w_cursor.lnum; 1861 #ifdef FEAT_DIFF 1862 curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline); 1863 if (curwin->w_topfill > 0 && extra > off) 1864 { 1865 curwin->w_topfill -= extra - off; 1866 if (curwin->w_topfill < 0) 1867 curwin->w_topfill = 0; 1868 } 1869 check_topfill(curwin, FALSE); 1870 #endif 1871 if (curwin->w_topline != old_topline 1872 #ifdef FEAT_DIFF 1873 || curwin->w_topfill != old_topfill 1874 #endif 1875 ) 1876 curwin->w_valid &= 1877 ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP); 1878 curwin->w_valid |= VALID_TOPLINE; 1879 } 1880 } 1881 1882 /* 1883 * Set w_empty_rows and w_filler_rows for window "wp", having used up "used" 1884 * screen lines for text lines. 1885 */ 1886 void 1887 set_empty_rows(win_T *wp, int used) 1888 { 1889 #ifdef FEAT_DIFF 1890 wp->w_filler_rows = 0; 1891 #endif 1892 if (used == 0) 1893 wp->w_empty_rows = 0; /* single line that doesn't fit */ 1894 else 1895 { 1896 wp->w_empty_rows = wp->w_height - used; 1897 #ifdef FEAT_DIFF 1898 if (wp->w_botline <= wp->w_buffer->b_ml.ml_line_count) 1899 { 1900 wp->w_filler_rows = diff_check_fill(wp, wp->w_botline); 1901 if (wp->w_empty_rows > wp->w_filler_rows) 1902 wp->w_empty_rows -= wp->w_filler_rows; 1903 else 1904 { 1905 wp->w_filler_rows = wp->w_empty_rows; 1906 wp->w_empty_rows = 0; 1907 } 1908 } 1909 #endif 1910 } 1911 } 1912 1913 /* 1914 * Recompute topline to put the cursor at the bottom of the window. 1915 * Scroll at least "min_scroll" lines. 1916 * If "set_topbot" is TRUE, set topline and botline first (for "zb"). 1917 * This is messy stuff!!! 1918 */ 1919 void 1920 scroll_cursor_bot(int min_scroll, int set_topbot) 1921 { 1922 int used; 1923 int scrolled = 0; 1924 int extra = 0; 1925 int i; 1926 linenr_T line_count; 1927 linenr_T old_topline = curwin->w_topline; 1928 lineoff_T loff; 1929 lineoff_T boff; 1930 #ifdef FEAT_DIFF 1931 int old_topfill = curwin->w_topfill; 1932 int fill_below_window; 1933 #endif 1934 linenr_T old_botline = curwin->w_botline; 1935 linenr_T old_valid = curwin->w_valid; 1936 int old_empty_rows = curwin->w_empty_rows; 1937 linenr_T cln; /* Cursor Line Number */ 1938 long so = get_scrolloff_value(); 1939 1940 cln = curwin->w_cursor.lnum; 1941 if (set_topbot) 1942 { 1943 used = 0; 1944 curwin->w_botline = cln + 1; 1945 #ifdef FEAT_DIFF 1946 loff.fill = 0; 1947 #endif 1948 for (curwin->w_topline = curwin->w_botline; 1949 curwin->w_topline > 1; 1950 curwin->w_topline = loff.lnum) 1951 { 1952 loff.lnum = curwin->w_topline; 1953 topline_back(&loff); 1954 if (loff.height == MAXCOL || used + loff.height > curwin->w_height) 1955 break; 1956 used += loff.height; 1957 #ifdef FEAT_DIFF 1958 curwin->w_topfill = loff.fill; 1959 #endif 1960 } 1961 set_empty_rows(curwin, used); 1962 curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP; 1963 if (curwin->w_topline != old_topline 1964 #ifdef FEAT_DIFF 1965 || curwin->w_topfill != old_topfill 1966 #endif 1967 ) 1968 curwin->w_valid &= ~(VALID_WROW|VALID_CROW); 1969 } 1970 else 1971 validate_botline(); 1972 1973 /* The lines of the cursor line itself are always used. */ 1974 #ifdef FEAT_DIFF 1975 used = plines_nofill(cln); 1976 #else 1977 validate_cheight(); 1978 used = curwin->w_cline_height; 1979 #endif 1980 1981 /* If the cursor is below botline, we will at least scroll by the height 1982 * of the cursor line. Correct for empty lines, which are really part of 1983 * botline. */ 1984 if (cln >= curwin->w_botline) 1985 { 1986 scrolled = used; 1987 if (cln == curwin->w_botline) 1988 scrolled -= curwin->w_empty_rows; 1989 } 1990 1991 /* 1992 * Stop counting lines to scroll when 1993 * - hitting start of the file 1994 * - scrolled nothing or at least 'sj' lines 1995 * - at least 'scrolloff' lines below the cursor 1996 * - lines between botline and cursor have been counted 1997 */ 1998 #ifdef FEAT_FOLDING 1999 if (!hasFolding(curwin->w_cursor.lnum, &loff.lnum, &boff.lnum)) 2000 #endif 2001 { 2002 loff.lnum = cln; 2003 boff.lnum = cln; 2004 } 2005 #ifdef FEAT_DIFF 2006 loff.fill = 0; 2007 boff.fill = 0; 2008 fill_below_window = diff_check_fill(curwin, curwin->w_botline) 2009 - curwin->w_filler_rows; 2010 #endif 2011 2012 while (loff.lnum > 1) 2013 { 2014 /* Stop when scrolled nothing or at least "min_scroll", found "extra" 2015 * context for 'scrolloff' and counted all lines below the window. */ 2016 if ((((scrolled <= 0 || scrolled >= min_scroll) 2017 && extra >= ( 2018 #ifdef FEAT_MOUSE 2019 mouse_dragging > 0 ? mouse_dragging - 1 : 2020 #endif 2021 so)) 2022 || boff.lnum + 1 > curbuf->b_ml.ml_line_count) 2023 && loff.lnum <= curwin->w_botline 2024 #ifdef FEAT_DIFF 2025 && (loff.lnum < curwin->w_botline 2026 || loff.fill >= fill_below_window) 2027 #endif 2028 ) 2029 break; 2030 2031 /* Add one line above */ 2032 topline_back(&loff); 2033 if (loff.height == MAXCOL) 2034 used = MAXCOL; 2035 else 2036 used += loff.height; 2037 if (used > curwin->w_height) 2038 break; 2039 if (loff.lnum >= curwin->w_botline 2040 #ifdef FEAT_DIFF 2041 && (loff.lnum > curwin->w_botline 2042 || loff.fill <= fill_below_window) 2043 #endif 2044 ) 2045 { 2046 /* Count screen lines that are below the window. */ 2047 scrolled += loff.height; 2048 if (loff.lnum == curwin->w_botline 2049 #ifdef FEAT_DIFF 2050 && loff.fill == 0 2051 #endif 2052 ) 2053 scrolled -= curwin->w_empty_rows; 2054 } 2055 2056 if (boff.lnum < curbuf->b_ml.ml_line_count) 2057 { 2058 /* Add one line below */ 2059 botline_forw(&boff); 2060 used += boff.height; 2061 if (used > curwin->w_height) 2062 break; 2063 if (extra < ( 2064 #ifdef FEAT_MOUSE 2065 mouse_dragging > 0 ? mouse_dragging - 1 : 2066 #endif 2067 so) || scrolled < min_scroll) 2068 { 2069 extra += boff.height; 2070 if (boff.lnum >= curwin->w_botline 2071 #ifdef FEAT_DIFF 2072 || (boff.lnum + 1 == curwin->w_botline 2073 && boff.fill > curwin->w_filler_rows) 2074 #endif 2075 ) 2076 { 2077 /* Count screen lines that are below the window. */ 2078 scrolled += boff.height; 2079 if (boff.lnum == curwin->w_botline 2080 #ifdef FEAT_DIFF 2081 && boff.fill == 0 2082 #endif 2083 ) 2084 scrolled -= curwin->w_empty_rows; 2085 } 2086 } 2087 } 2088 } 2089 2090 /* curwin->w_empty_rows is larger, no need to scroll */ 2091 if (scrolled <= 0) 2092 line_count = 0; 2093 /* more than a screenfull, don't scroll but redraw */ 2094 else if (used > curwin->w_height) 2095 line_count = used; 2096 /* scroll minimal number of lines */ 2097 else 2098 { 2099 line_count = 0; 2100 #ifdef FEAT_DIFF 2101 boff.fill = curwin->w_topfill; 2102 #endif 2103 boff.lnum = curwin->w_topline - 1; 2104 for (i = 0; i < scrolled && boff.lnum < curwin->w_botline; ) 2105 { 2106 botline_forw(&boff); 2107 i += boff.height; 2108 ++line_count; 2109 } 2110 if (i < scrolled) /* below curwin->w_botline, don't scroll */ 2111 line_count = 9999; 2112 } 2113 2114 /* 2115 * Scroll up if the cursor is off the bottom of the screen a bit. 2116 * Otherwise put it at 1/2 of the screen. 2117 */ 2118 if (line_count >= curwin->w_height && line_count > min_scroll) 2119 scroll_cursor_halfway(FALSE); 2120 else 2121 scrollup(line_count, TRUE); 2122 2123 /* 2124 * If topline didn't change we need to restore w_botline and w_empty_rows 2125 * (we changed them). 2126 * If topline did change, update_screen() will set botline. 2127 */ 2128 if (curwin->w_topline == old_topline && set_topbot) 2129 { 2130 curwin->w_botline = old_botline; 2131 curwin->w_empty_rows = old_empty_rows; 2132 curwin->w_valid = old_valid; 2133 } 2134 curwin->w_valid |= VALID_TOPLINE; 2135 } 2136 2137 /* 2138 * Recompute topline to put the cursor halfway the window 2139 * If "atend" is TRUE, also put it halfway at the end of the file. 2140 */ 2141 void 2142 scroll_cursor_halfway(int atend) 2143 { 2144 int above = 0; 2145 linenr_T topline; 2146 #ifdef FEAT_DIFF 2147 int topfill = 0; 2148 #endif 2149 int below = 0; 2150 int used; 2151 lineoff_T loff; 2152 lineoff_T boff; 2153 #ifdef FEAT_DIFF 2154 linenr_T old_topline = curwin->w_topline; 2155 #endif 2156 2157 loff.lnum = boff.lnum = curwin->w_cursor.lnum; 2158 #ifdef FEAT_FOLDING 2159 (void)hasFolding(loff.lnum, &loff.lnum, &boff.lnum); 2160 #endif 2161 #ifdef FEAT_DIFF 2162 used = plines_nofill(loff.lnum); 2163 loff.fill = 0; 2164 boff.fill = 0; 2165 #else 2166 used = plines(loff.lnum); 2167 #endif 2168 topline = loff.lnum; 2169 while (topline > 1) 2170 { 2171 if (below <= above) /* add a line below the cursor first */ 2172 { 2173 if (boff.lnum < curbuf->b_ml.ml_line_count) 2174 { 2175 botline_forw(&boff); 2176 used += boff.height; 2177 if (used > curwin->w_height) 2178 break; 2179 below += boff.height; 2180 } 2181 else 2182 { 2183 ++below; /* count a "~" line */ 2184 if (atend) 2185 ++used; 2186 } 2187 } 2188 2189 if (below > above) /* add a line above the cursor */ 2190 { 2191 topline_back(&loff); 2192 if (loff.height == MAXCOL) 2193 used = MAXCOL; 2194 else 2195 used += loff.height; 2196 if (used > curwin->w_height) 2197 break; 2198 above += loff.height; 2199 topline = loff.lnum; 2200 #ifdef FEAT_DIFF 2201 topfill = loff.fill; 2202 #endif 2203 } 2204 } 2205 #ifdef FEAT_FOLDING 2206 if (!hasFolding(topline, &curwin->w_topline, NULL)) 2207 #endif 2208 curwin->w_topline = topline; 2209 #ifdef FEAT_DIFF 2210 curwin->w_topfill = topfill; 2211 if (old_topline > curwin->w_topline + curwin->w_height) 2212 curwin->w_botfill = FALSE; 2213 check_topfill(curwin, FALSE); 2214 #endif 2215 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP); 2216 curwin->w_valid |= VALID_TOPLINE; 2217 } 2218 2219 /* 2220 * Correct the cursor position so that it is in a part of the screen at least 2221 * 'scrolloff' lines from the top and bottom, if possible. 2222 * If not possible, put it at the same position as scroll_cursor_halfway(). 2223 * When called topline must be valid! 2224 */ 2225 void 2226 cursor_correct(void) 2227 { 2228 int above = 0; /* screen lines above topline */ 2229 linenr_T topline; 2230 int below = 0; /* screen lines below botline */ 2231 linenr_T botline; 2232 int above_wanted, below_wanted; 2233 linenr_T cln; /* Cursor Line Number */ 2234 int max_off; 2235 long so = get_scrolloff_value(); 2236 2237 /* 2238 * How many lines we would like to have above/below the cursor depends on 2239 * whether the first/last line of the file is on screen. 2240 */ 2241 above_wanted = so; 2242 below_wanted = so; 2243 #ifdef FEAT_MOUSE 2244 if (mouse_dragging > 0) 2245 { 2246 above_wanted = mouse_dragging - 1; 2247 below_wanted = mouse_dragging - 1; 2248 } 2249 #endif 2250 if (curwin->w_topline == 1) 2251 { 2252 above_wanted = 0; 2253 max_off = curwin->w_height / 2; 2254 if (below_wanted > max_off) 2255 below_wanted = max_off; 2256 } 2257 validate_botline(); 2258 if (curwin->w_botline == curbuf->b_ml.ml_line_count + 1 2259 #ifdef FEAT_MOUSE 2260 && mouse_dragging == 0 2261 #endif 2262 ) 2263 { 2264 below_wanted = 0; 2265 max_off = (curwin->w_height - 1) / 2; 2266 if (above_wanted > max_off) 2267 above_wanted = max_off; 2268 } 2269 2270 /* 2271 * If there are sufficient file-lines above and below the cursor, we can 2272 * return now. 2273 */ 2274 cln = curwin->w_cursor.lnum; 2275 if (cln >= curwin->w_topline + above_wanted 2276 && cln < curwin->w_botline - below_wanted 2277 #ifdef FEAT_FOLDING 2278 && !hasAnyFolding(curwin) 2279 #endif 2280 ) 2281 return; 2282 2283 /* 2284 * Narrow down the area where the cursor can be put by taking lines from 2285 * the top and the bottom until: 2286 * - the desired context lines are found 2287 * - the lines from the top is past the lines from the bottom 2288 */ 2289 topline = curwin->w_topline; 2290 botline = curwin->w_botline - 1; 2291 #ifdef FEAT_DIFF 2292 /* count filler lines as context */ 2293 above = curwin->w_topfill; 2294 below = curwin->w_filler_rows; 2295 #endif 2296 while ((above < above_wanted || below < below_wanted) && topline < botline) 2297 { 2298 if (below < below_wanted && (below <= above || above >= above_wanted)) 2299 { 2300 #ifdef FEAT_FOLDING 2301 if (hasFolding(botline, &botline, NULL)) 2302 ++below; 2303 else 2304 #endif 2305 below += plines(botline); 2306 --botline; 2307 } 2308 if (above < above_wanted && (above < below || below >= below_wanted)) 2309 { 2310 #ifdef FEAT_FOLDING 2311 if (hasFolding(topline, NULL, &topline)) 2312 ++above; 2313 else 2314 #endif 2315 above += PLINES_NOFILL(topline); 2316 #ifdef FEAT_DIFF 2317 /* Count filler lines below this line as context. */ 2318 if (topline < botline) 2319 above += diff_check_fill(curwin, topline + 1); 2320 #endif 2321 ++topline; 2322 } 2323 } 2324 if (topline == botline || botline == 0) 2325 curwin->w_cursor.lnum = topline; 2326 else if (topline > botline) 2327 curwin->w_cursor.lnum = botline; 2328 else 2329 { 2330 if (cln < topline && curwin->w_topline > 1) 2331 { 2332 curwin->w_cursor.lnum = topline; 2333 curwin->w_valid &= 2334 ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW); 2335 } 2336 if (cln > botline && curwin->w_botline <= curbuf->b_ml.ml_line_count) 2337 { 2338 curwin->w_cursor.lnum = botline; 2339 curwin->w_valid &= 2340 ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW); 2341 } 2342 } 2343 curwin->w_valid |= VALID_TOPLINE; 2344 } 2345 2346 static void get_scroll_overlap(lineoff_T *lp, int dir); 2347 2348 /* 2349 * move screen 'count' pages up or down and update screen 2350 * 2351 * return FAIL for failure, OK otherwise 2352 */ 2353 int 2354 onepage(int dir, long count) 2355 { 2356 long n; 2357 int retval = OK; 2358 lineoff_T loff; 2359 linenr_T old_topline = curwin->w_topline; 2360 long so = get_scrolloff_value(); 2361 2362 if (curbuf->b_ml.ml_line_count == 1) /* nothing to do */ 2363 { 2364 beep_flush(); 2365 return FAIL; 2366 } 2367 2368 for ( ; count > 0; --count) 2369 { 2370 validate_botline(); 2371 /* 2372 * It's an error to move a page up when the first line is already on 2373 * the screen. It's an error to move a page down when the last line 2374 * is on the screen and the topline is 'scrolloff' lines from the 2375 * last line. 2376 */ 2377 if (dir == FORWARD 2378 ? ((curwin->w_topline >= curbuf->b_ml.ml_line_count - so) 2379 && curwin->w_botline > curbuf->b_ml.ml_line_count) 2380 : (curwin->w_topline == 1 2381 #ifdef FEAT_DIFF 2382 && curwin->w_topfill == 2383 diff_check_fill(curwin, curwin->w_topline) 2384 #endif 2385 )) 2386 { 2387 beep_flush(); 2388 retval = FAIL; 2389 break; 2390 } 2391 2392 #ifdef FEAT_DIFF 2393 loff.fill = 0; 2394 #endif 2395 if (dir == FORWARD) 2396 { 2397 if (ONE_WINDOW && p_window > 0 && p_window < Rows - 1) 2398 { 2399 /* Vi compatible scrolling */ 2400 if (p_window <= 2) 2401 ++curwin->w_topline; 2402 else 2403 curwin->w_topline += p_window - 2; 2404 if (curwin->w_topline > curbuf->b_ml.ml_line_count) 2405 curwin->w_topline = curbuf->b_ml.ml_line_count; 2406 curwin->w_cursor.lnum = curwin->w_topline; 2407 } 2408 else if (curwin->w_botline > curbuf->b_ml.ml_line_count) 2409 { 2410 /* at end of file */ 2411 curwin->w_topline = curbuf->b_ml.ml_line_count; 2412 #ifdef FEAT_DIFF 2413 curwin->w_topfill = 0; 2414 #endif 2415 curwin->w_valid &= ~(VALID_WROW|VALID_CROW); 2416 } 2417 else 2418 { 2419 /* For the overlap, start with the line just below the window 2420 * and go upwards. */ 2421 loff.lnum = curwin->w_botline; 2422 #ifdef FEAT_DIFF 2423 loff.fill = diff_check_fill(curwin, loff.lnum) 2424 - curwin->w_filler_rows; 2425 #endif 2426 get_scroll_overlap(&loff, -1); 2427 curwin->w_topline = loff.lnum; 2428 #ifdef FEAT_DIFF 2429 curwin->w_topfill = loff.fill; 2430 check_topfill(curwin, FALSE); 2431 #endif 2432 curwin->w_cursor.lnum = curwin->w_topline; 2433 curwin->w_valid &= ~(VALID_WCOL|VALID_CHEIGHT|VALID_WROW| 2434 VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP); 2435 } 2436 } 2437 else /* dir == BACKWARDS */ 2438 { 2439 #ifdef FEAT_DIFF 2440 if (curwin->w_topline == 1) 2441 { 2442 /* Include max number of filler lines */ 2443 max_topfill(); 2444 continue; 2445 } 2446 #endif 2447 if (ONE_WINDOW && p_window > 0 && p_window < Rows - 1) 2448 { 2449 /* Vi compatible scrolling (sort of) */ 2450 if (p_window <= 2) 2451 --curwin->w_topline; 2452 else 2453 curwin->w_topline -= p_window - 2; 2454 if (curwin->w_topline < 1) 2455 curwin->w_topline = 1; 2456 curwin->w_cursor.lnum = curwin->w_topline + p_window - 1; 2457 if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) 2458 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; 2459 continue; 2460 } 2461 2462 /* Find the line at the top of the window that is going to be the 2463 * line at the bottom of the window. Make sure this results in 2464 * the same line as before doing CTRL-F. */ 2465 loff.lnum = curwin->w_topline - 1; 2466 #ifdef FEAT_DIFF 2467 loff.fill = diff_check_fill(curwin, loff.lnum + 1) 2468 - curwin->w_topfill; 2469 #endif 2470 get_scroll_overlap(&loff, 1); 2471 2472 if (loff.lnum >= curbuf->b_ml.ml_line_count) 2473 { 2474 loff.lnum = curbuf->b_ml.ml_line_count; 2475 #ifdef FEAT_DIFF 2476 loff.fill = 0; 2477 } 2478 else 2479 { 2480 botline_topline(&loff); 2481 #endif 2482 } 2483 curwin->w_cursor.lnum = loff.lnum; 2484 2485 /* Find the line just above the new topline to get the right line 2486 * at the bottom of the window. */ 2487 n = 0; 2488 while (n <= curwin->w_height && loff.lnum >= 1) 2489 { 2490 topline_back(&loff); 2491 if (loff.height == MAXCOL) 2492 n = MAXCOL; 2493 else 2494 n += loff.height; 2495 } 2496 if (loff.lnum < 1) /* at begin of file */ 2497 { 2498 curwin->w_topline = 1; 2499 #ifdef FEAT_DIFF 2500 max_topfill(); 2501 #endif 2502 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE); 2503 } 2504 else 2505 { 2506 /* Go two lines forward again. */ 2507 #ifdef FEAT_DIFF 2508 topline_botline(&loff); 2509 #endif 2510 botline_forw(&loff); 2511 botline_forw(&loff); 2512 #ifdef FEAT_DIFF 2513 botline_topline(&loff); 2514 #endif 2515 #ifdef FEAT_FOLDING 2516 /* We're at the wrong end of a fold now. */ 2517 (void)hasFolding(loff.lnum, &loff.lnum, NULL); 2518 #endif 2519 2520 /* Always scroll at least one line. Avoid getting stuck on 2521 * very long lines. */ 2522 if (loff.lnum >= curwin->w_topline 2523 #ifdef FEAT_DIFF 2524 && (loff.lnum > curwin->w_topline 2525 || loff.fill >= curwin->w_topfill) 2526 #endif 2527 ) 2528 { 2529 #ifdef FEAT_DIFF 2530 /* First try using the maximum number of filler lines. If 2531 * that's not enough, backup one line. */ 2532 loff.fill = curwin->w_topfill; 2533 if (curwin->w_topfill < diff_check_fill(curwin, 2534 curwin->w_topline)) 2535 max_topfill(); 2536 if (curwin->w_topfill == loff.fill) 2537 #endif 2538 { 2539 --curwin->w_topline; 2540 #ifdef FEAT_DIFF 2541 curwin->w_topfill = 0; 2542 #endif 2543 } 2544 comp_botline(curwin); 2545 curwin->w_cursor.lnum = curwin->w_botline - 1; 2546 curwin->w_valid &= 2547 ~(VALID_WCOL|VALID_CHEIGHT|VALID_WROW|VALID_CROW); 2548 } 2549 else 2550 { 2551 curwin->w_topline = loff.lnum; 2552 #ifdef FEAT_DIFF 2553 curwin->w_topfill = loff.fill; 2554 check_topfill(curwin, FALSE); 2555 #endif 2556 curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE); 2557 } 2558 } 2559 } 2560 } 2561 #ifdef FEAT_FOLDING 2562 foldAdjustCursor(); 2563 #endif 2564 cursor_correct(); 2565 check_cursor_col(); 2566 if (retval == OK) 2567 beginline(BL_SOL | BL_FIX); 2568 curwin->w_valid &= ~(VALID_WCOL|VALID_WROW|VALID_VIRTCOL); 2569 2570 if (retval == OK && dir == FORWARD) 2571 { 2572 // Avoid the screen jumping up and down when 'scrolloff' is non-zero. 2573 // But make sure we scroll at least one line (happens with mix of long 2574 // wrapping lines and non-wrapping line). 2575 if (check_top_offset()) 2576 { 2577 scroll_cursor_top(1, FALSE); 2578 if (curwin->w_topline <= old_topline 2579 && old_topline < curbuf->b_ml.ml_line_count) 2580 { 2581 curwin->w_topline = old_topline + 1; 2582 #ifdef FEAT_FOLDING 2583 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); 2584 #endif 2585 } 2586 } 2587 #ifdef FEAT_FOLDING 2588 else if (curwin->w_botline > curbuf->b_ml.ml_line_count) 2589 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); 2590 #endif 2591 } 2592 2593 redraw_later(VALID); 2594 return retval; 2595 } 2596 2597 /* 2598 * Decide how much overlap to use for page-up or page-down scrolling. 2599 * This is symmetric, so that doing both keeps the same lines displayed. 2600 * Three lines are examined: 2601 * 2602 * before CTRL-F after CTRL-F / before CTRL-B 2603 * etc. l1 2604 * l1 last but one line ------------ 2605 * l2 last text line l2 top text line 2606 * ------------- l3 second text line 2607 * l3 etc. 2608 */ 2609 static void 2610 get_scroll_overlap(lineoff_T *lp, int dir) 2611 { 2612 int h1, h2, h3, h4; 2613 int min_height = curwin->w_height - 2; 2614 lineoff_T loff0, loff1, loff2; 2615 2616 #ifdef FEAT_DIFF 2617 if (lp->fill > 0) 2618 lp->height = 1; 2619 else 2620 lp->height = plines_nofill(lp->lnum); 2621 #else 2622 lp->height = plines(lp->lnum); 2623 #endif 2624 h1 = lp->height; 2625 if (h1 > min_height) 2626 return; /* no overlap */ 2627 2628 loff0 = *lp; 2629 if (dir > 0) 2630 botline_forw(lp); 2631 else 2632 topline_back(lp); 2633 h2 = lp->height; 2634 if (h2 == MAXCOL || h2 + h1 > min_height) 2635 { 2636 *lp = loff0; /* no overlap */ 2637 return; 2638 } 2639 2640 loff1 = *lp; 2641 if (dir > 0) 2642 botline_forw(lp); 2643 else 2644 topline_back(lp); 2645 h3 = lp->height; 2646 if (h3 == MAXCOL || h3 + h2 > min_height) 2647 { 2648 *lp = loff0; /* no overlap */ 2649 return; 2650 } 2651 2652 loff2 = *lp; 2653 if (dir > 0) 2654 botline_forw(lp); 2655 else 2656 topline_back(lp); 2657 h4 = lp->height; 2658 if (h4 == MAXCOL || h4 + h3 + h2 > min_height || h3 + h2 + h1 > min_height) 2659 *lp = loff1; /* 1 line overlap */ 2660 else 2661 *lp = loff2; /* 2 lines overlap */ 2662 return; 2663 } 2664 2665 /* 2666 * Scroll 'scroll' lines up or down. 2667 */ 2668 void 2669 halfpage(int flag, linenr_T Prenum) 2670 { 2671 long scrolled = 0; 2672 int i; 2673 int n; 2674 int room; 2675 2676 if (Prenum) 2677 curwin->w_p_scr = (Prenum > curwin->w_height) ? 2678 curwin->w_height : Prenum; 2679 n = (curwin->w_p_scr <= curwin->w_height) ? 2680 curwin->w_p_scr : curwin->w_height; 2681 2682 update_topline(); 2683 validate_botline(); 2684 room = curwin->w_empty_rows; 2685 #ifdef FEAT_DIFF 2686 room += curwin->w_filler_rows; 2687 #endif 2688 if (flag) 2689 { 2690 /* 2691 * scroll the text up 2692 */ 2693 while (n > 0 && curwin->w_botline <= curbuf->b_ml.ml_line_count) 2694 { 2695 #ifdef FEAT_DIFF 2696 if (curwin->w_topfill > 0) 2697 { 2698 i = 1; 2699 if (--n < 0 && scrolled > 0) 2700 break; 2701 --curwin->w_topfill; 2702 } 2703 else 2704 #endif 2705 { 2706 i = PLINES_NOFILL(curwin->w_topline); 2707 n -= i; 2708 if (n < 0 && scrolled > 0) 2709 break; 2710 #ifdef FEAT_FOLDING 2711 (void)hasFolding(curwin->w_topline, NULL, &curwin->w_topline); 2712 #endif 2713 ++curwin->w_topline; 2714 #ifdef FEAT_DIFF 2715 curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline); 2716 #endif 2717 2718 if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) 2719 { 2720 ++curwin->w_cursor.lnum; 2721 curwin->w_valid &= 2722 ~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL); 2723 } 2724 } 2725 curwin->w_valid &= ~(VALID_CROW|VALID_WROW); 2726 scrolled += i; 2727 2728 /* 2729 * Correct w_botline for changed w_topline. 2730 * Won't work when there are filler lines. 2731 */ 2732 #ifdef FEAT_DIFF 2733 if (curwin->w_p_diff) 2734 curwin->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP); 2735 else 2736 #endif 2737 { 2738 room += i; 2739 do 2740 { 2741 i = plines(curwin->w_botline); 2742 if (i > room) 2743 break; 2744 #ifdef FEAT_FOLDING 2745 (void)hasFolding(curwin->w_botline, NULL, 2746 &curwin->w_botline); 2747 #endif 2748 ++curwin->w_botline; 2749 room -= i; 2750 } while (curwin->w_botline <= curbuf->b_ml.ml_line_count); 2751 } 2752 } 2753 2754 /* 2755 * When hit bottom of the file: move cursor down. 2756 */ 2757 if (n > 0) 2758 { 2759 # ifdef FEAT_FOLDING 2760 if (hasAnyFolding(curwin)) 2761 { 2762 while (--n >= 0 2763 && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) 2764 { 2765 (void)hasFolding(curwin->w_cursor.lnum, NULL, 2766 &curwin->w_cursor.lnum); 2767 ++curwin->w_cursor.lnum; 2768 } 2769 } 2770 else 2771 # endif 2772 curwin->w_cursor.lnum += n; 2773 check_cursor_lnum(); 2774 } 2775 } 2776 else 2777 { 2778 /* 2779 * scroll the text down 2780 */ 2781 while (n > 0 && curwin->w_topline > 1) 2782 { 2783 #ifdef FEAT_DIFF 2784 if (curwin->w_topfill < diff_check_fill(curwin, curwin->w_topline)) 2785 { 2786 i = 1; 2787 --n; 2788 ++curwin->w_topfill; 2789 } 2790 else 2791 #endif 2792 { 2793 i = PLINES_NOFILL(curwin->w_topline - 1); 2794 n -= i; 2795 if (n < 0 && scrolled > 0) 2796 break; 2797 --curwin->w_topline; 2798 #ifdef FEAT_FOLDING 2799 (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); 2800 #endif 2801 #ifdef FEAT_DIFF 2802 curwin->w_topfill = 0; 2803 #endif 2804 } 2805 curwin->w_valid &= ~(VALID_CROW|VALID_WROW| 2806 VALID_BOTLINE|VALID_BOTLINE_AP); 2807 scrolled += i; 2808 if (curwin->w_cursor.lnum > 1) 2809 { 2810 --curwin->w_cursor.lnum; 2811 curwin->w_valid &= ~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL); 2812 } 2813 } 2814 2815 /* 2816 * When hit top of the file: move cursor up. 2817 */ 2818 if (n > 0) 2819 { 2820 if (curwin->w_cursor.lnum <= (linenr_T)n) 2821 curwin->w_cursor.lnum = 1; 2822 else 2823 # ifdef FEAT_FOLDING 2824 if (hasAnyFolding(curwin)) 2825 { 2826 while (--n >= 0 && curwin->w_cursor.lnum > 1) 2827 { 2828 --curwin->w_cursor.lnum; 2829 (void)hasFolding(curwin->w_cursor.lnum, 2830 &curwin->w_cursor.lnum, NULL); 2831 } 2832 } 2833 else 2834 # endif 2835 curwin->w_cursor.lnum -= n; 2836 } 2837 } 2838 # ifdef FEAT_FOLDING 2839 /* Move cursor to first line of closed fold. */ 2840 foldAdjustCursor(); 2841 # endif 2842 #ifdef FEAT_DIFF 2843 check_topfill(curwin, !flag); 2844 #endif 2845 cursor_correct(); 2846 beginline(BL_SOL | BL_FIX); 2847 redraw_later(VALID); 2848 } 2849 2850 void 2851 do_check_cursorbind(void) 2852 { 2853 linenr_T line = curwin->w_cursor.lnum; 2854 colnr_T col = curwin->w_cursor.col; 2855 colnr_T coladd = curwin->w_cursor.coladd; 2856 colnr_T curswant = curwin->w_curswant; 2857 int set_curswant = curwin->w_set_curswant; 2858 win_T *old_curwin = curwin; 2859 buf_T *old_curbuf = curbuf; 2860 int restart_edit_save; 2861 int old_VIsual_select = VIsual_select; 2862 int old_VIsual_active = VIsual_active; 2863 2864 /* 2865 * loop through the cursorbound windows 2866 */ 2867 VIsual_select = VIsual_active = 0; 2868 FOR_ALL_WINDOWS(curwin) 2869 { 2870 curbuf = curwin->w_buffer; 2871 /* skip original window and windows with 'noscrollbind' */ 2872 if (curwin != old_curwin && curwin->w_p_crb) 2873 { 2874 # ifdef FEAT_DIFF 2875 if (curwin->w_p_diff) 2876 curwin->w_cursor.lnum = 2877 diff_get_corresponding_line(old_curbuf, line); 2878 else 2879 # endif 2880 curwin->w_cursor.lnum = line; 2881 curwin->w_cursor.col = col; 2882 curwin->w_cursor.coladd = coladd; 2883 curwin->w_curswant = curswant; 2884 curwin->w_set_curswant = set_curswant; 2885 2886 /* Make sure the cursor is in a valid position. Temporarily set 2887 * "restart_edit" to allow the cursor to be beyond the EOL. */ 2888 restart_edit_save = restart_edit; 2889 restart_edit = TRUE; 2890 check_cursor(); 2891 # ifdef FEAT_SYN_HL 2892 if (curwin->w_p_cul || curwin->w_p_cuc) 2893 validate_cursor(); 2894 # endif 2895 restart_edit = restart_edit_save; 2896 /* Correct cursor for multi-byte character. */ 2897 if (has_mbyte) 2898 mb_adjust_cursor(); 2899 redraw_later(VALID); 2900 2901 /* Only scroll when 'scrollbind' hasn't done this. */ 2902 if (!curwin->w_p_scb) 2903 update_topline(); 2904 curwin->w_redr_status = TRUE; 2905 } 2906 } 2907 2908 /* 2909 * reset current-window 2910 */ 2911 VIsual_select = old_VIsual_select; 2912 VIsual_active = old_VIsual_active; 2913 curwin = old_curwin; 2914 curbuf = old_curbuf; 2915 } 2916