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