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