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 /* 11 * screen.c: code for displaying on the screen 12 * 13 * Output to the screen (console, terminal emulator or GUI window) is minimized 14 * by remembering what is already on the screen, and only updating the parts 15 * that changed. 16 * 17 * ScreenLines[off] Contains a copy of the whole screen, as it is currently 18 * displayed (excluding text written by external commands). 19 * ScreenAttrs[off] Contains the associated attributes. 20 * LineOffset[row] Contains the offset into ScreenLines*[] and ScreenAttrs[] 21 * for each line. 22 * LineWraps[row] Flag for each line whether it wraps to the next line. 23 * 24 * For double-byte characters, two consecutive bytes in ScreenLines[] can form 25 * one character which occupies two display cells. 26 * For UTF-8 a multi-byte character is converted to Unicode and stored in 27 * ScreenLinesUC[]. ScreenLines[] contains the first byte only. For an ASCII 28 * character without composing chars ScreenLinesUC[] will be 0 and 29 * ScreenLinesC[][] is not used. When the character occupies two display 30 * cells the next byte in ScreenLines[] is 0. 31 * ScreenLinesC[][] contain up to 'maxcombine' composing characters 32 * (drawn on top of the first character). There is 0 after the last one used. 33 * ScreenLines2[] is only used for euc-jp to store the second byte if the 34 * first byte is 0x8e (single-width character). 35 * 36 * The screen_*() functions write to the screen and handle updating 37 * ScreenLines[]. 38 * 39 * update_screen() is the function that updates all windows and status lines. 40 * It is called form the main loop when must_redraw is non-zero. It may be 41 * called from other places when an immediate screen update is needed. 42 * 43 * The part of the buffer that is displayed in a window is set with: 44 * - w_topline (first buffer line in window) 45 * - w_topfill (filler lines above the first line) 46 * - w_leftcol (leftmost window cell in window), 47 * - w_skipcol (skipped window cells of first line) 48 * 49 * Commands that only move the cursor around in a window, do not need to take 50 * action to update the display. The main loop will check if w_topline is 51 * valid and update it (scroll the window) when needed. 52 * 53 * Commands that scroll a window change w_topline and must call 54 * check_cursor() to move the cursor into the visible part of the window, and 55 * call redraw_later(VALID) to have the window displayed by update_screen() 56 * later. 57 * 58 * Commands that change text in the buffer must call changed_bytes() or 59 * changed_lines() to mark the area that changed and will require updating 60 * later. The main loop will call update_screen(), which will update each 61 * window that shows the changed buffer. This assumes text above the change 62 * can remain displayed as it is. Text after the change may need updating for 63 * scrolling, folding and syntax highlighting. 64 * 65 * Commands that change how a window is displayed (e.g., setting 'list') or 66 * invalidate the contents of a window in another way (e.g., change fold 67 * settings), must call redraw_later(NOT_VALID) to have the whole window 68 * redisplayed by update_screen() later. 69 * 70 * Commands that change how a buffer is displayed (e.g., setting 'tabstop') 71 * must call redraw_curbuf_later(NOT_VALID) to have all the windows for the 72 * buffer redisplayed by update_screen() later. 73 * 74 * Commands that change highlighting and possibly cause a scroll too must call 75 * redraw_later(SOME_VALID) to update the whole window but still use scrolling 76 * to avoid redrawing everything. But the length of displayed lines must not 77 * change, use NOT_VALID then. 78 * 79 * Commands that move the window position must call redraw_later(NOT_VALID). 80 * TODO: should minimize redrawing by scrolling when possible. 81 * 82 * Commands that change everything (e.g., resizing the screen) must call 83 * redraw_all_later(NOT_VALID) or redraw_all_later(CLEAR). 84 * 85 * Things that are handled indirectly: 86 * - When messages scroll the screen up, msg_scrolled will be set and 87 * update_screen() called to redraw. 88 */ 89 90 #include "vim.h" 91 92 #define MB_FILLER_CHAR '<' /* character used when a double-width character 93 * doesn't fit. */ 94 95 /* 96 * The attributes that are actually active for writing to the screen. 97 */ 98 static int screen_attr = 0; 99 100 /* 101 * Positioning the cursor is reduced by remembering the last position. 102 * Mostly used by windgoto() and screen_char(). 103 */ 104 static int screen_cur_row, screen_cur_col; /* last known cursor position */ 105 106 #ifdef FEAT_SEARCH_EXTRA 107 static match_T search_hl; /* used for 'hlsearch' highlight matching */ 108 #endif 109 110 #ifdef FEAT_FOLDING 111 static foldinfo_T win_foldinfo; /* info for 'foldcolumn' */ 112 static int compute_foldcolumn(win_T *wp, int col); 113 #endif 114 115 /* Flag that is set when drawing for a callback, not from the main command 116 * loop. */ 117 static int redrawing_for_callback = 0; 118 119 /* 120 * Buffer for one screen line (characters and attributes). 121 */ 122 static schar_T *current_ScreenLine; 123 124 static void win_update(win_T *wp); 125 static void win_redr_status(win_T *wp, int ignore_pum); 126 static void win_draw_end(win_T *wp, int c1, int c2, int draw_margin, int row, int endrow, hlf_T hl); 127 #ifdef FEAT_FOLDING 128 static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T lnum, int row); 129 static void fill_foldcolumn(char_u *p, win_T *wp, int closed, linenr_T lnum); 130 static void copy_text_attr(int off, char_u *buf, int len, int attr); 131 #endif 132 static int win_line(win_T *, linenr_T, int, int, int nochange, int number_only); 133 static void draw_vsep_win(win_T *wp, int row); 134 #ifdef FEAT_STL_OPT 135 static void redraw_custom_statusline(win_T *wp); 136 #endif 137 #ifdef FEAT_SEARCH_EXTRA 138 # define SEARCH_HL_PRIORITY 0 139 static void start_search_hl(void); 140 static void end_search_hl(void); 141 static void init_search_hl(win_T *wp); 142 static void prepare_search_hl(win_T *wp, linenr_T lnum); 143 static void next_search_hl(win_T *win, match_T *shl, linenr_T lnum, colnr_T mincol, matchitem_T *cur); 144 static int next_search_hl_pos(match_T *shl, linenr_T lnum, posmatch_T *pos, colnr_T mincol); 145 #endif 146 static void screen_char(unsigned off, int row, int col); 147 static void screen_char_2(unsigned off, int row, int col); 148 static void screenclear2(void); 149 static void lineclear(unsigned off, int width, int attr); 150 static void lineinvalid(unsigned off, int width); 151 static int win_do_lines(win_T *wp, int row, int line_count, int mayclear, int del, int clear_attr); 152 static void win_rest_invalid(win_T *wp); 153 static void msg_pos_mode(void); 154 static void recording_mode(int attr); 155 static int fillchar_status(int *attr, win_T *wp); 156 static int fillchar_vsep(int *attr); 157 #ifdef FEAT_MENU 158 static void redraw_win_toolbar(win_T *wp); 159 #endif 160 #ifdef FEAT_STL_OPT 161 static void win_redr_custom(win_T *wp, int draw_ruler); 162 #endif 163 #ifdef FEAT_CMDL_INFO 164 static void win_redr_ruler(win_T *wp, int always, int ignore_pum); 165 #endif 166 167 /* Ugly global: overrule attribute used by screen_char() */ 168 static int screen_char_attr = 0; 169 170 #if defined(FEAT_SYN_HL) && defined(FEAT_RELTIME) 171 /* Can limit syntax highlight time to 'redrawtime'. */ 172 # define SYN_TIME_LIMIT 1 173 #endif 174 175 #ifdef FEAT_RIGHTLEFT 176 # define HAS_RIGHTLEFT(x) x 177 #else 178 # define HAS_RIGHTLEFT(x) FALSE 179 #endif 180 181 /* 182 * Redraw the current window later, with update_screen(type). 183 * Set must_redraw only if not already set to a higher value. 184 * E.g. if must_redraw is CLEAR, type NOT_VALID will do nothing. 185 */ 186 void 187 redraw_later(int type) 188 { 189 redraw_win_later(curwin, type); 190 } 191 192 void 193 redraw_win_later( 194 win_T *wp, 195 int type) 196 { 197 if (!exiting && wp->w_redr_type < type) 198 { 199 wp->w_redr_type = type; 200 if (type >= NOT_VALID) 201 wp->w_lines_valid = 0; 202 if (must_redraw < type) /* must_redraw is the maximum of all windows */ 203 must_redraw = type; 204 } 205 } 206 207 /* 208 * Force a complete redraw later. Also resets the highlighting. To be used 209 * after executing a shell command that messes up the screen. 210 */ 211 void 212 redraw_later_clear(void) 213 { 214 redraw_all_later(CLEAR); 215 #ifdef FEAT_GUI 216 if (gui.in_use) 217 /* Use a code that will reset gui.highlight_mask in 218 * gui_stop_highlight(). */ 219 screen_attr = HL_ALL + 1; 220 else 221 #endif 222 /* Use attributes that is very unlikely to appear in text. */ 223 screen_attr = HL_BOLD | HL_UNDERLINE | HL_INVERSE | HL_STRIKETHROUGH; 224 } 225 226 /* 227 * Mark all windows to be redrawn later. 228 */ 229 void 230 redraw_all_later(int type) 231 { 232 win_T *wp; 233 234 FOR_ALL_WINDOWS(wp) 235 redraw_win_later(wp, type); 236 // This may be needed when switching tabs. 237 if (must_redraw < type) 238 must_redraw = type; 239 } 240 241 /* 242 * Mark all windows that are editing the current buffer to be updated later. 243 */ 244 void 245 redraw_curbuf_later(int type) 246 { 247 redraw_buf_later(curbuf, type); 248 } 249 250 void 251 redraw_buf_later(buf_T *buf, int type) 252 { 253 win_T *wp; 254 255 FOR_ALL_WINDOWS(wp) 256 { 257 if (wp->w_buffer == buf) 258 redraw_win_later(wp, type); 259 } 260 } 261 262 #if defined(FEAT_SIGNS) || defined(PROTO) 263 void 264 redraw_buf_line_later(buf_T *buf, linenr_T lnum) 265 { 266 win_T *wp; 267 268 FOR_ALL_WINDOWS(wp) 269 if (wp->w_buffer == buf && lnum >= wp->w_topline 270 && lnum < wp->w_botline) 271 redrawWinline(wp, lnum); 272 } 273 #endif 274 275 #if defined(FEAT_JOB_CHANNEL) || defined(PROTO) 276 void 277 redraw_buf_and_status_later(buf_T *buf, int type) 278 { 279 win_T *wp; 280 281 #ifdef FEAT_WILDMENU 282 if (wild_menu_showing != 0) 283 /* Don't redraw while the command line completion is displayed, it 284 * would disappear. */ 285 return; 286 #endif 287 FOR_ALL_WINDOWS(wp) 288 { 289 if (wp->w_buffer == buf) 290 { 291 redraw_win_later(wp, type); 292 wp->w_redr_status = TRUE; 293 } 294 } 295 } 296 #endif 297 298 #if defined(FEAT_TERMRESPONSE) || defined(PROTO) 299 /* 300 * Redraw as soon as possible. When the command line is not scrolled redraw 301 * right away and restore what was on the command line. 302 * Return a code indicating what happened. 303 */ 304 int 305 redraw_asap(int type) 306 { 307 int rows; 308 int cols = screen_Columns; 309 int r; 310 int ret = 0; 311 schar_T *screenline; /* copy from ScreenLines[] */ 312 sattr_T *screenattr; /* copy from ScreenAttrs[] */ 313 int i; 314 u8char_T *screenlineUC = NULL; /* copy from ScreenLinesUC[] */ 315 u8char_T *screenlineC[MAX_MCO]; /* copy from ScreenLinesC[][] */ 316 schar_T *screenline2 = NULL; /* copy from ScreenLines2[] */ 317 318 redraw_later(type); 319 if (msg_scrolled || (State != NORMAL && State != NORMAL_BUSY) || exiting) 320 return ret; 321 322 /* Allocate space to save the text displayed in the command line area. */ 323 rows = screen_Rows - cmdline_row; 324 screenline = (schar_T *)lalloc( 325 (long_u)(rows * cols * sizeof(schar_T)), FALSE); 326 screenattr = (sattr_T *)lalloc( 327 (long_u)(rows * cols * sizeof(sattr_T)), FALSE); 328 if (screenline == NULL || screenattr == NULL) 329 ret = 2; 330 if (enc_utf8) 331 { 332 screenlineUC = (u8char_T *)lalloc( 333 (long_u)(rows * cols * sizeof(u8char_T)), FALSE); 334 if (screenlineUC == NULL) 335 ret = 2; 336 for (i = 0; i < p_mco; ++i) 337 { 338 screenlineC[i] = (u8char_T *)lalloc( 339 (long_u)(rows * cols * sizeof(u8char_T)), FALSE); 340 if (screenlineC[i] == NULL) 341 ret = 2; 342 } 343 } 344 if (enc_dbcs == DBCS_JPNU) 345 { 346 screenline2 = (schar_T *)lalloc( 347 (long_u)(rows * cols * sizeof(schar_T)), FALSE); 348 if (screenline2 == NULL) 349 ret = 2; 350 } 351 352 if (ret != 2) 353 { 354 /* Save the text displayed in the command line area. */ 355 for (r = 0; r < rows; ++r) 356 { 357 mch_memmove(screenline + r * cols, 358 ScreenLines + LineOffset[cmdline_row + r], 359 (size_t)cols * sizeof(schar_T)); 360 mch_memmove(screenattr + r * cols, 361 ScreenAttrs + LineOffset[cmdline_row + r], 362 (size_t)cols * sizeof(sattr_T)); 363 if (enc_utf8) 364 { 365 mch_memmove(screenlineUC + r * cols, 366 ScreenLinesUC + LineOffset[cmdline_row + r], 367 (size_t)cols * sizeof(u8char_T)); 368 for (i = 0; i < p_mco; ++i) 369 mch_memmove(screenlineC[i] + r * cols, 370 ScreenLinesC[i] + LineOffset[cmdline_row + r], 371 (size_t)cols * sizeof(u8char_T)); 372 } 373 if (enc_dbcs == DBCS_JPNU) 374 mch_memmove(screenline2 + r * cols, 375 ScreenLines2 + LineOffset[cmdline_row + r], 376 (size_t)cols * sizeof(schar_T)); 377 } 378 379 update_screen(0); 380 ret = 3; 381 382 if (must_redraw == 0) 383 { 384 int off = (int)(current_ScreenLine - ScreenLines); 385 386 /* Restore the text displayed in the command line area. */ 387 for (r = 0; r < rows; ++r) 388 { 389 mch_memmove(current_ScreenLine, 390 screenline + r * cols, 391 (size_t)cols * sizeof(schar_T)); 392 mch_memmove(ScreenAttrs + off, 393 screenattr + r * cols, 394 (size_t)cols * sizeof(sattr_T)); 395 if (enc_utf8) 396 { 397 mch_memmove(ScreenLinesUC + off, 398 screenlineUC + r * cols, 399 (size_t)cols * sizeof(u8char_T)); 400 for (i = 0; i < p_mco; ++i) 401 mch_memmove(ScreenLinesC[i] + off, 402 screenlineC[i] + r * cols, 403 (size_t)cols * sizeof(u8char_T)); 404 } 405 if (enc_dbcs == DBCS_JPNU) 406 mch_memmove(ScreenLines2 + off, 407 screenline2 + r * cols, 408 (size_t)cols * sizeof(schar_T)); 409 screen_line(cmdline_row + r, 0, cols, cols, FALSE); 410 } 411 ret = 4; 412 } 413 } 414 415 vim_free(screenline); 416 vim_free(screenattr); 417 if (enc_utf8) 418 { 419 vim_free(screenlineUC); 420 for (i = 0; i < p_mco; ++i) 421 vim_free(screenlineC[i]); 422 } 423 if (enc_dbcs == DBCS_JPNU) 424 vim_free(screenline2); 425 426 /* Show the intro message when appropriate. */ 427 maybe_intro_message(); 428 429 setcursor(); 430 431 return ret; 432 } 433 #endif 434 435 /* 436 * Invoked after an asynchronous callback is called. 437 * If an echo command was used the cursor needs to be put back where 438 * it belongs. If highlighting was changed a redraw is needed. 439 * If "call_update_screen" is FALSE don't call update_screen() when at the 440 * command line. 441 */ 442 void 443 redraw_after_callback(int call_update_screen) 444 { 445 ++redrawing_for_callback; 446 447 if (State == HITRETURN || State == ASKMORE) 448 ; // do nothing 449 else if (State & CMDLINE) 450 { 451 // Don't redraw when in prompt_for_number(). 452 if (cmdline_row > 0) 453 { 454 // Redrawing only works when the screen didn't scroll. Don't clear 455 // wildmenu entries. 456 if (msg_scrolled == 0 457 #ifdef FEAT_WILDMENU 458 && wild_menu_showing == 0 459 #endif 460 && call_update_screen) 461 update_screen(0); 462 463 // Redraw in the same position, so that the user can continue 464 // editing the command. 465 redrawcmdline_ex(FALSE); 466 } 467 } 468 else if (State & (NORMAL | INSERT | TERMINAL)) 469 { 470 // keep the command line if possible 471 update_screen(VALID_NO_UPDATE); 472 setcursor(); 473 } 474 cursor_on(); 475 #ifdef FEAT_GUI 476 if (gui.in_use && !gui_mch_is_blink_off()) 477 // Don't update the cursor when it is blinking and off to avoid 478 // flicker. 479 out_flush_cursor(FALSE, FALSE); 480 else 481 #endif 482 out_flush(); 483 484 --redrawing_for_callback; 485 } 486 487 /* 488 * Changed something in the current window, at buffer line "lnum", that 489 * requires that line and possibly other lines to be redrawn. 490 * Used when entering/leaving Insert mode with the cursor on a folded line. 491 * Used to remove the "$" from a change command. 492 * Note that when also inserting/deleting lines w_redraw_top and w_redraw_bot 493 * may become invalid and the whole window will have to be redrawn. 494 */ 495 void 496 redrawWinline( 497 win_T *wp, 498 linenr_T lnum) 499 { 500 if (wp->w_redraw_top == 0 || wp->w_redraw_top > lnum) 501 wp->w_redraw_top = lnum; 502 if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lnum) 503 wp->w_redraw_bot = lnum; 504 redraw_win_later(wp, VALID); 505 } 506 507 void 508 reset_updating_screen(int may_resize_shell UNUSED) 509 { 510 updating_screen = FALSE; 511 #ifdef FEAT_GUI 512 if (may_resize_shell) 513 gui_may_resize_shell(); 514 #endif 515 #ifdef FEAT_TERMINAL 516 term_check_channel_closed_recently(); 517 #endif 518 519 #ifdef HAVE_DROP_FILE 520 // If handle_drop() was called while updating_screen was TRUE need to 521 // handle the drop now. 522 handle_any_postponed_drop(); 523 #endif 524 } 525 526 /* 527 * Update all windows that are editing the current buffer. 528 */ 529 void 530 update_curbuf(int type) 531 { 532 redraw_curbuf_later(type); 533 update_screen(type); 534 } 535 536 /* 537 * Based on the current value of curwin->w_topline, transfer a screenfull 538 * of stuff from Filemem to ScreenLines[], and update curwin->w_botline. 539 * Return OK when the screen was updated, FAIL if it was not done. 540 */ 541 int 542 update_screen(int type_arg) 543 { 544 int type = type_arg; 545 win_T *wp; 546 static int did_intro = FALSE; 547 #if defined(FEAT_SEARCH_EXTRA) || defined(FEAT_CLIPBOARD) 548 int did_one; 549 #endif 550 #ifdef FEAT_GUI 551 int did_undraw = FALSE; 552 int gui_cursor_col = 0; 553 int gui_cursor_row = 0; 554 #endif 555 int no_update = FALSE; 556 557 /* Don't do anything if the screen structures are (not yet) valid. */ 558 if (!screen_valid(TRUE)) 559 return FAIL; 560 561 if (type == VALID_NO_UPDATE) 562 { 563 no_update = TRUE; 564 type = 0; 565 } 566 567 if (must_redraw) 568 { 569 if (type < must_redraw) /* use maximal type */ 570 type = must_redraw; 571 572 /* must_redraw is reset here, so that when we run into some weird 573 * reason to redraw while busy redrawing (e.g., asynchronous 574 * scrolling), or update_topline() in win_update() will cause a 575 * scroll, the screen will be redrawn later or in win_update(). */ 576 must_redraw = 0; 577 } 578 579 /* May need to update w_lines[]. */ 580 if (curwin->w_lines_valid == 0 && type < NOT_VALID 581 #ifdef FEAT_TERMINAL 582 && !term_do_update_window(curwin) 583 #endif 584 ) 585 type = NOT_VALID; 586 587 /* Postpone the redrawing when it's not needed and when being called 588 * recursively. */ 589 if (!redrawing() || updating_screen) 590 { 591 redraw_later(type); /* remember type for next time */ 592 must_redraw = type; 593 if (type > INVERTED_ALL) 594 curwin->w_lines_valid = 0; /* don't use w_lines[].wl_size now */ 595 return FAIL; 596 } 597 598 updating_screen = TRUE; 599 #ifdef FEAT_SYN_HL 600 ++display_tick; /* let syntax code know we're in a next round of 601 * display updating */ 602 #endif 603 if (no_update) 604 ++no_win_do_lines_ins; 605 606 /* 607 * if the screen was scrolled up when displaying a message, scroll it down 608 */ 609 if (msg_scrolled) 610 { 611 clear_cmdline = TRUE; 612 if (msg_scrolled > Rows - 5) /* clearing is faster */ 613 type = CLEAR; 614 else if (type != CLEAR) 615 { 616 check_for_delay(FALSE); 617 if (screen_ins_lines(0, 0, msg_scrolled, (int)Rows, 0, NULL) 618 == FAIL) 619 type = CLEAR; 620 FOR_ALL_WINDOWS(wp) 621 { 622 if (W_WINROW(wp) < msg_scrolled) 623 { 624 if (W_WINROW(wp) + wp->w_height > msg_scrolled 625 && wp->w_redr_type < REDRAW_TOP 626 && wp->w_lines_valid > 0 627 && wp->w_topline == wp->w_lines[0].wl_lnum) 628 { 629 wp->w_upd_rows = msg_scrolled - W_WINROW(wp); 630 wp->w_redr_type = REDRAW_TOP; 631 } 632 else 633 { 634 wp->w_redr_type = NOT_VALID; 635 if (W_WINROW(wp) + wp->w_height + wp->w_status_height 636 <= msg_scrolled) 637 wp->w_redr_status = TRUE; 638 } 639 } 640 } 641 if (!no_update) 642 redraw_cmdline = TRUE; 643 redraw_tabline = TRUE; 644 } 645 msg_scrolled = 0; 646 need_wait_return = FALSE; 647 } 648 649 /* reset cmdline_row now (may have been changed temporarily) */ 650 compute_cmdrow(); 651 652 /* Check for changed highlighting */ 653 if (need_highlight_changed) 654 highlight_changed(); 655 656 if (type == CLEAR) /* first clear screen */ 657 { 658 screenclear(); /* will reset clear_cmdline */ 659 type = NOT_VALID; 660 /* must_redraw may be set indirectly, avoid another redraw later */ 661 must_redraw = 0; 662 } 663 664 if (clear_cmdline) /* going to clear cmdline (done below) */ 665 check_for_delay(FALSE); 666 667 #ifdef FEAT_LINEBREAK 668 /* Force redraw when width of 'number' or 'relativenumber' column 669 * changes. */ 670 if (curwin->w_redr_type < NOT_VALID 671 && curwin->w_nrwidth != ((curwin->w_p_nu || curwin->w_p_rnu) 672 ? number_width(curwin) : 0)) 673 curwin->w_redr_type = NOT_VALID; 674 #endif 675 676 /* 677 * Only start redrawing if there is really something to do. 678 */ 679 if (type == INVERTED) 680 update_curswant(); 681 if (curwin->w_redr_type < type 682 && !((type == VALID 683 && curwin->w_lines[0].wl_valid 684 #ifdef FEAT_DIFF 685 && curwin->w_topfill == curwin->w_old_topfill 686 && curwin->w_botfill == curwin->w_old_botfill 687 #endif 688 && curwin->w_topline == curwin->w_lines[0].wl_lnum) 689 || (type == INVERTED 690 && VIsual_active 691 && curwin->w_old_cursor_lnum == curwin->w_cursor.lnum 692 && curwin->w_old_visual_mode == VIsual_mode 693 && (curwin->w_valid & VALID_VIRTCOL) 694 && curwin->w_old_curswant == curwin->w_curswant) 695 )) 696 curwin->w_redr_type = type; 697 698 /* Redraw the tab pages line if needed. */ 699 if (redraw_tabline || type >= NOT_VALID) 700 draw_tabline(); 701 702 #ifdef FEAT_SYN_HL 703 /* 704 * Correct stored syntax highlighting info for changes in each displayed 705 * buffer. Each buffer must only be done once. 706 */ 707 FOR_ALL_WINDOWS(wp) 708 { 709 if (wp->w_buffer->b_mod_set) 710 { 711 win_T *wwp; 712 713 /* Check if we already did this buffer. */ 714 for (wwp = firstwin; wwp != wp; wwp = wwp->w_next) 715 if (wwp->w_buffer == wp->w_buffer) 716 break; 717 if (wwp == wp && syntax_present(wp)) 718 syn_stack_apply_changes(wp->w_buffer); 719 } 720 } 721 #endif 722 723 /* 724 * Go from top to bottom through the windows, redrawing the ones that need 725 * it. 726 */ 727 #if defined(FEAT_SEARCH_EXTRA) || defined(FEAT_CLIPBOARD) 728 did_one = FALSE; 729 #endif 730 #ifdef FEAT_SEARCH_EXTRA 731 search_hl.rm.regprog = NULL; 732 #endif 733 FOR_ALL_WINDOWS(wp) 734 { 735 if (wp->w_redr_type != 0) 736 { 737 cursor_off(); 738 #if defined(FEAT_SEARCH_EXTRA) || defined(FEAT_CLIPBOARD) 739 if (!did_one) 740 { 741 did_one = TRUE; 742 # ifdef FEAT_SEARCH_EXTRA 743 start_search_hl(); 744 # endif 745 # ifdef FEAT_CLIPBOARD 746 /* When Visual area changed, may have to update selection. */ 747 if (clip_star.available && clip_isautosel_star()) 748 clip_update_selection(&clip_star); 749 if (clip_plus.available && clip_isautosel_plus()) 750 clip_update_selection(&clip_plus); 751 # endif 752 #ifdef FEAT_GUI 753 /* Remove the cursor before starting to do anything, because 754 * scrolling may make it difficult to redraw the text under 755 * it. */ 756 if (gui.in_use && wp == curwin) 757 { 758 gui_cursor_col = gui.cursor_col; 759 gui_cursor_row = gui.cursor_row; 760 gui_undraw_cursor(); 761 did_undraw = TRUE; 762 } 763 #endif 764 } 765 #endif 766 win_update(wp); 767 } 768 769 /* redraw status line after the window to minimize cursor movement */ 770 if (wp->w_redr_status) 771 { 772 cursor_off(); 773 win_redr_status(wp, TRUE); // any popup menu will be redrawn below 774 } 775 } 776 #if defined(FEAT_SEARCH_EXTRA) 777 end_search_hl(); 778 #endif 779 #ifdef FEAT_INS_EXPAND 780 /* May need to redraw the popup menu. */ 781 pum_may_redraw(); 782 #endif 783 784 /* Reset b_mod_set flags. Going through all windows is probably faster 785 * than going through all buffers (there could be many buffers). */ 786 FOR_ALL_WINDOWS(wp) 787 wp->w_buffer->b_mod_set = FALSE; 788 789 reset_updating_screen(TRUE); 790 791 /* Clear or redraw the command line. Done last, because scrolling may 792 * mess up the command line. */ 793 if (clear_cmdline || redraw_cmdline || redraw_mode) 794 showmode(); 795 796 if (no_update) 797 --no_win_do_lines_ins; 798 799 /* May put up an introductory message when not editing a file */ 800 if (!did_intro) 801 maybe_intro_message(); 802 did_intro = TRUE; 803 804 #ifdef FEAT_GUI 805 /* Redraw the cursor and update the scrollbars when all screen updating is 806 * done. */ 807 if (gui.in_use) 808 { 809 if (did_undraw && !gui_mch_is_blink_off()) 810 { 811 mch_disable_flush(); 812 out_flush(); /* required before updating the cursor */ 813 mch_enable_flush(); 814 815 /* Put the GUI position where the cursor was, gui_update_cursor() 816 * uses that. */ 817 gui.col = gui_cursor_col; 818 gui.row = gui_cursor_row; 819 gui.col = mb_fix_col(gui.col, gui.row); 820 gui_update_cursor(FALSE, FALSE); 821 gui_may_flush(); 822 screen_cur_col = gui.col; 823 screen_cur_row = gui.row; 824 } 825 else 826 out_flush(); 827 gui_update_scrollbars(FALSE); 828 } 829 #endif 830 return OK; 831 } 832 833 #if defined(FEAT_NETBEANS_INTG) || defined(FEAT_GUI) 834 /* 835 * Prepare for updating one or more windows. 836 * Caller must check for "updating_screen" already set to avoid recursiveness. 837 */ 838 static void 839 update_prepare(void) 840 { 841 cursor_off(); 842 updating_screen = TRUE; 843 #ifdef FEAT_GUI 844 /* Remove the cursor before starting to do anything, because scrolling may 845 * make it difficult to redraw the text under it. */ 846 if (gui.in_use) 847 gui_undraw_cursor(); 848 #endif 849 #ifdef FEAT_SEARCH_EXTRA 850 start_search_hl(); 851 #endif 852 } 853 854 /* 855 * Finish updating one or more windows. 856 */ 857 static void 858 update_finish(void) 859 { 860 if (redraw_cmdline || redraw_mode) 861 showmode(); 862 863 # ifdef FEAT_SEARCH_EXTRA 864 end_search_hl(); 865 # endif 866 867 reset_updating_screen(TRUE); 868 869 # ifdef FEAT_GUI 870 /* Redraw the cursor and update the scrollbars when all screen updating is 871 * done. */ 872 if (gui.in_use) 873 { 874 out_flush_cursor(FALSE, FALSE); 875 gui_update_scrollbars(FALSE); 876 } 877 # endif 878 } 879 #endif 880 881 #if defined(FEAT_CONCEAL) || defined(PROTO) 882 /* 883 * Return TRUE if the cursor line in window "wp" may be concealed, according 884 * to the 'concealcursor' option. 885 */ 886 int 887 conceal_cursor_line(win_T *wp) 888 { 889 int c; 890 891 if (*wp->w_p_cocu == NUL) 892 return FALSE; 893 if (get_real_state() & VISUAL) 894 c = 'v'; 895 else if (State & INSERT) 896 c = 'i'; 897 else if (State & NORMAL) 898 c = 'n'; 899 else if (State & CMDLINE) 900 c = 'c'; 901 else 902 return FALSE; 903 return vim_strchr(wp->w_p_cocu, c) != NULL; 904 } 905 906 /* 907 * Check if the cursor line needs to be redrawn because of 'concealcursor'. 908 */ 909 void 910 conceal_check_cursor_line(void) 911 { 912 if (curwin->w_p_cole > 0 && conceal_cursor_line(curwin)) 913 { 914 need_cursor_line_redraw = TRUE; 915 /* Need to recompute cursor column, e.g., when starting Visual mode 916 * without concealing. */ 917 curs_columns(TRUE); 918 } 919 } 920 #endif 921 922 #if defined(FEAT_NETBEANS_INTG) || defined(PROTO) 923 void 924 update_debug_sign(buf_T *buf, linenr_T lnum) 925 { 926 win_T *wp; 927 int doit = FALSE; 928 929 # ifdef FEAT_FOLDING 930 win_foldinfo.fi_level = 0; 931 # endif 932 933 // update/delete a specific sign 934 redraw_buf_line_later(buf, lnum); 935 936 // check if it resulted in the need to redraw a window 937 FOR_ALL_WINDOWS(wp) 938 if (wp->w_redr_type != 0) 939 doit = TRUE; 940 941 /* Return when there is nothing to do, screen updating is already 942 * happening (recursive call), messages on the screen or still starting up. 943 */ 944 if (!doit || updating_screen 945 || State == ASKMORE || State == HITRETURN 946 || msg_scrolled 947 #ifdef FEAT_GUI 948 || gui.starting 949 #endif 950 || starting) 951 return; 952 953 /* update all windows that need updating */ 954 update_prepare(); 955 956 FOR_ALL_WINDOWS(wp) 957 { 958 if (wp->w_redr_type != 0) 959 win_update(wp); 960 if (wp->w_redr_status) 961 win_redr_status(wp, FALSE); 962 } 963 964 update_finish(); 965 } 966 #endif 967 968 969 #if defined(FEAT_GUI) || defined(PROTO) 970 /* 971 * Update a single window, its status line and maybe the command line msg. 972 * Used for the GUI scrollbar. 973 */ 974 void 975 updateWindow(win_T *wp) 976 { 977 /* return if already busy updating */ 978 if (updating_screen) 979 return; 980 981 update_prepare(); 982 983 #ifdef FEAT_CLIPBOARD 984 /* When Visual area changed, may have to update selection. */ 985 if (clip_star.available && clip_isautosel_star()) 986 clip_update_selection(&clip_star); 987 if (clip_plus.available && clip_isautosel_plus()) 988 clip_update_selection(&clip_plus); 989 #endif 990 991 win_update(wp); 992 993 /* When the screen was cleared redraw the tab pages line. */ 994 if (redraw_tabline) 995 draw_tabline(); 996 997 if (wp->w_redr_status 998 # ifdef FEAT_CMDL_INFO 999 || p_ru 1000 # endif 1001 # ifdef FEAT_STL_OPT 1002 || *p_stl != NUL || *wp->w_p_stl != NUL 1003 # endif 1004 ) 1005 win_redr_status(wp, FALSE); 1006 1007 update_finish(); 1008 } 1009 #endif 1010 1011 /* 1012 * Update a single window. 1013 * 1014 * This may cause the windows below it also to be redrawn (when clearing the 1015 * screen or scrolling lines). 1016 * 1017 * How the window is redrawn depends on wp->w_redr_type. Each type also 1018 * implies the one below it. 1019 * NOT_VALID redraw the whole window 1020 * SOME_VALID redraw the whole window but do scroll when possible 1021 * REDRAW_TOP redraw the top w_upd_rows window lines, otherwise like VALID 1022 * INVERTED redraw the changed part of the Visual area 1023 * INVERTED_ALL redraw the whole Visual area 1024 * VALID 1. scroll up/down to adjust for a changed w_topline 1025 * 2. update lines at the top when scrolled down 1026 * 3. redraw changed text: 1027 * - if wp->w_buffer->b_mod_set set, update lines between 1028 * b_mod_top and b_mod_bot. 1029 * - if wp->w_redraw_top non-zero, redraw lines between 1030 * wp->w_redraw_top and wp->w_redr_bot. 1031 * - continue redrawing when syntax status is invalid. 1032 * 4. if scrolled up, update lines at the bottom. 1033 * This results in three areas that may need updating: 1034 * top: from first row to top_end (when scrolled down) 1035 * mid: from mid_start to mid_end (update inversion or changed text) 1036 * bot: from bot_start to last row (when scrolled up) 1037 */ 1038 static void 1039 win_update(win_T *wp) 1040 { 1041 buf_T *buf = wp->w_buffer; 1042 int type; 1043 int top_end = 0; /* Below last row of the top area that needs 1044 updating. 0 when no top area updating. */ 1045 int mid_start = 999;/* first row of the mid area that needs 1046 updating. 999 when no mid area updating. */ 1047 int mid_end = 0; /* Below last row of the mid area that needs 1048 updating. 0 when no mid area updating. */ 1049 int bot_start = 999;/* first row of the bot area that needs 1050 updating. 999 when no bot area updating */ 1051 int scrolled_down = FALSE; /* TRUE when scrolled down when 1052 w_topline got smaller a bit */ 1053 #ifdef FEAT_SEARCH_EXTRA 1054 matchitem_T *cur; /* points to the match list */ 1055 int top_to_mod = FALSE; /* redraw above mod_top */ 1056 #endif 1057 1058 int row; /* current window row to display */ 1059 linenr_T lnum; /* current buffer lnum to display */ 1060 int idx; /* current index in w_lines[] */ 1061 int srow; /* starting row of the current line */ 1062 1063 int eof = FALSE; /* if TRUE, we hit the end of the file */ 1064 int didline = FALSE; /* if TRUE, we finished the last line */ 1065 int i; 1066 long j; 1067 static int recursive = FALSE; /* being called recursively */ 1068 int old_botline = wp->w_botline; 1069 #ifdef FEAT_FOLDING 1070 long fold_count; 1071 #endif 1072 #ifdef FEAT_SYN_HL 1073 /* remember what happened to the previous line, to know if 1074 * check_visual_highlight() can be used */ 1075 #define DID_NONE 1 /* didn't update a line */ 1076 #define DID_LINE 2 /* updated a normal line */ 1077 #define DID_FOLD 3 /* updated a folded line */ 1078 int did_update = DID_NONE; 1079 linenr_T syntax_last_parsed = 0; /* last parsed text line */ 1080 #endif 1081 linenr_T mod_top = 0; 1082 linenr_T mod_bot = 0; 1083 #if defined(FEAT_SYN_HL) || defined(FEAT_SEARCH_EXTRA) 1084 int save_got_int; 1085 #endif 1086 #ifdef SYN_TIME_LIMIT 1087 proftime_T syntax_tm; 1088 #endif 1089 1090 type = wp->w_redr_type; 1091 1092 if (type == NOT_VALID) 1093 { 1094 wp->w_redr_status = TRUE; 1095 wp->w_lines_valid = 0; 1096 } 1097 1098 /* Window is zero-height: nothing to draw. */ 1099 if (wp->w_height + WINBAR_HEIGHT(wp) == 0) 1100 { 1101 wp->w_redr_type = 0; 1102 return; 1103 } 1104 1105 /* Window is zero-width: Only need to draw the separator. */ 1106 if (wp->w_width == 0) 1107 { 1108 /* draw the vertical separator right of this window */ 1109 draw_vsep_win(wp, 0); 1110 wp->w_redr_type = 0; 1111 return; 1112 } 1113 1114 #ifdef FEAT_TERMINAL 1115 // If this window contains a terminal, redraw works completely differently. 1116 if (term_do_update_window(wp)) 1117 { 1118 term_update_window(wp); 1119 # ifdef FEAT_MENU 1120 /* Draw the window toolbar, if there is one. */ 1121 if (winbar_height(wp) > 0) 1122 redraw_win_toolbar(wp); 1123 # endif 1124 wp->w_redr_type = 0; 1125 return; 1126 } 1127 #endif 1128 1129 #ifdef FEAT_SEARCH_EXTRA 1130 init_search_hl(wp); 1131 #endif 1132 1133 #ifdef FEAT_LINEBREAK 1134 /* Force redraw when width of 'number' or 'relativenumber' column 1135 * changes. */ 1136 i = (wp->w_p_nu || wp->w_p_rnu) ? number_width(wp) : 0; 1137 if (wp->w_nrwidth != i) 1138 { 1139 type = NOT_VALID; 1140 wp->w_nrwidth = i; 1141 } 1142 else 1143 #endif 1144 1145 if (buf->b_mod_set && buf->b_mod_xlines != 0 && wp->w_redraw_top != 0) 1146 { 1147 /* 1148 * When there are both inserted/deleted lines and specific lines to be 1149 * redrawn, w_redraw_top and w_redraw_bot may be invalid, just redraw 1150 * everything (only happens when redrawing is off for while). 1151 */ 1152 type = NOT_VALID; 1153 } 1154 else 1155 { 1156 /* 1157 * Set mod_top to the first line that needs displaying because of 1158 * changes. Set mod_bot to the first line after the changes. 1159 */ 1160 mod_top = wp->w_redraw_top; 1161 if (wp->w_redraw_bot != 0) 1162 mod_bot = wp->w_redraw_bot + 1; 1163 else 1164 mod_bot = 0; 1165 if (buf->b_mod_set) 1166 { 1167 if (mod_top == 0 || mod_top > buf->b_mod_top) 1168 { 1169 mod_top = buf->b_mod_top; 1170 #ifdef FEAT_SYN_HL 1171 /* Need to redraw lines above the change that may be included 1172 * in a pattern match. */ 1173 if (syntax_present(wp)) 1174 { 1175 mod_top -= buf->b_s.b_syn_sync_linebreaks; 1176 if (mod_top < 1) 1177 mod_top = 1; 1178 } 1179 #endif 1180 } 1181 if (mod_bot == 0 || mod_bot < buf->b_mod_bot) 1182 mod_bot = buf->b_mod_bot; 1183 1184 #ifdef FEAT_SEARCH_EXTRA 1185 /* When 'hlsearch' is on and using a multi-line search pattern, a 1186 * change in one line may make the Search highlighting in a 1187 * previous line invalid. Simple solution: redraw all visible 1188 * lines above the change. 1189 * Same for a match pattern. 1190 */ 1191 if (search_hl.rm.regprog != NULL 1192 && re_multiline(search_hl.rm.regprog)) 1193 top_to_mod = TRUE; 1194 else 1195 { 1196 cur = wp->w_match_head; 1197 while (cur != NULL) 1198 { 1199 if (cur->match.regprog != NULL 1200 && re_multiline(cur->match.regprog)) 1201 { 1202 top_to_mod = TRUE; 1203 break; 1204 } 1205 cur = cur->next; 1206 } 1207 } 1208 #endif 1209 } 1210 #ifdef FEAT_FOLDING 1211 if (mod_top != 0 && hasAnyFolding(wp)) 1212 { 1213 linenr_T lnumt, lnumb; 1214 1215 /* 1216 * A change in a line can cause lines above it to become folded or 1217 * unfolded. Find the top most buffer line that may be affected. 1218 * If the line was previously folded and displayed, get the first 1219 * line of that fold. If the line is folded now, get the first 1220 * folded line. Use the minimum of these two. 1221 */ 1222 1223 /* Find last valid w_lines[] entry above mod_top. Set lnumt to 1224 * the line below it. If there is no valid entry, use w_topline. 1225 * Find the first valid w_lines[] entry below mod_bot. Set lnumb 1226 * to this line. If there is no valid entry, use MAXLNUM. */ 1227 lnumt = wp->w_topline; 1228 lnumb = MAXLNUM; 1229 for (i = 0; i < wp->w_lines_valid; ++i) 1230 if (wp->w_lines[i].wl_valid) 1231 { 1232 if (wp->w_lines[i].wl_lastlnum < mod_top) 1233 lnumt = wp->w_lines[i].wl_lastlnum + 1; 1234 if (lnumb == MAXLNUM && wp->w_lines[i].wl_lnum >= mod_bot) 1235 { 1236 lnumb = wp->w_lines[i].wl_lnum; 1237 /* When there is a fold column it might need updating 1238 * in the next line ("J" just above an open fold). */ 1239 if (compute_foldcolumn(wp, 0) > 0) 1240 ++lnumb; 1241 } 1242 } 1243 1244 (void)hasFoldingWin(wp, mod_top, &mod_top, NULL, TRUE, NULL); 1245 if (mod_top > lnumt) 1246 mod_top = lnumt; 1247 1248 /* Now do the same for the bottom line (one above mod_bot). */ 1249 --mod_bot; 1250 (void)hasFoldingWin(wp, mod_bot, NULL, &mod_bot, TRUE, NULL); 1251 ++mod_bot; 1252 if (mod_bot < lnumb) 1253 mod_bot = lnumb; 1254 } 1255 #endif 1256 1257 /* When a change starts above w_topline and the end is below 1258 * w_topline, start redrawing at w_topline. 1259 * If the end of the change is above w_topline: do like no change was 1260 * made, but redraw the first line to find changes in syntax. */ 1261 if (mod_top != 0 && mod_top < wp->w_topline) 1262 { 1263 if (mod_bot > wp->w_topline) 1264 mod_top = wp->w_topline; 1265 #ifdef FEAT_SYN_HL 1266 else if (syntax_present(wp)) 1267 top_end = 1; 1268 #endif 1269 } 1270 1271 /* When line numbers are displayed need to redraw all lines below 1272 * inserted/deleted lines. */ 1273 if (mod_top != 0 && buf->b_mod_xlines != 0 && wp->w_p_nu) 1274 mod_bot = MAXLNUM; 1275 } 1276 wp->w_redraw_top = 0; // reset for next time 1277 wp->w_redraw_bot = 0; 1278 1279 /* 1280 * When only displaying the lines at the top, set top_end. Used when 1281 * window has scrolled down for msg_scrolled. 1282 */ 1283 if (type == REDRAW_TOP) 1284 { 1285 j = 0; 1286 for (i = 0; i < wp->w_lines_valid; ++i) 1287 { 1288 j += wp->w_lines[i].wl_size; 1289 if (j >= wp->w_upd_rows) 1290 { 1291 top_end = j; 1292 break; 1293 } 1294 } 1295 if (top_end == 0) 1296 /* not found (cannot happen?): redraw everything */ 1297 type = NOT_VALID; 1298 else 1299 /* top area defined, the rest is VALID */ 1300 type = VALID; 1301 } 1302 1303 /* Trick: we want to avoid clearing the screen twice. screenclear() will 1304 * set "screen_cleared" to TRUE. The special value MAYBE (which is still 1305 * non-zero and thus not FALSE) will indicate that screenclear() was not 1306 * called. */ 1307 if (screen_cleared) 1308 screen_cleared = MAYBE; 1309 1310 /* 1311 * If there are no changes on the screen that require a complete redraw, 1312 * handle three cases: 1313 * 1: we are off the top of the screen by a few lines: scroll down 1314 * 2: wp->w_topline is below wp->w_lines[0].wl_lnum: may scroll up 1315 * 3: wp->w_topline is wp->w_lines[0].wl_lnum: find first entry in 1316 * w_lines[] that needs updating. 1317 */ 1318 if ((type == VALID || type == SOME_VALID 1319 || type == INVERTED || type == INVERTED_ALL) 1320 #ifdef FEAT_DIFF 1321 && !wp->w_botfill && !wp->w_old_botfill 1322 #endif 1323 ) 1324 { 1325 if (mod_top != 0 && wp->w_topline == mod_top) 1326 { 1327 /* 1328 * w_topline is the first changed line, the scrolling will be done 1329 * further down. 1330 */ 1331 } 1332 else if (wp->w_lines[0].wl_valid 1333 && (wp->w_topline < wp->w_lines[0].wl_lnum 1334 #ifdef FEAT_DIFF 1335 || (wp->w_topline == wp->w_lines[0].wl_lnum 1336 && wp->w_topfill > wp->w_old_topfill) 1337 #endif 1338 )) 1339 { 1340 /* 1341 * New topline is above old topline: May scroll down. 1342 */ 1343 #ifdef FEAT_FOLDING 1344 if (hasAnyFolding(wp)) 1345 { 1346 linenr_T ln; 1347 1348 /* count the number of lines we are off, counting a sequence 1349 * of folded lines as one */ 1350 j = 0; 1351 for (ln = wp->w_topline; ln < wp->w_lines[0].wl_lnum; ++ln) 1352 { 1353 ++j; 1354 if (j >= wp->w_height - 2) 1355 break; 1356 (void)hasFoldingWin(wp, ln, NULL, &ln, TRUE, NULL); 1357 } 1358 } 1359 else 1360 #endif 1361 j = wp->w_lines[0].wl_lnum - wp->w_topline; 1362 if (j < wp->w_height - 2) /* not too far off */ 1363 { 1364 i = plines_m_win(wp, wp->w_topline, wp->w_lines[0].wl_lnum - 1); 1365 #ifdef FEAT_DIFF 1366 /* insert extra lines for previously invisible filler lines */ 1367 if (wp->w_lines[0].wl_lnum != wp->w_topline) 1368 i += diff_check_fill(wp, wp->w_lines[0].wl_lnum) 1369 - wp->w_old_topfill; 1370 #endif 1371 if (i < wp->w_height - 2) /* less than a screen off */ 1372 { 1373 /* 1374 * Try to insert the correct number of lines. 1375 * If not the last window, delete the lines at the bottom. 1376 * win_ins_lines may fail when the terminal can't do it. 1377 */ 1378 if (i > 0) 1379 check_for_delay(FALSE); 1380 if (win_ins_lines(wp, 0, i, FALSE, wp == firstwin) == OK) 1381 { 1382 if (wp->w_lines_valid != 0) 1383 { 1384 /* Need to update rows that are new, stop at the 1385 * first one that scrolled down. */ 1386 top_end = i; 1387 scrolled_down = TRUE; 1388 1389 /* Move the entries that were scrolled, disable 1390 * the entries for the lines to be redrawn. */ 1391 if ((wp->w_lines_valid += j) > wp->w_height) 1392 wp->w_lines_valid = wp->w_height; 1393 for (idx = wp->w_lines_valid; idx - j >= 0; idx--) 1394 wp->w_lines[idx] = wp->w_lines[idx - j]; 1395 while (idx >= 0) 1396 wp->w_lines[idx--].wl_valid = FALSE; 1397 } 1398 } 1399 else 1400 mid_start = 0; /* redraw all lines */ 1401 } 1402 else 1403 mid_start = 0; /* redraw all lines */ 1404 } 1405 else 1406 mid_start = 0; /* redraw all lines */ 1407 } 1408 else 1409 { 1410 /* 1411 * New topline is at or below old topline: May scroll up. 1412 * When topline didn't change, find first entry in w_lines[] that 1413 * needs updating. 1414 */ 1415 1416 /* try to find wp->w_topline in wp->w_lines[].wl_lnum */ 1417 j = -1; 1418 row = 0; 1419 for (i = 0; i < wp->w_lines_valid; i++) 1420 { 1421 if (wp->w_lines[i].wl_valid 1422 && wp->w_lines[i].wl_lnum == wp->w_topline) 1423 { 1424 j = i; 1425 break; 1426 } 1427 row += wp->w_lines[i].wl_size; 1428 } 1429 if (j == -1) 1430 { 1431 /* if wp->w_topline is not in wp->w_lines[].wl_lnum redraw all 1432 * lines */ 1433 mid_start = 0; 1434 } 1435 else 1436 { 1437 /* 1438 * Try to delete the correct number of lines. 1439 * wp->w_topline is at wp->w_lines[i].wl_lnum. 1440 */ 1441 #ifdef FEAT_DIFF 1442 /* If the topline didn't change, delete old filler lines, 1443 * otherwise delete filler lines of the new topline... */ 1444 if (wp->w_lines[0].wl_lnum == wp->w_topline) 1445 row += wp->w_old_topfill; 1446 else 1447 row += diff_check_fill(wp, wp->w_topline); 1448 /* ... but don't delete new filler lines. */ 1449 row -= wp->w_topfill; 1450 #endif 1451 if (row > 0) 1452 { 1453 check_for_delay(FALSE); 1454 if (win_del_lines(wp, 0, row, FALSE, wp == firstwin, 0) 1455 == OK) 1456 bot_start = wp->w_height - row; 1457 else 1458 mid_start = 0; /* redraw all lines */ 1459 } 1460 if ((row == 0 || bot_start < 999) && wp->w_lines_valid != 0) 1461 { 1462 /* 1463 * Skip the lines (below the deleted lines) that are still 1464 * valid and don't need redrawing. Copy their info 1465 * upwards, to compensate for the deleted lines. Set 1466 * bot_start to the first row that needs redrawing. 1467 */ 1468 bot_start = 0; 1469 idx = 0; 1470 for (;;) 1471 { 1472 wp->w_lines[idx] = wp->w_lines[j]; 1473 /* stop at line that didn't fit, unless it is still 1474 * valid (no lines deleted) */ 1475 if (row > 0 && bot_start + row 1476 + (int)wp->w_lines[j].wl_size > wp->w_height) 1477 { 1478 wp->w_lines_valid = idx + 1; 1479 break; 1480 } 1481 bot_start += wp->w_lines[idx++].wl_size; 1482 1483 /* stop at the last valid entry in w_lines[].wl_size */ 1484 if (++j >= wp->w_lines_valid) 1485 { 1486 wp->w_lines_valid = idx; 1487 break; 1488 } 1489 } 1490 #ifdef FEAT_DIFF 1491 /* Correct the first entry for filler lines at the top 1492 * when it won't get updated below. */ 1493 if (wp->w_p_diff && bot_start > 0) 1494 wp->w_lines[0].wl_size = 1495 plines_win_nofill(wp, wp->w_topline, TRUE) 1496 + wp->w_topfill; 1497 #endif 1498 } 1499 } 1500 } 1501 1502 /* When starting redraw in the first line, redraw all lines. When 1503 * there is only one window it's probably faster to clear the screen 1504 * first. */ 1505 if (mid_start == 0) 1506 { 1507 mid_end = wp->w_height; 1508 if (ONE_WINDOW) 1509 { 1510 /* Clear the screen when it was not done by win_del_lines() or 1511 * win_ins_lines() above, "screen_cleared" is FALSE or MAYBE 1512 * then. */ 1513 if (screen_cleared != TRUE) 1514 screenclear(); 1515 /* The screen was cleared, redraw the tab pages line. */ 1516 if (redraw_tabline) 1517 draw_tabline(); 1518 } 1519 } 1520 1521 /* When win_del_lines() or win_ins_lines() caused the screen to be 1522 * cleared (only happens for the first window) or when screenclear() 1523 * was called directly above, "must_redraw" will have been set to 1524 * NOT_VALID, need to reset it here to avoid redrawing twice. */ 1525 if (screen_cleared == TRUE) 1526 must_redraw = 0; 1527 } 1528 else 1529 { 1530 /* Not VALID or INVERTED: redraw all lines. */ 1531 mid_start = 0; 1532 mid_end = wp->w_height; 1533 } 1534 1535 if (type == SOME_VALID) 1536 { 1537 /* SOME_VALID: redraw all lines. */ 1538 mid_start = 0; 1539 mid_end = wp->w_height; 1540 type = NOT_VALID; 1541 } 1542 1543 /* check if we are updating or removing the inverted part */ 1544 if ((VIsual_active && buf == curwin->w_buffer) 1545 || (wp->w_old_cursor_lnum != 0 && type != NOT_VALID)) 1546 { 1547 linenr_T from, to; 1548 1549 if (VIsual_active) 1550 { 1551 if (VIsual_active 1552 && (VIsual_mode != wp->w_old_visual_mode 1553 || type == INVERTED_ALL)) 1554 { 1555 /* 1556 * If the type of Visual selection changed, redraw the whole 1557 * selection. Also when the ownership of the X selection is 1558 * gained or lost. 1559 */ 1560 if (curwin->w_cursor.lnum < VIsual.lnum) 1561 { 1562 from = curwin->w_cursor.lnum; 1563 to = VIsual.lnum; 1564 } 1565 else 1566 { 1567 from = VIsual.lnum; 1568 to = curwin->w_cursor.lnum; 1569 } 1570 /* redraw more when the cursor moved as well */ 1571 if (wp->w_old_cursor_lnum < from) 1572 from = wp->w_old_cursor_lnum; 1573 if (wp->w_old_cursor_lnum > to) 1574 to = wp->w_old_cursor_lnum; 1575 if (wp->w_old_visual_lnum < from) 1576 from = wp->w_old_visual_lnum; 1577 if (wp->w_old_visual_lnum > to) 1578 to = wp->w_old_visual_lnum; 1579 } 1580 else 1581 { 1582 /* 1583 * Find the line numbers that need to be updated: The lines 1584 * between the old cursor position and the current cursor 1585 * position. Also check if the Visual position changed. 1586 */ 1587 if (curwin->w_cursor.lnum < wp->w_old_cursor_lnum) 1588 { 1589 from = curwin->w_cursor.lnum; 1590 to = wp->w_old_cursor_lnum; 1591 } 1592 else 1593 { 1594 from = wp->w_old_cursor_lnum; 1595 to = curwin->w_cursor.lnum; 1596 if (from == 0) /* Visual mode just started */ 1597 from = to; 1598 } 1599 1600 if (VIsual.lnum != wp->w_old_visual_lnum 1601 || VIsual.col != wp->w_old_visual_col) 1602 { 1603 if (wp->w_old_visual_lnum < from 1604 && wp->w_old_visual_lnum != 0) 1605 from = wp->w_old_visual_lnum; 1606 if (wp->w_old_visual_lnum > to) 1607 to = wp->w_old_visual_lnum; 1608 if (VIsual.lnum < from) 1609 from = VIsual.lnum; 1610 if (VIsual.lnum > to) 1611 to = VIsual.lnum; 1612 } 1613 } 1614 1615 /* 1616 * If in block mode and changed column or curwin->w_curswant: 1617 * update all lines. 1618 * First compute the actual start and end column. 1619 */ 1620 if (VIsual_mode == Ctrl_V) 1621 { 1622 colnr_T fromc, toc; 1623 #if defined(FEAT_LINEBREAK) 1624 int save_ve_flags = ve_flags; 1625 1626 if (curwin->w_p_lbr) 1627 ve_flags = VE_ALL; 1628 #endif 1629 getvcols(wp, &VIsual, &curwin->w_cursor, &fromc, &toc); 1630 #if defined(FEAT_LINEBREAK) 1631 ve_flags = save_ve_flags; 1632 #endif 1633 ++toc; 1634 if (curwin->w_curswant == MAXCOL) 1635 toc = MAXCOL; 1636 1637 if (fromc != wp->w_old_cursor_fcol 1638 || toc != wp->w_old_cursor_lcol) 1639 { 1640 if (from > VIsual.lnum) 1641 from = VIsual.lnum; 1642 if (to < VIsual.lnum) 1643 to = VIsual.lnum; 1644 } 1645 wp->w_old_cursor_fcol = fromc; 1646 wp->w_old_cursor_lcol = toc; 1647 } 1648 } 1649 else 1650 { 1651 /* Use the line numbers of the old Visual area. */ 1652 if (wp->w_old_cursor_lnum < wp->w_old_visual_lnum) 1653 { 1654 from = wp->w_old_cursor_lnum; 1655 to = wp->w_old_visual_lnum; 1656 } 1657 else 1658 { 1659 from = wp->w_old_visual_lnum; 1660 to = wp->w_old_cursor_lnum; 1661 } 1662 } 1663 1664 /* 1665 * There is no need to update lines above the top of the window. 1666 */ 1667 if (from < wp->w_topline) 1668 from = wp->w_topline; 1669 1670 /* 1671 * If we know the value of w_botline, use it to restrict the update to 1672 * the lines that are visible in the window. 1673 */ 1674 if (wp->w_valid & VALID_BOTLINE) 1675 { 1676 if (from >= wp->w_botline) 1677 from = wp->w_botline - 1; 1678 if (to >= wp->w_botline) 1679 to = wp->w_botline - 1; 1680 } 1681 1682 /* 1683 * Find the minimal part to be updated. 1684 * Watch out for scrolling that made entries in w_lines[] invalid. 1685 * E.g., CTRL-U makes the first half of w_lines[] invalid and sets 1686 * top_end; need to redraw from top_end to the "to" line. 1687 * A middle mouse click with a Visual selection may change the text 1688 * above the Visual area and reset wl_valid, do count these for 1689 * mid_end (in srow). 1690 */ 1691 if (mid_start > 0) 1692 { 1693 lnum = wp->w_topline; 1694 idx = 0; 1695 srow = 0; 1696 if (scrolled_down) 1697 mid_start = top_end; 1698 else 1699 mid_start = 0; 1700 while (lnum < from && idx < wp->w_lines_valid) /* find start */ 1701 { 1702 if (wp->w_lines[idx].wl_valid) 1703 mid_start += wp->w_lines[idx].wl_size; 1704 else if (!scrolled_down) 1705 srow += wp->w_lines[idx].wl_size; 1706 ++idx; 1707 # ifdef FEAT_FOLDING 1708 if (idx < wp->w_lines_valid && wp->w_lines[idx].wl_valid) 1709 lnum = wp->w_lines[idx].wl_lnum; 1710 else 1711 # endif 1712 ++lnum; 1713 } 1714 srow += mid_start; 1715 mid_end = wp->w_height; 1716 for ( ; idx < wp->w_lines_valid; ++idx) /* find end */ 1717 { 1718 if (wp->w_lines[idx].wl_valid 1719 && wp->w_lines[idx].wl_lnum >= to + 1) 1720 { 1721 /* Only update until first row of this line */ 1722 mid_end = srow; 1723 break; 1724 } 1725 srow += wp->w_lines[idx].wl_size; 1726 } 1727 } 1728 } 1729 1730 if (VIsual_active && buf == curwin->w_buffer) 1731 { 1732 wp->w_old_visual_mode = VIsual_mode; 1733 wp->w_old_cursor_lnum = curwin->w_cursor.lnum; 1734 wp->w_old_visual_lnum = VIsual.lnum; 1735 wp->w_old_visual_col = VIsual.col; 1736 wp->w_old_curswant = curwin->w_curswant; 1737 } 1738 else 1739 { 1740 wp->w_old_visual_mode = 0; 1741 wp->w_old_cursor_lnum = 0; 1742 wp->w_old_visual_lnum = 0; 1743 wp->w_old_visual_col = 0; 1744 } 1745 1746 #if defined(FEAT_SYN_HL) || defined(FEAT_SEARCH_EXTRA) 1747 /* reset got_int, otherwise regexp won't work */ 1748 save_got_int = got_int; 1749 got_int = 0; 1750 #endif 1751 #ifdef SYN_TIME_LIMIT 1752 /* Set the time limit to 'redrawtime'. */ 1753 profile_setlimit(p_rdt, &syntax_tm); 1754 syn_set_timeout(&syntax_tm); 1755 #endif 1756 #ifdef FEAT_FOLDING 1757 win_foldinfo.fi_level = 0; 1758 #endif 1759 1760 #ifdef FEAT_MENU 1761 /* 1762 * Draw the window toolbar, if there is one. 1763 * TODO: only when needed. 1764 */ 1765 if (winbar_height(wp) > 0) 1766 redraw_win_toolbar(wp); 1767 #endif 1768 1769 /* 1770 * Update all the window rows. 1771 */ 1772 idx = 0; /* first entry in w_lines[].wl_size */ 1773 row = 0; 1774 srow = 0; 1775 lnum = wp->w_topline; /* first line shown in window */ 1776 for (;;) 1777 { 1778 /* stop updating when reached the end of the window (check for _past_ 1779 * the end of the window is at the end of the loop) */ 1780 if (row == wp->w_height) 1781 { 1782 didline = TRUE; 1783 break; 1784 } 1785 1786 /* stop updating when hit the end of the file */ 1787 if (lnum > buf->b_ml.ml_line_count) 1788 { 1789 eof = TRUE; 1790 break; 1791 } 1792 1793 /* Remember the starting row of the line that is going to be dealt 1794 * with. It is used further down when the line doesn't fit. */ 1795 srow = row; 1796 1797 /* 1798 * Update a line when it is in an area that needs updating, when it 1799 * has changes or w_lines[idx] is invalid. 1800 * "bot_start" may be halfway a wrapped line after using 1801 * win_del_lines(), check if the current line includes it. 1802 * When syntax folding is being used, the saved syntax states will 1803 * already have been updated, we can't see where the syntax state is 1804 * the same again, just update until the end of the window. 1805 */ 1806 if (row < top_end 1807 || (row >= mid_start && row < mid_end) 1808 #ifdef FEAT_SEARCH_EXTRA 1809 || top_to_mod 1810 #endif 1811 || idx >= wp->w_lines_valid 1812 || (row + wp->w_lines[idx].wl_size > bot_start) 1813 || (mod_top != 0 1814 && (lnum == mod_top 1815 || (lnum >= mod_top 1816 && (lnum < mod_bot 1817 #ifdef FEAT_SYN_HL 1818 || did_update == DID_FOLD 1819 || (did_update == DID_LINE 1820 && syntax_present(wp) 1821 && ( 1822 # ifdef FEAT_FOLDING 1823 (foldmethodIsSyntax(wp) 1824 && hasAnyFolding(wp)) || 1825 # endif 1826 syntax_check_changed(lnum))) 1827 #endif 1828 #ifdef FEAT_SEARCH_EXTRA 1829 /* match in fixed position might need redraw 1830 * if lines were inserted or deleted */ 1831 || (wp->w_match_head != NULL 1832 && buf->b_mod_xlines != 0) 1833 #endif 1834 ))))) 1835 { 1836 #ifdef FEAT_SEARCH_EXTRA 1837 if (lnum == mod_top) 1838 top_to_mod = FALSE; 1839 #endif 1840 1841 /* 1842 * When at start of changed lines: May scroll following lines 1843 * up or down to minimize redrawing. 1844 * Don't do this when the change continues until the end. 1845 * Don't scroll when dollar_vcol >= 0, keep the "$". 1846 */ 1847 if (lnum == mod_top 1848 && mod_bot != MAXLNUM 1849 && !(dollar_vcol >= 0 && mod_bot == mod_top + 1)) 1850 { 1851 int old_rows = 0; 1852 int new_rows = 0; 1853 int xtra_rows; 1854 linenr_T l; 1855 1856 /* Count the old number of window rows, using w_lines[], which 1857 * should still contain the sizes for the lines as they are 1858 * currently displayed. */ 1859 for (i = idx; i < wp->w_lines_valid; ++i) 1860 { 1861 /* Only valid lines have a meaningful wl_lnum. Invalid 1862 * lines are part of the changed area. */ 1863 if (wp->w_lines[i].wl_valid 1864 && wp->w_lines[i].wl_lnum == mod_bot) 1865 break; 1866 old_rows += wp->w_lines[i].wl_size; 1867 #ifdef FEAT_FOLDING 1868 if (wp->w_lines[i].wl_valid 1869 && wp->w_lines[i].wl_lastlnum + 1 == mod_bot) 1870 { 1871 /* Must have found the last valid entry above mod_bot. 1872 * Add following invalid entries. */ 1873 ++i; 1874 while (i < wp->w_lines_valid 1875 && !wp->w_lines[i].wl_valid) 1876 old_rows += wp->w_lines[i++].wl_size; 1877 break; 1878 } 1879 #endif 1880 } 1881 1882 if (i >= wp->w_lines_valid) 1883 { 1884 /* We can't find a valid line below the changed lines, 1885 * need to redraw until the end of the window. 1886 * Inserting/deleting lines has no use. */ 1887 bot_start = 0; 1888 } 1889 else 1890 { 1891 /* Able to count old number of rows: Count new window 1892 * rows, and may insert/delete lines */ 1893 j = idx; 1894 for (l = lnum; l < mod_bot; ++l) 1895 { 1896 #ifdef FEAT_FOLDING 1897 if (hasFoldingWin(wp, l, NULL, &l, TRUE, NULL)) 1898 ++new_rows; 1899 else 1900 #endif 1901 #ifdef FEAT_DIFF 1902 if (l == wp->w_topline) 1903 new_rows += plines_win_nofill(wp, l, TRUE) 1904 + wp->w_topfill; 1905 else 1906 #endif 1907 new_rows += plines_win(wp, l, TRUE); 1908 ++j; 1909 if (new_rows > wp->w_height - row - 2) 1910 { 1911 /* it's getting too much, must redraw the rest */ 1912 new_rows = 9999; 1913 break; 1914 } 1915 } 1916 xtra_rows = new_rows - old_rows; 1917 if (xtra_rows < 0) 1918 { 1919 /* May scroll text up. If there is not enough 1920 * remaining text or scrolling fails, must redraw the 1921 * rest. If scrolling works, must redraw the text 1922 * below the scrolled text. */ 1923 if (row - xtra_rows >= wp->w_height - 2) 1924 mod_bot = MAXLNUM; 1925 else 1926 { 1927 check_for_delay(FALSE); 1928 if (win_del_lines(wp, row, 1929 -xtra_rows, FALSE, FALSE, 0) == FAIL) 1930 mod_bot = MAXLNUM; 1931 else 1932 bot_start = wp->w_height + xtra_rows; 1933 } 1934 } 1935 else if (xtra_rows > 0) 1936 { 1937 /* May scroll text down. If there is not enough 1938 * remaining text of scrolling fails, must redraw the 1939 * rest. */ 1940 if (row + xtra_rows >= wp->w_height - 2) 1941 mod_bot = MAXLNUM; 1942 else 1943 { 1944 check_for_delay(FALSE); 1945 if (win_ins_lines(wp, row + old_rows, 1946 xtra_rows, FALSE, FALSE) == FAIL) 1947 mod_bot = MAXLNUM; 1948 else if (top_end > row + old_rows) 1949 /* Scrolled the part at the top that requires 1950 * updating down. */ 1951 top_end += xtra_rows; 1952 } 1953 } 1954 1955 /* When not updating the rest, may need to move w_lines[] 1956 * entries. */ 1957 if (mod_bot != MAXLNUM && i != j) 1958 { 1959 if (j < i) 1960 { 1961 int x = row + new_rows; 1962 1963 /* move entries in w_lines[] upwards */ 1964 for (;;) 1965 { 1966 /* stop at last valid entry in w_lines[] */ 1967 if (i >= wp->w_lines_valid) 1968 { 1969 wp->w_lines_valid = j; 1970 break; 1971 } 1972 wp->w_lines[j] = wp->w_lines[i]; 1973 /* stop at a line that won't fit */ 1974 if (x + (int)wp->w_lines[j].wl_size 1975 > wp->w_height) 1976 { 1977 wp->w_lines_valid = j + 1; 1978 break; 1979 } 1980 x += wp->w_lines[j++].wl_size; 1981 ++i; 1982 } 1983 if (bot_start > x) 1984 bot_start = x; 1985 } 1986 else /* j > i */ 1987 { 1988 /* move entries in w_lines[] downwards */ 1989 j -= i; 1990 wp->w_lines_valid += j; 1991 if (wp->w_lines_valid > wp->w_height) 1992 wp->w_lines_valid = wp->w_height; 1993 for (i = wp->w_lines_valid; i - j >= idx; --i) 1994 wp->w_lines[i] = wp->w_lines[i - j]; 1995 1996 /* The w_lines[] entries for inserted lines are 1997 * now invalid, but wl_size may be used above. 1998 * Reset to zero. */ 1999 while (i >= idx) 2000 { 2001 wp->w_lines[i].wl_size = 0; 2002 wp->w_lines[i--].wl_valid = FALSE; 2003 } 2004 } 2005 } 2006 } 2007 } 2008 2009 #ifdef FEAT_FOLDING 2010 /* 2011 * When lines are folded, display one line for all of them. 2012 * Otherwise, display normally (can be several display lines when 2013 * 'wrap' is on). 2014 */ 2015 fold_count = foldedCount(wp, lnum, &win_foldinfo); 2016 if (fold_count != 0) 2017 { 2018 fold_line(wp, fold_count, &win_foldinfo, lnum, row); 2019 ++row; 2020 --fold_count; 2021 wp->w_lines[idx].wl_folded = TRUE; 2022 wp->w_lines[idx].wl_lastlnum = lnum + fold_count; 2023 # ifdef FEAT_SYN_HL 2024 did_update = DID_FOLD; 2025 # endif 2026 } 2027 else 2028 #endif 2029 if (idx < wp->w_lines_valid 2030 && wp->w_lines[idx].wl_valid 2031 && wp->w_lines[idx].wl_lnum == lnum 2032 && lnum > wp->w_topline 2033 && !(dy_flags & (DY_LASTLINE | DY_TRUNCATE)) 2034 && srow + wp->w_lines[idx].wl_size > wp->w_height 2035 #ifdef FEAT_DIFF 2036 && diff_check_fill(wp, lnum) == 0 2037 #endif 2038 ) 2039 { 2040 /* This line is not going to fit. Don't draw anything here, 2041 * will draw "@ " lines below. */ 2042 row = wp->w_height + 1; 2043 } 2044 else 2045 { 2046 #ifdef FEAT_SEARCH_EXTRA 2047 prepare_search_hl(wp, lnum); 2048 #endif 2049 #ifdef FEAT_SYN_HL 2050 /* Let the syntax stuff know we skipped a few lines. */ 2051 if (syntax_last_parsed != 0 && syntax_last_parsed + 1 < lnum 2052 && syntax_present(wp)) 2053 syntax_end_parsing(syntax_last_parsed + 1); 2054 #endif 2055 2056 /* 2057 * Display one line. 2058 */ 2059 row = win_line(wp, lnum, srow, wp->w_height, 2060 mod_top == 0, FALSE); 2061 2062 #ifdef FEAT_FOLDING 2063 wp->w_lines[idx].wl_folded = FALSE; 2064 wp->w_lines[idx].wl_lastlnum = lnum; 2065 #endif 2066 #ifdef FEAT_SYN_HL 2067 did_update = DID_LINE; 2068 syntax_last_parsed = lnum; 2069 #endif 2070 } 2071 2072 wp->w_lines[idx].wl_lnum = lnum; 2073 wp->w_lines[idx].wl_valid = TRUE; 2074 2075 /* Past end of the window or end of the screen. Note that after 2076 * resizing wp->w_height may be end up too big. That's a problem 2077 * elsewhere, but prevent a crash here. */ 2078 if (row > wp->w_height || row + wp->w_winrow >= Rows) 2079 { 2080 /* we may need the size of that too long line later on */ 2081 if (dollar_vcol == -1) 2082 wp->w_lines[idx].wl_size = plines_win(wp, lnum, TRUE); 2083 ++idx; 2084 break; 2085 } 2086 if (dollar_vcol == -1) 2087 wp->w_lines[idx].wl_size = row - srow; 2088 ++idx; 2089 #ifdef FEAT_FOLDING 2090 lnum += fold_count + 1; 2091 #else 2092 ++lnum; 2093 #endif 2094 } 2095 else 2096 { 2097 if (wp->w_p_rnu) 2098 { 2099 #ifdef FEAT_FOLDING 2100 // 'relativenumber' set: The text doesn't need to be drawn, but 2101 // the number column nearly always does. 2102 fold_count = foldedCount(wp, lnum, &win_foldinfo); 2103 if (fold_count != 0) 2104 fold_line(wp, fold_count, &win_foldinfo, lnum, row); 2105 else 2106 #endif 2107 (void)win_line(wp, lnum, srow, wp->w_height, TRUE, TRUE); 2108 } 2109 2110 // This line does not need to be drawn, advance to the next one. 2111 row += wp->w_lines[idx++].wl_size; 2112 if (row > wp->w_height) /* past end of screen */ 2113 break; 2114 #ifdef FEAT_FOLDING 2115 lnum = wp->w_lines[idx - 1].wl_lastlnum + 1; 2116 #else 2117 ++lnum; 2118 #endif 2119 #ifdef FEAT_SYN_HL 2120 did_update = DID_NONE; 2121 #endif 2122 } 2123 2124 if (lnum > buf->b_ml.ml_line_count) 2125 { 2126 eof = TRUE; 2127 break; 2128 } 2129 } 2130 /* 2131 * End of loop over all window lines. 2132 */ 2133 2134 #ifdef FEAT_VTP 2135 /* Rewrite the character at the end of the screen line. */ 2136 if (use_vtp()) 2137 { 2138 int i; 2139 2140 for (i = 0; i < Rows; ++i) 2141 if (enc_utf8) 2142 if ((*mb_off2cells)(LineOffset[i] + Columns - 2, 2143 LineOffset[i] + screen_Columns) > 1) 2144 screen_draw_rectangle(i, Columns - 2, 1, 2, FALSE); 2145 else 2146 screen_draw_rectangle(i, Columns - 1, 1, 1, FALSE); 2147 else 2148 screen_char(LineOffset[i] + Columns - 1, i, Columns - 1); 2149 } 2150 #endif 2151 2152 if (idx > wp->w_lines_valid) 2153 wp->w_lines_valid = idx; 2154 2155 #ifdef FEAT_SYN_HL 2156 /* 2157 * Let the syntax stuff know we stop parsing here. 2158 */ 2159 if (syntax_last_parsed != 0 && syntax_present(wp)) 2160 syntax_end_parsing(syntax_last_parsed + 1); 2161 #endif 2162 2163 /* 2164 * If we didn't hit the end of the file, and we didn't finish the last 2165 * line we were working on, then the line didn't fit. 2166 */ 2167 wp->w_empty_rows = 0; 2168 #ifdef FEAT_DIFF 2169 wp->w_filler_rows = 0; 2170 #endif 2171 if (!eof && !didline) 2172 { 2173 if (lnum == wp->w_topline) 2174 { 2175 /* 2176 * Single line that does not fit! 2177 * Don't overwrite it, it can be edited. 2178 */ 2179 wp->w_botline = lnum + 1; 2180 } 2181 #ifdef FEAT_DIFF 2182 else if (diff_check_fill(wp, lnum) >= wp->w_height - srow) 2183 { 2184 /* Window ends in filler lines. */ 2185 wp->w_botline = lnum; 2186 wp->w_filler_rows = wp->w_height - srow; 2187 } 2188 #endif 2189 else if (dy_flags & DY_TRUNCATE) /* 'display' has "truncate" */ 2190 { 2191 int scr_row = W_WINROW(wp) + wp->w_height - 1; 2192 2193 /* 2194 * Last line isn't finished: Display "@@@" in the last screen line. 2195 */ 2196 screen_puts_len((char_u *)"@@", 2, scr_row, wp->w_wincol, 2197 HL_ATTR(HLF_AT)); 2198 screen_fill(scr_row, scr_row + 1, 2199 (int)wp->w_wincol + 2, (int)W_ENDCOL(wp), 2200 '@', ' ', HL_ATTR(HLF_AT)); 2201 set_empty_rows(wp, srow); 2202 wp->w_botline = lnum; 2203 } 2204 else if (dy_flags & DY_LASTLINE) /* 'display' has "lastline" */ 2205 { 2206 /* 2207 * Last line isn't finished: Display "@@@" at the end. 2208 */ 2209 screen_fill(W_WINROW(wp) + wp->w_height - 1, 2210 W_WINROW(wp) + wp->w_height, 2211 (int)W_ENDCOL(wp) - 3, (int)W_ENDCOL(wp), 2212 '@', '@', HL_ATTR(HLF_AT)); 2213 set_empty_rows(wp, srow); 2214 wp->w_botline = lnum; 2215 } 2216 else 2217 { 2218 win_draw_end(wp, '@', ' ', TRUE, srow, wp->w_height, HLF_AT); 2219 wp->w_botline = lnum; 2220 } 2221 } 2222 else 2223 { 2224 draw_vsep_win(wp, row); 2225 if (eof) /* we hit the end of the file */ 2226 { 2227 wp->w_botline = buf->b_ml.ml_line_count + 1; 2228 #ifdef FEAT_DIFF 2229 j = diff_check_fill(wp, wp->w_botline); 2230 if (j > 0 && !wp->w_botfill) 2231 { 2232 // Display filler lines at the end of the file. 2233 if (char2cells(fill_diff) > 1) 2234 i = '-'; 2235 else 2236 i = fill_diff; 2237 if (row + j > wp->w_height) 2238 j = wp->w_height - row; 2239 win_draw_end(wp, i, i, TRUE, row, row + (int)j, HLF_DED); 2240 row += j; 2241 } 2242 #endif 2243 } 2244 else if (dollar_vcol == -1) 2245 wp->w_botline = lnum; 2246 2247 // Make sure the rest of the screen is blank 2248 // put '~'s on rows that aren't part of the file. 2249 win_draw_end(wp, '~', ' ', FALSE, row, wp->w_height, HLF_EOB); 2250 } 2251 2252 #ifdef SYN_TIME_LIMIT 2253 syn_set_timeout(NULL); 2254 #endif 2255 2256 /* Reset the type of redrawing required, the window has been updated. */ 2257 wp->w_redr_type = 0; 2258 #ifdef FEAT_DIFF 2259 wp->w_old_topfill = wp->w_topfill; 2260 wp->w_old_botfill = wp->w_botfill; 2261 #endif 2262 2263 if (dollar_vcol == -1) 2264 { 2265 /* 2266 * There is a trick with w_botline. If we invalidate it on each 2267 * change that might modify it, this will cause a lot of expensive 2268 * calls to plines() in update_topline() each time. Therefore the 2269 * value of w_botline is often approximated, and this value is used to 2270 * compute the value of w_topline. If the value of w_botline was 2271 * wrong, check that the value of w_topline is correct (cursor is on 2272 * the visible part of the text). If it's not, we need to redraw 2273 * again. Mostly this just means scrolling up a few lines, so it 2274 * doesn't look too bad. Only do this for the current window (where 2275 * changes are relevant). 2276 */ 2277 wp->w_valid |= VALID_BOTLINE; 2278 if (wp == curwin && wp->w_botline != old_botline && !recursive) 2279 { 2280 recursive = TRUE; 2281 curwin->w_valid &= ~VALID_TOPLINE; 2282 update_topline(); /* may invalidate w_botline again */ 2283 if (must_redraw != 0) 2284 { 2285 /* Don't update for changes in buffer again. */ 2286 i = curbuf->b_mod_set; 2287 curbuf->b_mod_set = FALSE; 2288 win_update(curwin); 2289 must_redraw = 0; 2290 curbuf->b_mod_set = i; 2291 } 2292 recursive = FALSE; 2293 } 2294 } 2295 2296 #if defined(FEAT_SYN_HL) || defined(FEAT_SEARCH_EXTRA) 2297 /* restore got_int, unless CTRL-C was hit while redrawing */ 2298 if (!got_int) 2299 got_int = save_got_int; 2300 #endif 2301 } 2302 2303 /* 2304 * Call screen_fill() with the columns adjusted for 'rightleft' if needed. 2305 * Return the new offset. 2306 */ 2307 static int 2308 screen_fill_end( 2309 win_T *wp, 2310 int c1, 2311 int c2, 2312 int off, 2313 int width, 2314 int row, 2315 int endrow, 2316 int attr) 2317 { 2318 int nn = off + width; 2319 2320 if (nn > wp->w_width) 2321 nn = wp->w_width; 2322 #ifdef FEAT_RIGHTLEFT 2323 if (wp->w_p_rl) 2324 { 2325 screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow, 2326 W_ENDCOL(wp) - nn, (int)W_ENDCOL(wp) - off, 2327 c1, c2, attr); 2328 } 2329 else 2330 #endif 2331 screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow, 2332 wp->w_wincol + off, (int)wp->w_wincol + nn, 2333 c1, c2, attr); 2334 return nn; 2335 } 2336 2337 /* 2338 * Clear lines near the end the window and mark the unused lines with "c1". 2339 * use "c2" as the filler character. 2340 * When "draw_margin" is TRUE then draw the sign, fold and number columns. 2341 */ 2342 static void 2343 win_draw_end( 2344 win_T *wp, 2345 int c1, 2346 int c2, 2347 int draw_margin, 2348 int row, 2349 int endrow, 2350 hlf_T hl) 2351 { 2352 int n = 0; 2353 2354 if (draw_margin) 2355 { 2356 #ifdef FEAT_FOLDING 2357 int fdc = compute_foldcolumn(wp, 0); 2358 2359 if (fdc > 0) 2360 // draw the fold column 2361 n = screen_fill_end(wp, ' ', ' ', n, fdc, 2362 row, endrow, HL_ATTR(HLF_FC)); 2363 #endif 2364 #ifdef FEAT_SIGNS 2365 if (signcolumn_on(wp)) 2366 // draw the sign column 2367 n = screen_fill_end(wp, ' ', ' ', n, 2, 2368 row, endrow, HL_ATTR(HLF_SC)); 2369 #endif 2370 if ((wp->w_p_nu || wp->w_p_rnu) 2371 && vim_strchr(p_cpo, CPO_NUMCOL) == NULL) 2372 // draw the number column 2373 n = screen_fill_end(wp, ' ', ' ', n, number_width(wp) + 1, 2374 row, endrow, HL_ATTR(HLF_N)); 2375 } 2376 2377 #ifdef FEAT_RIGHTLEFT 2378 if (wp->w_p_rl) 2379 { 2380 screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow, 2381 wp->w_wincol, W_ENDCOL(wp) - 1 - n, 2382 c2, c2, HL_ATTR(hl)); 2383 screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow, 2384 W_ENDCOL(wp) - 1 - n, W_ENDCOL(wp) - n, 2385 c1, c2, HL_ATTR(hl)); 2386 } 2387 else 2388 #endif 2389 { 2390 screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow, 2391 wp->w_wincol + n, (int)W_ENDCOL(wp), 2392 c1, c2, HL_ATTR(hl)); 2393 } 2394 2395 set_empty_rows(wp, row); 2396 } 2397 2398 #ifdef FEAT_SYN_HL 2399 /* 2400 * Advance **color_cols and return TRUE when there are columns to draw. 2401 */ 2402 static int 2403 advance_color_col(int vcol, int **color_cols) 2404 { 2405 while (**color_cols >= 0 && vcol > **color_cols) 2406 ++*color_cols; 2407 return (**color_cols >= 0); 2408 } 2409 #endif 2410 2411 #if defined(FEAT_MENU) || defined(FEAT_FOLDING) 2412 /* 2413 * Copy "text" to ScreenLines using "attr". 2414 * Returns the next screen column. 2415 */ 2416 static int 2417 text_to_screenline(win_T *wp, char_u *text, int col) 2418 { 2419 int off = (int)(current_ScreenLine - ScreenLines); 2420 2421 if (has_mbyte) 2422 { 2423 int cells; 2424 int u8c, u8cc[MAX_MCO]; 2425 int i; 2426 int idx; 2427 int c_len; 2428 char_u *p; 2429 # ifdef FEAT_ARABIC 2430 int prev_c = 0; /* previous Arabic character */ 2431 int prev_c1 = 0; /* first composing char for prev_c */ 2432 # endif 2433 2434 # ifdef FEAT_RIGHTLEFT 2435 if (wp->w_p_rl) 2436 idx = off; 2437 else 2438 # endif 2439 idx = off + col; 2440 2441 /* Store multibyte characters in ScreenLines[] et al. correctly. */ 2442 for (p = text; *p != NUL; ) 2443 { 2444 cells = (*mb_ptr2cells)(p); 2445 c_len = (*mb_ptr2len)(p); 2446 if (col + cells > wp->w_width 2447 # ifdef FEAT_RIGHTLEFT 2448 - (wp->w_p_rl ? col : 0) 2449 # endif 2450 ) 2451 break; 2452 ScreenLines[idx] = *p; 2453 if (enc_utf8) 2454 { 2455 u8c = utfc_ptr2char(p, u8cc); 2456 if (*p < 0x80 && u8cc[0] == 0) 2457 { 2458 ScreenLinesUC[idx] = 0; 2459 #ifdef FEAT_ARABIC 2460 prev_c = u8c; 2461 #endif 2462 } 2463 else 2464 { 2465 #ifdef FEAT_ARABIC 2466 if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) 2467 { 2468 /* Do Arabic shaping. */ 2469 int pc, pc1, nc; 2470 int pcc[MAX_MCO]; 2471 int firstbyte = *p; 2472 2473 /* The idea of what is the previous and next 2474 * character depends on 'rightleft'. */ 2475 if (wp->w_p_rl) 2476 { 2477 pc = prev_c; 2478 pc1 = prev_c1; 2479 nc = utf_ptr2char(p + c_len); 2480 prev_c1 = u8cc[0]; 2481 } 2482 else 2483 { 2484 pc = utfc_ptr2char(p + c_len, pcc); 2485 nc = prev_c; 2486 pc1 = pcc[0]; 2487 } 2488 prev_c = u8c; 2489 2490 u8c = arabic_shape(u8c, &firstbyte, &u8cc[0], 2491 pc, pc1, nc); 2492 ScreenLines[idx] = firstbyte; 2493 } 2494 else 2495 prev_c = u8c; 2496 #endif 2497 /* Non-BMP character: display as ? or fullwidth ?. */ 2498 ScreenLinesUC[idx] = u8c; 2499 for (i = 0; i < Screen_mco; ++i) 2500 { 2501 ScreenLinesC[i][idx] = u8cc[i]; 2502 if (u8cc[i] == 0) 2503 break; 2504 } 2505 } 2506 if (cells > 1) 2507 ScreenLines[idx + 1] = 0; 2508 } 2509 else if (enc_dbcs == DBCS_JPNU && *p == 0x8e) 2510 /* double-byte single width character */ 2511 ScreenLines2[idx] = p[1]; 2512 else if (cells > 1) 2513 /* double-width character */ 2514 ScreenLines[idx + 1] = p[1]; 2515 col += cells; 2516 idx += cells; 2517 p += c_len; 2518 } 2519 } 2520 else 2521 { 2522 int len = (int)STRLEN(text); 2523 2524 if (len > wp->w_width - col) 2525 len = wp->w_width - col; 2526 if (len > 0) 2527 { 2528 #ifdef FEAT_RIGHTLEFT 2529 if (wp->w_p_rl) 2530 mch_memmove(current_ScreenLine, text, len); 2531 else 2532 #endif 2533 mch_memmove(current_ScreenLine + col, text, len); 2534 col += len; 2535 } 2536 } 2537 return col; 2538 } 2539 #endif 2540 2541 #ifdef FEAT_FOLDING 2542 /* 2543 * Compute the width of the foldcolumn. Based on 'foldcolumn' and how much 2544 * space is available for window "wp", minus "col". 2545 */ 2546 static int 2547 compute_foldcolumn(win_T *wp, int col) 2548 { 2549 int fdc = wp->w_p_fdc; 2550 int wmw = wp == curwin && p_wmw == 0 ? 1 : p_wmw; 2551 int wwidth = wp->w_width; 2552 2553 if (fdc > wwidth - (col + wmw)) 2554 fdc = wwidth - (col + wmw); 2555 return fdc; 2556 } 2557 2558 /* 2559 * Display one folded line. 2560 */ 2561 static void 2562 fold_line( 2563 win_T *wp, 2564 long fold_count, 2565 foldinfo_T *foldinfo, 2566 linenr_T lnum, 2567 int row) 2568 { 2569 char_u buf[FOLD_TEXT_LEN]; 2570 pos_T *top, *bot; 2571 linenr_T lnume = lnum + fold_count - 1; 2572 int len; 2573 char_u *text; 2574 int fdc; 2575 int col; 2576 int txtcol; 2577 int off = (int)(current_ScreenLine - ScreenLines); 2578 int ri; 2579 2580 /* Build the fold line: 2581 * 1. Add the cmdwin_type for the command-line window 2582 * 2. Add the 'foldcolumn' 2583 * 3. Add the 'number' or 'relativenumber' column 2584 * 4. Compose the text 2585 * 5. Add the text 2586 * 6. set highlighting for the Visual area an other text 2587 */ 2588 col = 0; 2589 2590 /* 2591 * 1. Add the cmdwin_type for the command-line window 2592 * Ignores 'rightleft', this window is never right-left. 2593 */ 2594 #ifdef FEAT_CMDWIN 2595 if (cmdwin_type != 0 && wp == curwin) 2596 { 2597 ScreenLines[off] = cmdwin_type; 2598 ScreenAttrs[off] = HL_ATTR(HLF_AT); 2599 if (enc_utf8) 2600 ScreenLinesUC[off] = 0; 2601 ++col; 2602 } 2603 #endif 2604 2605 /* 2606 * 2. Add the 'foldcolumn' 2607 * Reduce the width when there is not enough space. 2608 */ 2609 fdc = compute_foldcolumn(wp, col); 2610 if (fdc > 0) 2611 { 2612 fill_foldcolumn(buf, wp, TRUE, lnum); 2613 #ifdef FEAT_RIGHTLEFT 2614 if (wp->w_p_rl) 2615 { 2616 int i; 2617 2618 copy_text_attr(off + wp->w_width - fdc - col, buf, fdc, 2619 HL_ATTR(HLF_FC)); 2620 /* reverse the fold column */ 2621 for (i = 0; i < fdc; ++i) 2622 ScreenLines[off + wp->w_width - i - 1 - col] = buf[i]; 2623 } 2624 else 2625 #endif 2626 copy_text_attr(off + col, buf, fdc, HL_ATTR(HLF_FC)); 2627 col += fdc; 2628 } 2629 2630 #ifdef FEAT_RIGHTLEFT 2631 # define RL_MEMSET(p, v, l) \ 2632 do { \ 2633 if (wp->w_p_rl) \ 2634 for (ri = 0; ri < l; ++ri) \ 2635 ScreenAttrs[off + (wp->w_width - (p) - (l)) + ri] = v; \ 2636 else \ 2637 for (ri = 0; ri < l; ++ri) \ 2638 ScreenAttrs[off + (p) + ri] = v; \ 2639 } while (0) 2640 #else 2641 # define RL_MEMSET(p, v, l) \ 2642 do { \ 2643 for (ri = 0; ri < l; ++ri) \ 2644 ScreenAttrs[off + (p) + ri] = v; \ 2645 } while (0) 2646 #endif 2647 2648 /* Set all attributes of the 'number' or 'relativenumber' column and the 2649 * text */ 2650 RL_MEMSET(col, HL_ATTR(HLF_FL), wp->w_width - col); 2651 2652 #ifdef FEAT_SIGNS 2653 /* If signs are being displayed, add two spaces. */ 2654 if (signcolumn_on(wp)) 2655 { 2656 len = wp->w_width - col; 2657 if (len > 0) 2658 { 2659 if (len > 2) 2660 len = 2; 2661 # ifdef FEAT_RIGHTLEFT 2662 if (wp->w_p_rl) 2663 /* the line number isn't reversed */ 2664 copy_text_attr(off + wp->w_width - len - col, 2665 (char_u *)" ", len, HL_ATTR(HLF_FL)); 2666 else 2667 # endif 2668 copy_text_attr(off + col, (char_u *)" ", len, HL_ATTR(HLF_FL)); 2669 col += len; 2670 } 2671 } 2672 #endif 2673 2674 /* 2675 * 3. Add the 'number' or 'relativenumber' column 2676 */ 2677 if (wp->w_p_nu || wp->w_p_rnu) 2678 { 2679 len = wp->w_width - col; 2680 if (len > 0) 2681 { 2682 int w = number_width(wp); 2683 long num; 2684 char *fmt = "%*ld "; 2685 2686 if (len > w + 1) 2687 len = w + 1; 2688 2689 if (wp->w_p_nu && !wp->w_p_rnu) 2690 /* 'number' + 'norelativenumber' */ 2691 num = (long)lnum; 2692 else 2693 { 2694 /* 'relativenumber', don't use negative numbers */ 2695 num = labs((long)get_cursor_rel_lnum(wp, lnum)); 2696 if (num == 0 && wp->w_p_nu && wp->w_p_rnu) 2697 { 2698 /* 'number' + 'relativenumber': cursor line shows absolute 2699 * line number */ 2700 num = lnum; 2701 fmt = "%-*ld "; 2702 } 2703 } 2704 2705 sprintf((char *)buf, fmt, w, num); 2706 #ifdef FEAT_RIGHTLEFT 2707 if (wp->w_p_rl) 2708 /* the line number isn't reversed */ 2709 copy_text_attr(off + wp->w_width - len - col, buf, len, 2710 HL_ATTR(HLF_FL)); 2711 else 2712 #endif 2713 copy_text_attr(off + col, buf, len, HL_ATTR(HLF_FL)); 2714 col += len; 2715 } 2716 } 2717 2718 /* 2719 * 4. Compose the folded-line string with 'foldtext', if set. 2720 */ 2721 text = get_foldtext(wp, lnum, lnume, foldinfo, buf); 2722 2723 txtcol = col; /* remember where text starts */ 2724 2725 /* 2726 * 5. move the text to current_ScreenLine. Fill up with "fill_fold". 2727 * Right-left text is put in columns 0 - number-col, normal text is put 2728 * in columns number-col - window-width. 2729 */ 2730 col = text_to_screenline(wp, text, col); 2731 2732 /* Fill the rest of the line with the fold filler */ 2733 #ifdef FEAT_RIGHTLEFT 2734 if (wp->w_p_rl) 2735 col -= txtcol; 2736 #endif 2737 while (col < wp->w_width 2738 #ifdef FEAT_RIGHTLEFT 2739 - (wp->w_p_rl ? txtcol : 0) 2740 #endif 2741 ) 2742 { 2743 if (enc_utf8) 2744 { 2745 if (fill_fold >= 0x80) 2746 { 2747 ScreenLinesUC[off + col] = fill_fold; 2748 ScreenLinesC[0][off + col] = 0; 2749 ScreenLines[off + col] = 0x80; /* avoid storing zero */ 2750 } 2751 else 2752 { 2753 ScreenLinesUC[off + col] = 0; 2754 ScreenLines[off + col] = fill_fold; 2755 } 2756 col++; 2757 } 2758 else 2759 ScreenLines[off + col++] = fill_fold; 2760 } 2761 2762 if (text != buf) 2763 vim_free(text); 2764 2765 /* 2766 * 6. set highlighting for the Visual area an other text. 2767 * If all folded lines are in the Visual area, highlight the line. 2768 */ 2769 if (VIsual_active && wp->w_buffer == curwin->w_buffer) 2770 { 2771 if (LTOREQ_POS(curwin->w_cursor, VIsual)) 2772 { 2773 /* Visual is after curwin->w_cursor */ 2774 top = &curwin->w_cursor; 2775 bot = &VIsual; 2776 } 2777 else 2778 { 2779 /* Visual is before curwin->w_cursor */ 2780 top = &VIsual; 2781 bot = &curwin->w_cursor; 2782 } 2783 if (lnum >= top->lnum 2784 && lnume <= bot->lnum 2785 && (VIsual_mode != 'v' 2786 || ((lnum > top->lnum 2787 || (lnum == top->lnum 2788 && top->col == 0)) 2789 && (lnume < bot->lnum 2790 || (lnume == bot->lnum 2791 && (bot->col - (*p_sel == 'e')) 2792 >= (colnr_T)STRLEN(ml_get_buf(wp->w_buffer, lnume, FALSE))))))) 2793 { 2794 if (VIsual_mode == Ctrl_V) 2795 { 2796 /* Visual block mode: highlight the chars part of the block */ 2797 if (wp->w_old_cursor_fcol + txtcol < (colnr_T)wp->w_width) 2798 { 2799 if (wp->w_old_cursor_lcol != MAXCOL 2800 && wp->w_old_cursor_lcol + txtcol 2801 < (colnr_T)wp->w_width) 2802 len = wp->w_old_cursor_lcol; 2803 else 2804 len = wp->w_width - txtcol; 2805 RL_MEMSET(wp->w_old_cursor_fcol + txtcol, HL_ATTR(HLF_V), 2806 len - (int)wp->w_old_cursor_fcol); 2807 } 2808 } 2809 else 2810 { 2811 /* Set all attributes of the text */ 2812 RL_MEMSET(txtcol, HL_ATTR(HLF_V), wp->w_width - txtcol); 2813 } 2814 } 2815 } 2816 2817 #ifdef FEAT_SYN_HL 2818 /* Show colorcolumn in the fold line, but let cursorcolumn override it. */ 2819 if (wp->w_p_cc_cols) 2820 { 2821 int i = 0; 2822 int j = wp->w_p_cc_cols[i]; 2823 int old_txtcol = txtcol; 2824 2825 while (j > -1) 2826 { 2827 txtcol += j; 2828 if (wp->w_p_wrap) 2829 txtcol -= wp->w_skipcol; 2830 else 2831 txtcol -= wp->w_leftcol; 2832 if (txtcol >= 0 && txtcol < wp->w_width) 2833 ScreenAttrs[off + txtcol] = hl_combine_attr( 2834 ScreenAttrs[off + txtcol], HL_ATTR(HLF_MC)); 2835 txtcol = old_txtcol; 2836 j = wp->w_p_cc_cols[++i]; 2837 } 2838 } 2839 2840 /* Show 'cursorcolumn' in the fold line. */ 2841 if (wp->w_p_cuc) 2842 { 2843 txtcol += wp->w_virtcol; 2844 if (wp->w_p_wrap) 2845 txtcol -= wp->w_skipcol; 2846 else 2847 txtcol -= wp->w_leftcol; 2848 if (txtcol >= 0 && txtcol < wp->w_width) 2849 ScreenAttrs[off + txtcol] = hl_combine_attr( 2850 ScreenAttrs[off + txtcol], HL_ATTR(HLF_CUC)); 2851 } 2852 #endif 2853 2854 screen_line(row + W_WINROW(wp), wp->w_wincol, (int)wp->w_width, 2855 (int)wp->w_width, FALSE); 2856 2857 /* 2858 * Update w_cline_height and w_cline_folded if the cursor line was 2859 * updated (saves a call to plines() later). 2860 */ 2861 if (wp == curwin 2862 && lnum <= curwin->w_cursor.lnum 2863 && lnume >= curwin->w_cursor.lnum) 2864 { 2865 curwin->w_cline_row = row; 2866 curwin->w_cline_height = 1; 2867 curwin->w_cline_folded = TRUE; 2868 curwin->w_valid |= (VALID_CHEIGHT|VALID_CROW); 2869 } 2870 } 2871 2872 /* 2873 * Copy "buf[len]" to ScreenLines["off"] and set attributes to "attr". 2874 */ 2875 static void 2876 copy_text_attr( 2877 int off, 2878 char_u *buf, 2879 int len, 2880 int attr) 2881 { 2882 int i; 2883 2884 mch_memmove(ScreenLines + off, buf, (size_t)len); 2885 if (enc_utf8) 2886 vim_memset(ScreenLinesUC + off, 0, sizeof(u8char_T) * (size_t)len); 2887 for (i = 0; i < len; ++i) 2888 ScreenAttrs[off + i] = attr; 2889 } 2890 2891 /* 2892 * Fill the foldcolumn at "p" for window "wp". 2893 * Only to be called when 'foldcolumn' > 0. 2894 */ 2895 static void 2896 fill_foldcolumn( 2897 char_u *p, 2898 win_T *wp, 2899 int closed, /* TRUE of FALSE */ 2900 linenr_T lnum) /* current line number */ 2901 { 2902 int i = 0; 2903 int level; 2904 int first_level; 2905 int empty; 2906 int fdc = compute_foldcolumn(wp, 0); 2907 2908 /* Init to all spaces. */ 2909 vim_memset(p, ' ', (size_t)fdc); 2910 2911 level = win_foldinfo.fi_level; 2912 if (level > 0) 2913 { 2914 /* If there is only one column put more info in it. */ 2915 empty = (fdc == 1) ? 0 : 1; 2916 2917 /* If the column is too narrow, we start at the lowest level that 2918 * fits and use numbers to indicated the depth. */ 2919 first_level = level - fdc - closed + 1 + empty; 2920 if (first_level < 1) 2921 first_level = 1; 2922 2923 for (i = 0; i + empty < fdc; ++i) 2924 { 2925 if (win_foldinfo.fi_lnum == lnum 2926 && first_level + i >= win_foldinfo.fi_low_level) 2927 p[i] = '-'; 2928 else if (first_level == 1) 2929 p[i] = '|'; 2930 else if (first_level + i <= 9) 2931 p[i] = '0' + first_level + i; 2932 else 2933 p[i] = '>'; 2934 if (first_level + i == level) 2935 break; 2936 } 2937 } 2938 if (closed) 2939 p[i >= fdc ? i - 1 : i] = '+'; 2940 } 2941 #endif /* FEAT_FOLDING */ 2942 2943 #ifdef FEAT_TEXT_PROP 2944 static textprop_T *current_text_props = NULL; 2945 static buf_T *current_buf = NULL; 2946 2947 static int 2948 text_prop_compare(const void *s1, const void *s2) 2949 { 2950 int idx1, idx2; 2951 proptype_T *pt1, *pt2; 2952 colnr_T col1, col2; 2953 2954 idx1 = *(int *)s1; 2955 idx2 = *(int *)s2; 2956 pt1 = text_prop_type_by_id(current_buf, current_text_props[idx1].tp_type); 2957 pt2 = text_prop_type_by_id(current_buf, current_text_props[idx2].tp_type); 2958 if (pt1 == pt2) 2959 return 0; 2960 if (pt1 == NULL) 2961 return -1; 2962 if (pt2 == NULL) 2963 return 1; 2964 if (pt1->pt_priority != pt2->pt_priority) 2965 return pt1->pt_priority > pt2->pt_priority ? 1 : -1; 2966 col1 = current_text_props[idx1].tp_col; 2967 col2 = current_text_props[idx2].tp_col; 2968 return col1 == col2 ? 0 : col1 > col2 ? 1 : -1; 2969 } 2970 #endif 2971 2972 /* 2973 * Display line "lnum" of window 'wp' on the screen. 2974 * Start at row "startrow", stop when "endrow" is reached. 2975 * wp->w_virtcol needs to be valid. 2976 * 2977 * Return the number of last row the line occupies. 2978 */ 2979 static int 2980 win_line( 2981 win_T *wp, 2982 linenr_T lnum, 2983 int startrow, 2984 int endrow, 2985 int nochange UNUSED, // not updating for changed text 2986 int number_only) // only update the number column 2987 { 2988 int col = 0; /* visual column on screen */ 2989 unsigned off; /* offset in ScreenLines/ScreenAttrs */ 2990 int c = 0; /* init for GCC */ 2991 long vcol = 0; /* virtual column (for tabs) */ 2992 #ifdef FEAT_LINEBREAK 2993 long vcol_sbr = -1; /* virtual column after showbreak */ 2994 #endif 2995 long vcol_prev = -1; /* "vcol" of previous character */ 2996 char_u *line; /* current line */ 2997 char_u *ptr; /* current position in "line" */ 2998 int row; /* row in the window, excl w_winrow */ 2999 int screen_row; /* row on the screen, incl w_winrow */ 3000 3001 char_u extra[18]; /* "%ld" and 'fdc' must fit in here */ 3002 int n_extra = 0; /* number of extra chars */ 3003 char_u *p_extra = NULL; /* string of extra chars, plus NUL */ 3004 char_u *p_extra_free = NULL; /* p_extra needs to be freed */ 3005 int c_extra = NUL; /* extra chars, all the same */ 3006 int c_final = NUL; /* final char, mandatory if set */ 3007 int extra_attr = 0; /* attributes when n_extra != 0 */ 3008 static char_u *at_end_str = (char_u *)""; /* used for p_extra when 3009 displaying lcs_eol at end-of-line */ 3010 int lcs_eol_one = lcs_eol; /* lcs_eol until it's been used */ 3011 int lcs_prec_todo = lcs_prec; /* lcs_prec until it's been used */ 3012 3013 /* saved "extra" items for when draw_state becomes WL_LINE (again) */ 3014 int saved_n_extra = 0; 3015 char_u *saved_p_extra = NULL; 3016 int saved_c_extra = 0; 3017 int saved_c_final = 0; 3018 int saved_char_attr = 0; 3019 3020 int n_attr = 0; /* chars with special attr */ 3021 int saved_attr2 = 0; /* char_attr saved for n_attr */ 3022 int n_attr3 = 0; /* chars with overruling special attr */ 3023 int saved_attr3 = 0; /* char_attr saved for n_attr3 */ 3024 3025 int n_skip = 0; /* nr of chars to skip for 'nowrap' */ 3026 3027 int fromcol, tocol; /* start/end of inverting */ 3028 int fromcol_prev = -2; /* start of inverting after cursor */ 3029 int noinvcur = FALSE; /* don't invert the cursor */ 3030 pos_T *top, *bot; 3031 int lnum_in_visual_area = FALSE; 3032 pos_T pos; 3033 long v; 3034 3035 int char_attr = 0; /* attributes for next character */ 3036 int attr_pri = FALSE; /* char_attr has priority */ 3037 int area_highlighting = FALSE; /* Visual or incsearch highlighting 3038 in this line */ 3039 int attr = 0; /* attributes for area highlighting */ 3040 int area_attr = 0; /* attributes desired by highlighting */ 3041 int search_attr = 0; /* attributes desired by 'hlsearch' */ 3042 #ifdef FEAT_SYN_HL 3043 int vcol_save_attr = 0; /* saved attr for 'cursorcolumn' */ 3044 int syntax_attr = 0; /* attributes desired by syntax */ 3045 int has_syntax = FALSE; /* this buffer has syntax highl. */ 3046 int save_did_emsg; 3047 int eol_hl_off = 0; /* 1 if highlighted char after EOL */ 3048 int draw_color_col = FALSE; /* highlight colorcolumn */ 3049 int *color_cols = NULL; /* pointer to according columns array */ 3050 #endif 3051 #ifdef FEAT_TEXT_PROP 3052 int text_prop_count; 3053 int text_prop_next = 0; // next text property to use 3054 textprop_T *text_props = NULL; 3055 int *text_prop_idxs = NULL; 3056 int text_props_active = 0; 3057 proptype_T *text_prop_type = NULL; 3058 int text_prop_attr = 0; 3059 int text_prop_combine = FALSE; 3060 #endif 3061 #ifdef FEAT_SPELL 3062 int has_spell = FALSE; /* this buffer has spell checking */ 3063 # define SPWORDLEN 150 3064 char_u nextline[SPWORDLEN * 2];/* text with start of the next line */ 3065 int nextlinecol = 0; /* column where nextline[] starts */ 3066 int nextline_idx = 0; /* index in nextline[] where next line 3067 starts */ 3068 int spell_attr = 0; /* attributes desired by spelling */ 3069 int word_end = 0; /* last byte with same spell_attr */ 3070 static linenr_T checked_lnum = 0; /* line number for "checked_col" */ 3071 static int checked_col = 0; /* column in "checked_lnum" up to which 3072 * there are no spell errors */ 3073 static int cap_col = -1; /* column to check for Cap word */ 3074 static linenr_T capcol_lnum = 0; /* line number where "cap_col" used */ 3075 int cur_checked_col = 0; /* checked column for current line */ 3076 #endif 3077 int extra_check = 0; // has extra highlighting 3078 int multi_attr = 0; /* attributes desired by multibyte */ 3079 int mb_l = 1; /* multi-byte byte length */ 3080 int mb_c = 0; /* decoded multi-byte character */ 3081 int mb_utf8 = FALSE; /* screen char is UTF-8 char */ 3082 int u8cc[MAX_MCO]; /* composing UTF-8 chars */ 3083 #ifdef FEAT_DIFF 3084 int filler_lines; /* nr of filler lines to be drawn */ 3085 int filler_todo; /* nr of filler lines still to do + 1 */ 3086 hlf_T diff_hlf = (hlf_T)0; /* type of diff highlighting */ 3087 int change_start = MAXCOL; /* first col of changed area */ 3088 int change_end = -1; /* last col of changed area */ 3089 #endif 3090 colnr_T trailcol = MAXCOL; /* start of trailing spaces */ 3091 #ifdef FEAT_LINEBREAK 3092 int need_showbreak = FALSE; /* overlong line, skipping first x 3093 chars */ 3094 #endif 3095 #if defined(FEAT_SIGNS) || defined(FEAT_QUICKFIX) \ 3096 || defined(FEAT_SYN_HL) || defined(FEAT_DIFF) 3097 # define LINE_ATTR 3098 int line_attr = 0; /* attribute for the whole line */ 3099 #endif 3100 #ifdef FEAT_SEARCH_EXTRA 3101 matchitem_T *cur; /* points to the match list */ 3102 match_T *shl; /* points to search_hl or a match */ 3103 int shl_flag; /* flag to indicate whether search_hl 3104 has been processed or not */ 3105 int pos_inprogress; /* marks that position match search is 3106 in progress */ 3107 int prevcol_hl_flag; /* flag to indicate whether prevcol 3108 equals startcol of search_hl or one 3109 of the matches */ 3110 #endif 3111 #ifdef FEAT_ARABIC 3112 int prev_c = 0; /* previous Arabic character */ 3113 int prev_c1 = 0; /* first composing char for prev_c */ 3114 #endif 3115 #if defined(LINE_ATTR) 3116 int did_line_attr = 0; 3117 #endif 3118 #ifdef FEAT_TERMINAL 3119 int get_term_attr = FALSE; 3120 int term_attr = 0; /* background for terminal window */ 3121 #endif 3122 3123 /* draw_state: items that are drawn in sequence: */ 3124 #define WL_START 0 /* nothing done yet */ 3125 #ifdef FEAT_CMDWIN 3126 # define WL_CMDLINE WL_START + 1 /* cmdline window column */ 3127 #else 3128 # define WL_CMDLINE WL_START 3129 #endif 3130 #ifdef FEAT_FOLDING 3131 # define WL_FOLD WL_CMDLINE + 1 /* 'foldcolumn' */ 3132 #else 3133 # define WL_FOLD WL_CMDLINE 3134 #endif 3135 #ifdef FEAT_SIGNS 3136 # define WL_SIGN WL_FOLD + 1 /* column for signs */ 3137 #else 3138 # define WL_SIGN WL_FOLD /* column for signs */ 3139 #endif 3140 #define WL_NR WL_SIGN + 1 /* line number */ 3141 #ifdef FEAT_LINEBREAK 3142 # define WL_BRI WL_NR + 1 /* 'breakindent' */ 3143 #else 3144 # define WL_BRI WL_NR 3145 #endif 3146 #if defined(FEAT_LINEBREAK) || defined(FEAT_DIFF) 3147 # define WL_SBR WL_BRI + 1 /* 'showbreak' or 'diff' */ 3148 #else 3149 # define WL_SBR WL_BRI 3150 #endif 3151 #define WL_LINE WL_SBR + 1 /* text in the line */ 3152 int draw_state = WL_START; /* what to draw next */ 3153 #if defined(FEAT_XIM) && defined(FEAT_GUI_GTK) 3154 int feedback_col = 0; 3155 int feedback_old_attr = -1; 3156 #endif 3157 3158 #ifdef FEAT_CONCEAL 3159 int syntax_flags = 0; 3160 int syntax_seqnr = 0; 3161 int prev_syntax_id = 0; 3162 int conceal_attr = HL_ATTR(HLF_CONCEAL); 3163 int is_concealing = FALSE; 3164 int boguscols = 0; /* nonexistent columns added to force 3165 wrapping */ 3166 int vcol_off = 0; /* offset for concealed characters */ 3167 int did_wcol = FALSE; 3168 int match_conc = 0; /* cchar for match functions */ 3169 int has_match_conc = 0; /* match wants to conceal */ 3170 int old_boguscols = 0; 3171 # define VCOL_HLC (vcol - vcol_off) 3172 # define FIX_FOR_BOGUSCOLS \ 3173 { \ 3174 n_extra += vcol_off; \ 3175 vcol -= vcol_off; \ 3176 vcol_off = 0; \ 3177 col -= boguscols; \ 3178 old_boguscols = boguscols; \ 3179 boguscols = 0; \ 3180 } 3181 #else 3182 # define VCOL_HLC (vcol) 3183 #endif 3184 3185 if (startrow > endrow) /* past the end already! */ 3186 return startrow; 3187 3188 row = startrow; 3189 screen_row = row + W_WINROW(wp); 3190 3191 if (!number_only) 3192 { 3193 /* 3194 * To speed up the loop below, set extra_check when there is linebreak, 3195 * trailing white space and/or syntax processing to be done. 3196 */ 3197 #ifdef FEAT_LINEBREAK 3198 extra_check = wp->w_p_lbr; 3199 #endif 3200 #ifdef FEAT_SYN_HL 3201 if (syntax_present(wp) && !wp->w_s->b_syn_error 3202 # ifdef SYN_TIME_LIMIT 3203 && !wp->w_s->b_syn_slow 3204 # endif 3205 ) 3206 { 3207 /* Prepare for syntax highlighting in this line. When there is an 3208 * error, stop syntax highlighting. */ 3209 save_did_emsg = did_emsg; 3210 did_emsg = FALSE; 3211 syntax_start(wp, lnum); 3212 if (did_emsg) 3213 wp->w_s->b_syn_error = TRUE; 3214 else 3215 { 3216 did_emsg = save_did_emsg; 3217 #ifdef SYN_TIME_LIMIT 3218 if (!wp->w_s->b_syn_slow) 3219 #endif 3220 { 3221 has_syntax = TRUE; 3222 extra_check = TRUE; 3223 } 3224 } 3225 } 3226 3227 /* Check for columns to display for 'colorcolumn'. */ 3228 color_cols = wp->w_p_cc_cols; 3229 if (color_cols != NULL) 3230 draw_color_col = advance_color_col(VCOL_HLC, &color_cols); 3231 #endif 3232 3233 #ifdef FEAT_TERMINAL 3234 if (term_show_buffer(wp->w_buffer)) 3235 { 3236 extra_check = TRUE; 3237 get_term_attr = TRUE; 3238 term_attr = term_get_attr(wp->w_buffer, lnum, -1); 3239 } 3240 #endif 3241 3242 #ifdef FEAT_SPELL 3243 if (wp->w_p_spell 3244 && *wp->w_s->b_p_spl != NUL 3245 && wp->w_s->b_langp.ga_len > 0 3246 && *(char **)(wp->w_s->b_langp.ga_data) != NULL) 3247 { 3248 /* Prepare for spell checking. */ 3249 has_spell = TRUE; 3250 extra_check = TRUE; 3251 3252 /* Get the start of the next line, so that words that wrap to the 3253 * next line are found too: "et<line-break>al.". 3254 * Trick: skip a few chars for C/shell/Vim comments */ 3255 nextline[SPWORDLEN] = NUL; 3256 if (lnum < wp->w_buffer->b_ml.ml_line_count) 3257 { 3258 line = ml_get_buf(wp->w_buffer, lnum + 1, FALSE); 3259 spell_cat_line(nextline + SPWORDLEN, line, SPWORDLEN); 3260 } 3261 3262 /* When a word wrapped from the previous line the start of the 3263 * current line is valid. */ 3264 if (lnum == checked_lnum) 3265 cur_checked_col = checked_col; 3266 checked_lnum = 0; 3267 3268 /* When there was a sentence end in the previous line may require a 3269 * word starting with capital in this line. In line 1 always check 3270 * the first word. */ 3271 if (lnum != capcol_lnum) 3272 cap_col = -1; 3273 if (lnum == 1) 3274 cap_col = 0; 3275 capcol_lnum = 0; 3276 } 3277 #endif 3278 3279 /* 3280 * handle visual active in this window 3281 */ 3282 fromcol = -10; 3283 tocol = MAXCOL; 3284 if (VIsual_active && wp->w_buffer == curwin->w_buffer) 3285 { 3286 /* Visual is after curwin->w_cursor */ 3287 if (LTOREQ_POS(curwin->w_cursor, VIsual)) 3288 { 3289 top = &curwin->w_cursor; 3290 bot = &VIsual; 3291 } 3292 else /* Visual is before curwin->w_cursor */ 3293 { 3294 top = &VIsual; 3295 bot = &curwin->w_cursor; 3296 } 3297 lnum_in_visual_area = (lnum >= top->lnum && lnum <= bot->lnum); 3298 if (VIsual_mode == Ctrl_V) /* block mode */ 3299 { 3300 if (lnum_in_visual_area) 3301 { 3302 fromcol = wp->w_old_cursor_fcol; 3303 tocol = wp->w_old_cursor_lcol; 3304 } 3305 } 3306 else /* non-block mode */ 3307 { 3308 if (lnum > top->lnum && lnum <= bot->lnum) 3309 fromcol = 0; 3310 else if (lnum == top->lnum) 3311 { 3312 if (VIsual_mode == 'V') /* linewise */ 3313 fromcol = 0; 3314 else 3315 { 3316 getvvcol(wp, top, (colnr_T *)&fromcol, NULL, NULL); 3317 if (gchar_pos(top) == NUL) 3318 tocol = fromcol + 1; 3319 } 3320 } 3321 if (VIsual_mode != 'V' && lnum == bot->lnum) 3322 { 3323 if (*p_sel == 'e' && bot->col == 0 && bot->coladd == 0) 3324 { 3325 fromcol = -10; 3326 tocol = MAXCOL; 3327 } 3328 else if (bot->col == MAXCOL) 3329 tocol = MAXCOL; 3330 else 3331 { 3332 pos = *bot; 3333 if (*p_sel == 'e') 3334 getvvcol(wp, &pos, (colnr_T *)&tocol, NULL, NULL); 3335 else 3336 { 3337 getvvcol(wp, &pos, NULL, NULL, (colnr_T *)&tocol); 3338 ++tocol; 3339 } 3340 } 3341 } 3342 } 3343 3344 /* Check if the character under the cursor should not be inverted */ 3345 if (!highlight_match && lnum == curwin->w_cursor.lnum && wp == curwin 3346 #ifdef FEAT_GUI 3347 && !gui.in_use 3348 #endif 3349 ) 3350 noinvcur = TRUE; 3351 3352 /* if inverting in this line set area_highlighting */ 3353 if (fromcol >= 0) 3354 { 3355 area_highlighting = TRUE; 3356 attr = HL_ATTR(HLF_V); 3357 #if defined(FEAT_CLIPBOARD) && defined(FEAT_X11) 3358 if ((clip_star.available && !clip_star.owned 3359 && clip_isautosel_star()) 3360 || (clip_plus.available && !clip_plus.owned 3361 && clip_isautosel_plus())) 3362 attr = HL_ATTR(HLF_VNC); 3363 #endif 3364 } 3365 } 3366 3367 /* 3368 * handle 'incsearch' and ":s///c" highlighting 3369 */ 3370 else if (highlight_match 3371 && wp == curwin 3372 && lnum >= curwin->w_cursor.lnum 3373 && lnum <= curwin->w_cursor.lnum + search_match_lines) 3374 { 3375 if (lnum == curwin->w_cursor.lnum) 3376 getvcol(curwin, &(curwin->w_cursor), 3377 (colnr_T *)&fromcol, NULL, NULL); 3378 else 3379 fromcol = 0; 3380 if (lnum == curwin->w_cursor.lnum + search_match_lines) 3381 { 3382 pos.lnum = lnum; 3383 pos.col = search_match_endcol; 3384 getvcol(curwin, &pos, (colnr_T *)&tocol, NULL, NULL); 3385 } 3386 else 3387 tocol = MAXCOL; 3388 /* do at least one character; happens when past end of line */ 3389 if (fromcol == tocol) 3390 tocol = fromcol + 1; 3391 area_highlighting = TRUE; 3392 attr = HL_ATTR(HLF_I); 3393 } 3394 } 3395 3396 #ifdef FEAT_DIFF 3397 filler_lines = diff_check(wp, lnum); 3398 if (filler_lines < 0) 3399 { 3400 if (filler_lines == -1) 3401 { 3402 if (diff_find_change(wp, lnum, &change_start, &change_end)) 3403 diff_hlf = HLF_ADD; /* added line */ 3404 else if (change_start == 0) 3405 diff_hlf = HLF_TXD; /* changed text */ 3406 else 3407 diff_hlf = HLF_CHD; /* changed line */ 3408 } 3409 else 3410 diff_hlf = HLF_ADD; /* added line */ 3411 filler_lines = 0; 3412 area_highlighting = TRUE; 3413 } 3414 if (lnum == wp->w_topline) 3415 filler_lines = wp->w_topfill; 3416 filler_todo = filler_lines; 3417 #endif 3418 3419 #ifdef LINE_ATTR 3420 # ifdef FEAT_SIGNS 3421 /* If this line has a sign with line highlighting set line_attr. */ 3422 v = buf_getsigntype(wp->w_buffer, lnum, SIGN_LINEHL); 3423 if (v != 0) 3424 line_attr = sign_get_attr((int)v, TRUE); 3425 # endif 3426 # if defined(FEAT_QUICKFIX) 3427 /* Highlight the current line in the quickfix window. */ 3428 if (bt_quickfix(wp->w_buffer) && qf_current_entry(wp) == lnum) 3429 line_attr = HL_ATTR(HLF_QFL); 3430 # endif 3431 if (line_attr != 0) 3432 area_highlighting = TRUE; 3433 #endif 3434 3435 line = ml_get_buf(wp->w_buffer, lnum, FALSE); 3436 ptr = line; 3437 3438 #ifdef FEAT_SPELL 3439 if (has_spell && !number_only) 3440 { 3441 /* For checking first word with a capital skip white space. */ 3442 if (cap_col == 0) 3443 cap_col = getwhitecols(line); 3444 3445 /* To be able to spell-check over line boundaries copy the end of the 3446 * current line into nextline[]. Above the start of the next line was 3447 * copied to nextline[SPWORDLEN]. */ 3448 if (nextline[SPWORDLEN] == NUL) 3449 { 3450 /* No next line or it is empty. */ 3451 nextlinecol = MAXCOL; 3452 nextline_idx = 0; 3453 } 3454 else 3455 { 3456 v = (long)STRLEN(line); 3457 if (v < SPWORDLEN) 3458 { 3459 /* Short line, use it completely and append the start of the 3460 * next line. */ 3461 nextlinecol = 0; 3462 mch_memmove(nextline, line, (size_t)v); 3463 STRMOVE(nextline + v, nextline + SPWORDLEN); 3464 nextline_idx = v + 1; 3465 } 3466 else 3467 { 3468 /* Long line, use only the last SPWORDLEN bytes. */ 3469 nextlinecol = v - SPWORDLEN; 3470 mch_memmove(nextline, line + nextlinecol, SPWORDLEN); 3471 nextline_idx = SPWORDLEN + 1; 3472 } 3473 } 3474 } 3475 #endif 3476 3477 if (wp->w_p_list) 3478 { 3479 if (lcs_space || lcs_trail || lcs_nbsp) 3480 extra_check = TRUE; 3481 /* find start of trailing whitespace */ 3482 if (lcs_trail) 3483 { 3484 trailcol = (colnr_T)STRLEN(ptr); 3485 while (trailcol > (colnr_T)0 && VIM_ISWHITE(ptr[trailcol - 1])) 3486 --trailcol; 3487 trailcol += (colnr_T) (ptr - line); 3488 } 3489 } 3490 3491 /* 3492 * 'nowrap' or 'wrap' and a single line that doesn't fit: Advance to the 3493 * first character to be displayed. 3494 */ 3495 if (wp->w_p_wrap) 3496 v = wp->w_skipcol; 3497 else 3498 v = wp->w_leftcol; 3499 if (v > 0 && !number_only) 3500 { 3501 char_u *prev_ptr = ptr; 3502 3503 while (vcol < v && *ptr != NUL) 3504 { 3505 c = win_lbr_chartabsize(wp, line, ptr, (colnr_T)vcol, NULL); 3506 vcol += c; 3507 prev_ptr = ptr; 3508 MB_PTR_ADV(ptr); 3509 } 3510 3511 /* When: 3512 * - 'cuc' is set, or 3513 * - 'colorcolumn' is set, or 3514 * - 'virtualedit' is set, or 3515 * - the visual mode is active, 3516 * the end of the line may be before the start of the displayed part. 3517 */ 3518 if (vcol < v && ( 3519 #ifdef FEAT_SYN_HL 3520 wp->w_p_cuc || draw_color_col || 3521 #endif 3522 virtual_active() || 3523 (VIsual_active && wp->w_buffer == curwin->w_buffer))) 3524 vcol = v; 3525 3526 /* Handle a character that's not completely on the screen: Put ptr at 3527 * that character but skip the first few screen characters. */ 3528 if (vcol > v) 3529 { 3530 vcol -= c; 3531 ptr = prev_ptr; 3532 /* If the character fits on the screen, don't need to skip it. 3533 * Except for a TAB. */ 3534 if (( (*mb_ptr2cells)(ptr) >= c || *ptr == TAB) && col == 0) 3535 n_skip = v - vcol; 3536 } 3537 3538 /* 3539 * Adjust for when the inverted text is before the screen, 3540 * and when the start of the inverted text is before the screen. 3541 */ 3542 if (tocol <= vcol) 3543 fromcol = 0; 3544 else if (fromcol >= 0 && fromcol < vcol) 3545 fromcol = vcol; 3546 3547 #ifdef FEAT_LINEBREAK 3548 /* When w_skipcol is non-zero, first line needs 'showbreak' */ 3549 if (wp->w_p_wrap) 3550 need_showbreak = TRUE; 3551 #endif 3552 #ifdef FEAT_SPELL 3553 /* When spell checking a word we need to figure out the start of the 3554 * word and if it's badly spelled or not. */ 3555 if (has_spell) 3556 { 3557 int len; 3558 colnr_T linecol = (colnr_T)(ptr - line); 3559 hlf_T spell_hlf = HLF_COUNT; 3560 3561 pos = wp->w_cursor; 3562 wp->w_cursor.lnum = lnum; 3563 wp->w_cursor.col = linecol; 3564 len = spell_move_to(wp, FORWARD, TRUE, TRUE, &spell_hlf); 3565 3566 /* spell_move_to() may call ml_get() and make "line" invalid */ 3567 line = ml_get_buf(wp->w_buffer, lnum, FALSE); 3568 ptr = line + linecol; 3569 3570 if (len == 0 || (int)wp->w_cursor.col > ptr - line) 3571 { 3572 /* no bad word found at line start, don't check until end of a 3573 * word */ 3574 spell_hlf = HLF_COUNT; 3575 word_end = (int)(spell_to_word_end(ptr, wp) - line + 1); 3576 } 3577 else 3578 { 3579 /* bad word found, use attributes until end of word */ 3580 word_end = wp->w_cursor.col + len + 1; 3581 3582 /* Turn index into actual attributes. */ 3583 if (spell_hlf != HLF_COUNT) 3584 spell_attr = highlight_attr[spell_hlf]; 3585 } 3586 wp->w_cursor = pos; 3587 3588 # ifdef FEAT_SYN_HL 3589 /* Need to restart syntax highlighting for this line. */ 3590 if (has_syntax) 3591 syntax_start(wp, lnum); 3592 # endif 3593 } 3594 #endif 3595 } 3596 3597 /* 3598 * Correct highlighting for cursor that can't be disabled. 3599 * Avoids having to check this for each character. 3600 */ 3601 if (fromcol >= 0) 3602 { 3603 if (noinvcur) 3604 { 3605 if ((colnr_T)fromcol == wp->w_virtcol) 3606 { 3607 /* highlighting starts at cursor, let it start just after the 3608 * cursor */ 3609 fromcol_prev = fromcol; 3610 fromcol = -1; 3611 } 3612 else if ((colnr_T)fromcol < wp->w_virtcol) 3613 /* restart highlighting after the cursor */ 3614 fromcol_prev = wp->w_virtcol; 3615 } 3616 if (fromcol >= tocol) 3617 fromcol = -1; 3618 } 3619 3620 #ifdef FEAT_SEARCH_EXTRA 3621 /* 3622 * Handle highlighting the last used search pattern and matches. 3623 * Do this for both search_hl and the match list. 3624 */ 3625 cur = wp->w_match_head; 3626 shl_flag = FALSE; 3627 while ((cur != NULL || shl_flag == FALSE) && !number_only) 3628 { 3629 if (shl_flag == FALSE) 3630 { 3631 shl = &search_hl; 3632 shl_flag = TRUE; 3633 } 3634 else 3635 shl = &cur->hl; 3636 shl->startcol = MAXCOL; 3637 shl->endcol = MAXCOL; 3638 shl->attr_cur = 0; 3639 shl->is_addpos = FALSE; 3640 v = (long)(ptr - line); 3641 if (cur != NULL) 3642 cur->pos.cur = 0; 3643 next_search_hl(wp, shl, lnum, (colnr_T)v, 3644 shl == &search_hl ? NULL : cur); 3645 3646 /* Need to get the line again, a multi-line regexp may have made it 3647 * invalid. */ 3648 line = ml_get_buf(wp->w_buffer, lnum, FALSE); 3649 ptr = line + v; 3650 3651 if (shl->lnum != 0 && shl->lnum <= lnum) 3652 { 3653 if (shl->lnum == lnum) 3654 shl->startcol = shl->rm.startpos[0].col; 3655 else 3656 shl->startcol = 0; 3657 if (lnum == shl->lnum + shl->rm.endpos[0].lnum 3658 - shl->rm.startpos[0].lnum) 3659 shl->endcol = shl->rm.endpos[0].col; 3660 else 3661 shl->endcol = MAXCOL; 3662 /* Highlight one character for an empty match. */ 3663 if (shl->startcol == shl->endcol) 3664 { 3665 if (has_mbyte && line[shl->endcol] != NUL) 3666 shl->endcol += (*mb_ptr2len)(line + shl->endcol); 3667 else 3668 ++shl->endcol; 3669 } 3670 if ((long)shl->startcol < v) /* match at leftcol */ 3671 { 3672 shl->attr_cur = shl->attr; 3673 search_attr = shl->attr; 3674 } 3675 area_highlighting = TRUE; 3676 } 3677 if (shl != &search_hl && cur != NULL) 3678 cur = cur->next; 3679 } 3680 #endif 3681 3682 #ifdef FEAT_SYN_HL 3683 // Cursor line highlighting for 'cursorline' in the current window. 3684 if (wp->w_p_cul && lnum == wp->w_cursor.lnum) 3685 { 3686 // Do not show the cursor line when Visual mode is active, because it's 3687 // not clear what is selected then. Do update w_last_cursorline. 3688 if (!(wp == curwin && VIsual_active)) 3689 { 3690 line_attr = HL_ATTR(HLF_CUL); 3691 area_highlighting = TRUE; 3692 } 3693 wp->w_last_cursorline = wp->w_cursor.lnum; 3694 } 3695 #endif 3696 3697 #ifdef FEAT_TEXT_PROP 3698 { 3699 char_u *prop_start; 3700 3701 text_prop_count = get_text_props(wp->w_buffer, lnum, 3702 &prop_start, FALSE); 3703 if (text_prop_count > 0) 3704 { 3705 // Make a copy of the properties, so that they are properly 3706 // aligned. 3707 text_props = (textprop_T *)alloc( 3708 text_prop_count * sizeof(textprop_T)); 3709 if (text_props != NULL) 3710 mch_memmove(text_props, prop_start, 3711 text_prop_count * sizeof(textprop_T)); 3712 3713 // Allocate an array for the indexes. 3714 text_prop_idxs = (int *)alloc(text_prop_count * sizeof(int)); 3715 area_highlighting = TRUE; 3716 extra_check = TRUE; 3717 } 3718 } 3719 #endif 3720 3721 off = (unsigned)(current_ScreenLine - ScreenLines); 3722 col = 0; 3723 #ifdef FEAT_RIGHTLEFT 3724 if (wp->w_p_rl) 3725 { 3726 /* Rightleft window: process the text in the normal direction, but put 3727 * it in current_ScreenLine[] from right to left. Start at the 3728 * rightmost column of the window. */ 3729 col = wp->w_width - 1; 3730 off += col; 3731 } 3732 #endif 3733 3734 /* 3735 * Repeat for the whole displayed line. 3736 */ 3737 for (;;) 3738 { 3739 #ifdef FEAT_CONCEAL 3740 has_match_conc = 0; 3741 #endif 3742 /* Skip this quickly when working on the text. */ 3743 if (draw_state != WL_LINE) 3744 { 3745 #ifdef FEAT_CMDWIN 3746 if (draw_state == WL_CMDLINE - 1 && n_extra == 0) 3747 { 3748 draw_state = WL_CMDLINE; 3749 if (cmdwin_type != 0 && wp == curwin) 3750 { 3751 /* Draw the cmdline character. */ 3752 n_extra = 1; 3753 c_extra = cmdwin_type; 3754 c_final = NUL; 3755 char_attr = HL_ATTR(HLF_AT); 3756 } 3757 } 3758 #endif 3759 3760 #ifdef FEAT_FOLDING 3761 if (draw_state == WL_FOLD - 1 && n_extra == 0) 3762 { 3763 int fdc = compute_foldcolumn(wp, 0); 3764 3765 draw_state = WL_FOLD; 3766 if (fdc > 0) 3767 { 3768 /* Draw the 'foldcolumn'. Allocate a buffer, "extra" may 3769 * already be in use. */ 3770 vim_free(p_extra_free); 3771 p_extra_free = alloc(12 + 1); 3772 3773 if (p_extra_free != NULL) 3774 { 3775 fill_foldcolumn(p_extra_free, wp, FALSE, lnum); 3776 n_extra = fdc; 3777 p_extra_free[n_extra] = NUL; 3778 p_extra = p_extra_free; 3779 c_extra = NUL; 3780 c_final = NUL; 3781 char_attr = HL_ATTR(HLF_FC); 3782 } 3783 } 3784 } 3785 #endif 3786 3787 #ifdef FEAT_SIGNS 3788 if (draw_state == WL_SIGN - 1 && n_extra == 0) 3789 { 3790 draw_state = WL_SIGN; 3791 /* Show the sign column when there are any signs in this 3792 * buffer or when using Netbeans. */ 3793 if (signcolumn_on(wp)) 3794 { 3795 int text_sign; 3796 # ifdef FEAT_SIGN_ICONS 3797 int icon_sign; 3798 # endif 3799 3800 /* Draw two cells with the sign value or blank. */ 3801 c_extra = ' '; 3802 c_final = NUL; 3803 char_attr = HL_ATTR(HLF_SC); 3804 n_extra = 2; 3805 3806 if (row == startrow 3807 #ifdef FEAT_DIFF 3808 + filler_lines && filler_todo <= 0 3809 #endif 3810 ) 3811 { 3812 text_sign = buf_getsigntype(wp->w_buffer, lnum, 3813 SIGN_TEXT); 3814 # ifdef FEAT_SIGN_ICONS 3815 icon_sign = buf_getsigntype(wp->w_buffer, lnum, 3816 SIGN_ICON); 3817 if (gui.in_use && icon_sign != 0) 3818 { 3819 /* Use the image in this position. */ 3820 c_extra = SIGN_BYTE; 3821 c_final = NUL; 3822 # ifdef FEAT_NETBEANS_INTG 3823 if (buf_signcount(wp->w_buffer, lnum) > 1) 3824 { 3825 c_extra = MULTISIGN_BYTE; 3826 c_final = NUL; 3827 } 3828 # endif 3829 char_attr = icon_sign; 3830 } 3831 else 3832 # endif 3833 if (text_sign != 0) 3834 { 3835 p_extra = sign_get_text(text_sign); 3836 if (p_extra != NULL) 3837 { 3838 c_extra = NUL; 3839 c_final = NUL; 3840 n_extra = (int)STRLEN(p_extra); 3841 } 3842 char_attr = sign_get_attr(text_sign, FALSE); 3843 } 3844 } 3845 } 3846 } 3847 #endif 3848 3849 if (draw_state == WL_NR - 1 && n_extra == 0) 3850 { 3851 draw_state = WL_NR; 3852 /* Display the absolute or relative line number. After the 3853 * first fill with blanks when the 'n' flag isn't in 'cpo' */ 3854 if ((wp->w_p_nu || wp->w_p_rnu) 3855 && (row == startrow 3856 #ifdef FEAT_DIFF 3857 + filler_lines 3858 #endif 3859 || vim_strchr(p_cpo, CPO_NUMCOL) == NULL)) 3860 { 3861 /* Draw the line number (empty space after wrapping). */ 3862 if (row == startrow 3863 #ifdef FEAT_DIFF 3864 + filler_lines 3865 #endif 3866 ) 3867 { 3868 long num; 3869 char *fmt = "%*ld "; 3870 3871 if (wp->w_p_nu && !wp->w_p_rnu) 3872 /* 'number' + 'norelativenumber' */ 3873 num = (long)lnum; 3874 else 3875 { 3876 /* 'relativenumber', don't use negative numbers */ 3877 num = labs((long)get_cursor_rel_lnum(wp, lnum)); 3878 if (num == 0 && wp->w_p_nu && wp->w_p_rnu) 3879 { 3880 /* 'number' + 'relativenumber' */ 3881 num = lnum; 3882 fmt = "%-*ld "; 3883 } 3884 } 3885 3886 sprintf((char *)extra, fmt, 3887 number_width(wp), num); 3888 if (wp->w_skipcol > 0) 3889 for (p_extra = extra; *p_extra == ' '; ++p_extra) 3890 *p_extra = '-'; 3891 #ifdef FEAT_RIGHTLEFT 3892 if (wp->w_p_rl) /* reverse line numbers */ 3893 { 3894 char_u *p1, *p2; 3895 int t; 3896 3897 // like rl_mirror(), but keep the space at the end 3898 p2 = skiptowhite(extra) - 1; 3899 for (p1 = extra; p1 < p2; ++p1, --p2) 3900 { 3901 t = *p1; 3902 *p1 = *p2; 3903 *p2 = t; 3904 } 3905 } 3906 #endif 3907 p_extra = extra; 3908 c_extra = NUL; 3909 c_final = NUL; 3910 } 3911 else 3912 { 3913 c_extra = ' '; 3914 c_final = NUL; 3915 } 3916 n_extra = number_width(wp) + 1; 3917 char_attr = HL_ATTR(HLF_N); 3918 #ifdef FEAT_SYN_HL 3919 /* When 'cursorline' is set highlight the line number of 3920 * the current line differently. 3921 * TODO: Can we use CursorLine instead of CursorLineNr 3922 * when CursorLineNr isn't set? */ 3923 if ((wp->w_p_cul || wp->w_p_rnu) 3924 && lnum == wp->w_cursor.lnum) 3925 char_attr = HL_ATTR(HLF_CLN); 3926 #endif 3927 } 3928 } 3929 3930 #ifdef FEAT_LINEBREAK 3931 if (wp->w_p_brisbr && draw_state == WL_BRI - 1 3932 && n_extra == 0 && *p_sbr != NUL) 3933 /* draw indent after showbreak value */ 3934 draw_state = WL_BRI; 3935 else if (wp->w_p_brisbr && draw_state == WL_SBR && n_extra == 0) 3936 /* After the showbreak, draw the breakindent */ 3937 draw_state = WL_BRI - 1; 3938 3939 /* draw 'breakindent': indent wrapped text accordingly */ 3940 if (draw_state == WL_BRI - 1 && n_extra == 0) 3941 { 3942 draw_state = WL_BRI; 3943 /* if need_showbreak is set, breakindent also applies */ 3944 if (wp->w_p_bri && n_extra == 0 3945 && (row != startrow || need_showbreak) 3946 # ifdef FEAT_DIFF 3947 && filler_lines == 0 3948 # endif 3949 ) 3950 { 3951 char_attr = 0; 3952 # ifdef FEAT_DIFF 3953 if (diff_hlf != (hlf_T)0) 3954 { 3955 char_attr = HL_ATTR(diff_hlf); 3956 # ifdef FEAT_SYN_HL 3957 if (wp->w_p_cul && lnum == wp->w_cursor.lnum) 3958 char_attr = hl_combine_attr(char_attr, 3959 HL_ATTR(HLF_CUL)); 3960 # endif 3961 } 3962 # endif 3963 p_extra = NULL; 3964 c_extra = ' '; 3965 n_extra = get_breakindent_win(wp, 3966 ml_get_buf(wp->w_buffer, lnum, FALSE)); 3967 /* Correct end of highlighted area for 'breakindent', 3968 * required when 'linebreak' is also set. */ 3969 if (tocol == vcol) 3970 tocol += n_extra; 3971 } 3972 } 3973 #endif 3974 3975 #if defined(FEAT_LINEBREAK) || defined(FEAT_DIFF) 3976 if (draw_state == WL_SBR - 1 && n_extra == 0) 3977 { 3978 draw_state = WL_SBR; 3979 # ifdef FEAT_DIFF 3980 if (filler_todo > 0) 3981 { 3982 /* Draw "deleted" diff line(s). */ 3983 if (char2cells(fill_diff) > 1) 3984 { 3985 c_extra = '-'; 3986 c_final = NUL; 3987 } 3988 else 3989 { 3990 c_extra = fill_diff; 3991 c_final = NUL; 3992 } 3993 # ifdef FEAT_RIGHTLEFT 3994 if (wp->w_p_rl) 3995 n_extra = col + 1; 3996 else 3997 # endif 3998 n_extra = wp->w_width - col; 3999 char_attr = HL_ATTR(HLF_DED); 4000 } 4001 # endif 4002 # ifdef FEAT_LINEBREAK 4003 if (*p_sbr != NUL && need_showbreak) 4004 { 4005 /* Draw 'showbreak' at the start of each broken line. */ 4006 p_extra = p_sbr; 4007 c_extra = NUL; 4008 c_final = NUL; 4009 n_extra = (int)STRLEN(p_sbr); 4010 char_attr = HL_ATTR(HLF_AT); 4011 need_showbreak = FALSE; 4012 vcol_sbr = vcol + MB_CHARLEN(p_sbr); 4013 /* Correct end of highlighted area for 'showbreak', 4014 * required when 'linebreak' is also set. */ 4015 if (tocol == vcol) 4016 tocol += n_extra; 4017 #ifdef FEAT_SYN_HL 4018 /* combine 'showbreak' with 'cursorline' */ 4019 if (wp->w_p_cul && lnum == wp->w_cursor.lnum) 4020 char_attr = hl_combine_attr(char_attr, 4021 HL_ATTR(HLF_CUL)); 4022 #endif 4023 } 4024 # endif 4025 } 4026 #endif 4027 4028 if (draw_state == WL_LINE - 1 && n_extra == 0) 4029 { 4030 draw_state = WL_LINE; 4031 if (saved_n_extra) 4032 { 4033 /* Continue item from end of wrapped line. */ 4034 n_extra = saved_n_extra; 4035 c_extra = saved_c_extra; 4036 c_final = saved_c_final; 4037 p_extra = saved_p_extra; 4038 char_attr = saved_char_attr; 4039 } 4040 else 4041 char_attr = 0; 4042 } 4043 } 4044 4045 // When still displaying '$' of change command, stop at cursor. 4046 // When only displaying the (relative) line number and that's done, 4047 // stop here. 4048 if ((dollar_vcol >= 0 && wp == curwin 4049 && lnum == wp->w_cursor.lnum && vcol >= (long)wp->w_virtcol 4050 #ifdef FEAT_DIFF 4051 && filler_todo <= 0 4052 #endif 4053 ) 4054 || (number_only && draw_state > WL_NR)) 4055 { 4056 screen_line(screen_row, wp->w_wincol, col, -(int)wp->w_width, 4057 HAS_RIGHTLEFT(wp->w_p_rl)); 4058 /* Pretend we have finished updating the window. Except when 4059 * 'cursorcolumn' is set. */ 4060 #ifdef FEAT_SYN_HL 4061 if (wp->w_p_cuc) 4062 row = wp->w_cline_row + wp->w_cline_height; 4063 else 4064 #endif 4065 row = wp->w_height; 4066 break; 4067 } 4068 4069 if (draw_state == WL_LINE && (area_highlighting 4070 #ifdef FEAT_SPELL 4071 || has_spell 4072 #endif 4073 )) 4074 { 4075 /* handle Visual or match highlighting in this line */ 4076 if (vcol == fromcol 4077 || (has_mbyte && vcol + 1 == fromcol && n_extra == 0 4078 && (*mb_ptr2cells)(ptr) > 1) 4079 || ((int)vcol_prev == fromcol_prev 4080 && vcol_prev < vcol /* not at margin */ 4081 && vcol < tocol)) 4082 area_attr = attr; /* start highlighting */ 4083 else if (area_attr != 0 4084 && (vcol == tocol 4085 || (noinvcur && (colnr_T)vcol == wp->w_virtcol))) 4086 area_attr = 0; /* stop highlighting */ 4087 4088 #ifdef FEAT_SEARCH_EXTRA 4089 if (!n_extra) 4090 { 4091 /* 4092 * Check for start/end of search pattern match. 4093 * After end, check for start/end of next match. 4094 * When another match, have to check for start again. 4095 * Watch out for matching an empty string! 4096 * Do this for 'search_hl' and the match list (ordered by 4097 * priority). 4098 */ 4099 v = (long)(ptr - line); 4100 cur = wp->w_match_head; 4101 shl_flag = FALSE; 4102 while (cur != NULL || shl_flag == FALSE) 4103 { 4104 if (shl_flag == FALSE 4105 && ((cur != NULL 4106 && cur->priority > SEARCH_HL_PRIORITY) 4107 || cur == NULL)) 4108 { 4109 shl = &search_hl; 4110 shl_flag = TRUE; 4111 } 4112 else 4113 shl = &cur->hl; 4114 if (cur != NULL) 4115 cur->pos.cur = 0; 4116 pos_inprogress = TRUE; 4117 while (shl->rm.regprog != NULL 4118 || (cur != NULL && pos_inprogress)) 4119 { 4120 if (shl->startcol != MAXCOL 4121 && v >= (long)shl->startcol 4122 && v < (long)shl->endcol) 4123 { 4124 int tmp_col = v + MB_PTR2LEN(ptr); 4125 4126 if (shl->endcol < tmp_col) 4127 shl->endcol = tmp_col; 4128 shl->attr_cur = shl->attr; 4129 #ifdef FEAT_CONCEAL 4130 // Match with the "Conceal" group results in hiding 4131 // the match. 4132 if (cur != NULL 4133 && shl != &search_hl 4134 && syn_name2id((char_u *)"Conceal") 4135 == cur->hlg_id) 4136 { 4137 has_match_conc = 4138 v == (long)shl->startcol ? 2 : 1; 4139 match_conc = cur->conceal_char; 4140 } 4141 else 4142 has_match_conc = match_conc = 0; 4143 #endif 4144 } 4145 else if (v == (long)shl->endcol) 4146 { 4147 shl->attr_cur = 0; 4148 next_search_hl(wp, shl, lnum, (colnr_T)v, 4149 shl == &search_hl ? NULL : cur); 4150 pos_inprogress = cur == NULL || cur->pos.cur == 0 4151 ? FALSE : TRUE; 4152 4153 /* Need to get the line again, a multi-line regexp 4154 * may have made it invalid. */ 4155 line = ml_get_buf(wp->w_buffer, lnum, FALSE); 4156 ptr = line + v; 4157 4158 if (shl->lnum == lnum) 4159 { 4160 shl->startcol = shl->rm.startpos[0].col; 4161 if (shl->rm.endpos[0].lnum == 0) 4162 shl->endcol = shl->rm.endpos[0].col; 4163 else 4164 shl->endcol = MAXCOL; 4165 4166 if (shl->startcol == shl->endcol) 4167 { 4168 /* highlight empty match, try again after 4169 * it */ 4170 if (has_mbyte) 4171 shl->endcol += (*mb_ptr2len)(line 4172 + shl->endcol); 4173 else 4174 ++shl->endcol; 4175 } 4176 4177 /* Loop to check if the match starts at the 4178 * current position */ 4179 continue; 4180 } 4181 } 4182 break; 4183 } 4184 if (shl != &search_hl && cur != NULL) 4185 cur = cur->next; 4186 } 4187 4188 /* Use attributes from match with highest priority among 4189 * 'search_hl' and the match list. */ 4190 search_attr = search_hl.attr_cur; 4191 cur = wp->w_match_head; 4192 shl_flag = FALSE; 4193 while (cur != NULL || shl_flag == FALSE) 4194 { 4195 if (shl_flag == FALSE 4196 && ((cur != NULL 4197 && cur->priority > SEARCH_HL_PRIORITY) 4198 || cur == NULL)) 4199 { 4200 shl = &search_hl; 4201 shl_flag = TRUE; 4202 } 4203 else 4204 shl = &cur->hl; 4205 if (shl->attr_cur != 0) 4206 search_attr = shl->attr_cur; 4207 if (shl != &search_hl && cur != NULL) 4208 cur = cur->next; 4209 } 4210 /* Only highlight one character after the last column. */ 4211 if (*ptr == NUL && (did_line_attr >= 1 4212 || (wp->w_p_list && lcs_eol_one == -1))) 4213 search_attr = 0; 4214 } 4215 #endif 4216 4217 #ifdef FEAT_DIFF 4218 if (diff_hlf != (hlf_T)0) 4219 { 4220 if (diff_hlf == HLF_CHD && ptr - line >= change_start 4221 && n_extra == 0) 4222 diff_hlf = HLF_TXD; /* changed text */ 4223 if (diff_hlf == HLF_TXD && ptr - line > change_end 4224 && n_extra == 0) 4225 diff_hlf = HLF_CHD; /* changed line */ 4226 line_attr = HL_ATTR(diff_hlf); 4227 if (wp->w_p_cul && lnum == wp->w_cursor.lnum) 4228 line_attr = hl_combine_attr(line_attr, HL_ATTR(HLF_CUL)); 4229 } 4230 #endif 4231 4232 #ifdef FEAT_TEXT_PROP 4233 if (text_props != NULL) 4234 { 4235 int pi; 4236 int bcol = (int)(ptr - line); 4237 4238 // Check if any active property ends. 4239 for (pi = 0; pi < text_props_active; ++pi) 4240 { 4241 int tpi = text_prop_idxs[pi]; 4242 4243 if (bcol >= text_props[tpi].tp_col - 1 4244 + text_props[tpi].tp_len) 4245 { 4246 if (pi + 1 < text_props_active) 4247 mch_memmove(text_prop_idxs + pi, 4248 text_prop_idxs + pi + 1, 4249 sizeof(int) 4250 * (text_props_active - (pi + 1))); 4251 --text_props_active; 4252 --pi; 4253 } 4254 } 4255 4256 // Add any text property that starts in this column. 4257 while (text_prop_next < text_prop_count 4258 && bcol >= text_props[text_prop_next].tp_col - 1) 4259 text_prop_idxs[text_props_active++] = text_prop_next++; 4260 4261 text_prop_attr = 0; 4262 text_prop_combine = FALSE; 4263 if (text_props_active > 0) 4264 { 4265 // Sort the properties on priority and/or starting last. 4266 // Then combine the attributes, highest priority last. 4267 current_text_props = text_props; 4268 current_buf = wp->w_buffer; 4269 qsort((void *)text_prop_idxs, (size_t)text_props_active, 4270 sizeof(int), text_prop_compare); 4271 4272 for (pi = 0; pi < text_props_active; ++pi) 4273 { 4274 int tpi = text_prop_idxs[pi]; 4275 proptype_T *pt = text_prop_type_by_id( 4276 wp->w_buffer, text_props[tpi].tp_type); 4277 4278 if (pt != NULL) 4279 { 4280 int pt_attr = syn_id2attr(pt->pt_hl_id); 4281 4282 text_prop_type = pt; 4283 text_prop_attr = 4284 hl_combine_attr(text_prop_attr, pt_attr); 4285 text_prop_combine = pt->pt_flags & PT_FLAG_COMBINE; 4286 } 4287 } 4288 } 4289 } 4290 #endif 4291 4292 /* Decide which of the highlight attributes to use. */ 4293 attr_pri = TRUE; 4294 #ifdef LINE_ATTR 4295 if (area_attr != 0) 4296 char_attr = hl_combine_attr(line_attr, area_attr); 4297 else if (search_attr != 0) 4298 char_attr = hl_combine_attr(line_attr, search_attr); 4299 /* Use line_attr when not in the Visual or 'incsearch' area 4300 * (area_attr may be 0 when "noinvcur" is set). */ 4301 else if (line_attr != 0 && ((fromcol == -10 && tocol == MAXCOL) 4302 || vcol < fromcol || vcol_prev < fromcol_prev 4303 || vcol >= tocol)) 4304 char_attr = line_attr; 4305 #else 4306 if (area_attr != 0) 4307 char_attr = area_attr; 4308 else if (search_attr != 0) 4309 char_attr = search_attr; 4310 #endif 4311 else 4312 { 4313 attr_pri = FALSE; 4314 #ifdef FEAT_TEXT_PROP 4315 if (text_prop_type != NULL) 4316 { 4317 if (text_prop_combine) 4318 char_attr = hl_combine_attr( 4319 syntax_attr, text_prop_attr); 4320 else 4321 char_attr = text_prop_attr; 4322 } 4323 else 4324 #endif 4325 #ifdef FEAT_SYN_HL 4326 if (has_syntax) 4327 char_attr = syntax_attr; 4328 else 4329 #endif 4330 char_attr = 0; 4331 } 4332 } 4333 4334 /* 4335 * Get the next character to put on the screen. 4336 */ 4337 /* 4338 * The "p_extra" points to the extra stuff that is inserted to 4339 * represent special characters (non-printable stuff) and other 4340 * things. When all characters are the same, c_extra is used. 4341 * If c_final is set, it will compulsorily be used at the end. 4342 * "p_extra" must end in a NUL to avoid mb_ptr2len() reads past 4343 * "p_extra[n_extra]". 4344 * For the '$' of the 'list' option, n_extra == 1, p_extra == "". 4345 */ 4346 if (n_extra > 0) 4347 { 4348 if (c_extra != NUL || (n_extra == 1 && c_final != NUL)) 4349 { 4350 c = (n_extra == 1 && c_final != NUL) ? c_final : c_extra; 4351 mb_c = c; /* doesn't handle non-utf-8 multi-byte! */ 4352 if (enc_utf8 && utf_char2len(c) > 1) 4353 { 4354 mb_utf8 = TRUE; 4355 u8cc[0] = 0; 4356 c = 0xc0; 4357 } 4358 else 4359 mb_utf8 = FALSE; 4360 } 4361 else 4362 { 4363 c = *p_extra; 4364 if (has_mbyte) 4365 { 4366 mb_c = c; 4367 if (enc_utf8) 4368 { 4369 /* If the UTF-8 character is more than one byte: 4370 * Decode it into "mb_c". */ 4371 mb_l = utfc_ptr2len(p_extra); 4372 mb_utf8 = FALSE; 4373 if (mb_l > n_extra) 4374 mb_l = 1; 4375 else if (mb_l > 1) 4376 { 4377 mb_c = utfc_ptr2char(p_extra, u8cc); 4378 mb_utf8 = TRUE; 4379 c = 0xc0; 4380 } 4381 } 4382 else 4383 { 4384 /* if this is a DBCS character, put it in "mb_c" */ 4385 mb_l = MB_BYTE2LEN(c); 4386 if (mb_l >= n_extra) 4387 mb_l = 1; 4388 else if (mb_l > 1) 4389 mb_c = (c << 8) + p_extra[1]; 4390 } 4391 if (mb_l == 0) /* at the NUL at end-of-line */ 4392 mb_l = 1; 4393 4394 /* If a double-width char doesn't fit display a '>' in the 4395 * last column. */ 4396 if (( 4397 # ifdef FEAT_RIGHTLEFT 4398 wp->w_p_rl ? (col <= 0) : 4399 # endif 4400 (col >= wp->w_width - 1)) 4401 && (*mb_char2cells)(mb_c) == 2) 4402 { 4403 c = '>'; 4404 mb_c = c; 4405 mb_l = 1; 4406 mb_utf8 = FALSE; 4407 multi_attr = HL_ATTR(HLF_AT); 4408 /* put the pointer back to output the double-width 4409 * character at the start of the next line. */ 4410 ++n_extra; 4411 --p_extra; 4412 } 4413 else 4414 { 4415 n_extra -= mb_l - 1; 4416 p_extra += mb_l - 1; 4417 } 4418 } 4419 ++p_extra; 4420 } 4421 --n_extra; 4422 } 4423 else 4424 { 4425 #ifdef FEAT_LINEBREAK 4426 int c0; 4427 #endif 4428 4429 if (p_extra_free != NULL) 4430 VIM_CLEAR(p_extra_free); 4431 /* 4432 * Get a character from the line itself. 4433 */ 4434 c = *ptr; 4435 #ifdef FEAT_LINEBREAK 4436 c0 = *ptr; 4437 #endif 4438 if (has_mbyte) 4439 { 4440 mb_c = c; 4441 if (enc_utf8) 4442 { 4443 /* If the UTF-8 character is more than one byte: Decode it 4444 * into "mb_c". */ 4445 mb_l = utfc_ptr2len(ptr); 4446 mb_utf8 = FALSE; 4447 if (mb_l > 1) 4448 { 4449 mb_c = utfc_ptr2char(ptr, u8cc); 4450 /* Overlong encoded ASCII or ASCII with composing char 4451 * is displayed normally, except a NUL. */ 4452 if (mb_c < 0x80) 4453 { 4454 c = mb_c; 4455 #ifdef FEAT_LINEBREAK 4456 c0 = mb_c; 4457 #endif 4458 } 4459 mb_utf8 = TRUE; 4460 4461 /* At start of the line we can have a composing char. 4462 * Draw it as a space with a composing char. */ 4463 if (utf_iscomposing(mb_c)) 4464 { 4465 int i; 4466 4467 for (i = Screen_mco - 1; i > 0; --i) 4468 u8cc[i] = u8cc[i - 1]; 4469 u8cc[0] = mb_c; 4470 mb_c = ' '; 4471 } 4472 } 4473 4474 if ((mb_l == 1 && c >= 0x80) 4475 || (mb_l >= 1 && mb_c == 0) 4476 || (mb_l > 1 && (!vim_isprintc(mb_c)))) 4477 { 4478 /* 4479 * Illegal UTF-8 byte: display as <xx>. 4480 * Non-BMP character : display as ? or fullwidth ?. 4481 */ 4482 transchar_hex(extra, mb_c); 4483 # ifdef FEAT_RIGHTLEFT 4484 if (wp->w_p_rl) /* reverse */ 4485 rl_mirror(extra); 4486 # endif 4487 p_extra = extra; 4488 c = *p_extra; 4489 mb_c = mb_ptr2char_adv(&p_extra); 4490 mb_utf8 = (c >= 0x80); 4491 n_extra = (int)STRLEN(p_extra); 4492 c_extra = NUL; 4493 c_final = NUL; 4494 if (area_attr == 0 && search_attr == 0) 4495 { 4496 n_attr = n_extra + 1; 4497 extra_attr = HL_ATTR(HLF_8); 4498 saved_attr2 = char_attr; /* save current attr */ 4499 } 4500 } 4501 else if (mb_l == 0) /* at the NUL at end-of-line */ 4502 mb_l = 1; 4503 #ifdef FEAT_ARABIC 4504 else if (p_arshape && !p_tbidi && ARABIC_CHAR(mb_c)) 4505 { 4506 /* Do Arabic shaping. */ 4507 int pc, pc1, nc; 4508 int pcc[MAX_MCO]; 4509 4510 /* The idea of what is the previous and next 4511 * character depends on 'rightleft'. */ 4512 if (wp->w_p_rl) 4513 { 4514 pc = prev_c; 4515 pc1 = prev_c1; 4516 nc = utf_ptr2char(ptr + mb_l); 4517 prev_c1 = u8cc[0]; 4518 } 4519 else 4520 { 4521 pc = utfc_ptr2char(ptr + mb_l, pcc); 4522 nc = prev_c; 4523 pc1 = pcc[0]; 4524 } 4525 prev_c = mb_c; 4526 4527 mb_c = arabic_shape(mb_c, &c, &u8cc[0], pc, pc1, nc); 4528 } 4529 else 4530 prev_c = mb_c; 4531 #endif 4532 } 4533 else /* enc_dbcs */ 4534 { 4535 mb_l = MB_BYTE2LEN(c); 4536 if (mb_l == 0) /* at the NUL at end-of-line */ 4537 mb_l = 1; 4538 else if (mb_l > 1) 4539 { 4540 /* We assume a second byte below 32 is illegal. 4541 * Hopefully this is OK for all double-byte encodings! 4542 */ 4543 if (ptr[1] >= 32) 4544 mb_c = (c << 8) + ptr[1]; 4545 else 4546 { 4547 if (ptr[1] == NUL) 4548 { 4549 /* head byte at end of line */ 4550 mb_l = 1; 4551 transchar_nonprint(extra, c); 4552 } 4553 else 4554 { 4555 /* illegal tail byte */ 4556 mb_l = 2; 4557 STRCPY(extra, "XX"); 4558 } 4559 p_extra = extra; 4560 n_extra = (int)STRLEN(extra) - 1; 4561 c_extra = NUL; 4562 c_final = NUL; 4563 c = *p_extra++; 4564 if (area_attr == 0 && search_attr == 0) 4565 { 4566 n_attr = n_extra + 1; 4567 extra_attr = HL_ATTR(HLF_8); 4568 saved_attr2 = char_attr; /* save current attr */ 4569 } 4570 mb_c = c; 4571 } 4572 } 4573 } 4574 /* If a double-width char doesn't fit display a '>' in the 4575 * last column; the character is displayed at the start of the 4576 * next line. */ 4577 if (( 4578 # ifdef FEAT_RIGHTLEFT 4579 wp->w_p_rl ? (col <= 0) : 4580 # endif 4581 (col >= wp->w_width - 1)) 4582 && (*mb_char2cells)(mb_c) == 2) 4583 { 4584 c = '>'; 4585 mb_c = c; 4586 mb_utf8 = FALSE; 4587 mb_l = 1; 4588 multi_attr = HL_ATTR(HLF_AT); 4589 /* Put pointer back so that the character will be 4590 * displayed at the start of the next line. */ 4591 --ptr; 4592 } 4593 else if (*ptr != NUL) 4594 ptr += mb_l - 1; 4595 4596 /* If a double-width char doesn't fit at the left side display 4597 * a '<' in the first column. Don't do this for unprintable 4598 * characters. */ 4599 if (n_skip > 0 && mb_l > 1 && n_extra == 0) 4600 { 4601 n_extra = 1; 4602 c_extra = MB_FILLER_CHAR; 4603 c_final = NUL; 4604 c = ' '; 4605 if (area_attr == 0 && search_attr == 0) 4606 { 4607 n_attr = n_extra + 1; 4608 extra_attr = HL_ATTR(HLF_AT); 4609 saved_attr2 = char_attr; /* save current attr */ 4610 } 4611 mb_c = c; 4612 mb_utf8 = FALSE; 4613 mb_l = 1; 4614 } 4615 4616 } 4617 ++ptr; 4618 4619 if (extra_check) 4620 { 4621 #ifdef FEAT_SPELL 4622 int can_spell = TRUE; 4623 #endif 4624 4625 #ifdef FEAT_TERMINAL 4626 if (get_term_attr) 4627 { 4628 syntax_attr = term_get_attr(wp->w_buffer, lnum, vcol); 4629 4630 if (!attr_pri) 4631 char_attr = syntax_attr; 4632 else 4633 char_attr = hl_combine_attr(syntax_attr, char_attr); 4634 } 4635 #endif 4636 4637 #ifdef FEAT_SYN_HL 4638 // Get syntax attribute, unless still at the start of the line 4639 // (double-wide char that doesn't fit). 4640 v = (long)(ptr - line); 4641 if (has_syntax && v > 0) 4642 { 4643 /* Get the syntax attribute for the character. If there 4644 * is an error, disable syntax highlighting. */ 4645 save_did_emsg = did_emsg; 4646 did_emsg = FALSE; 4647 4648 syntax_attr = get_syntax_attr((colnr_T)v - 1, 4649 # ifdef FEAT_SPELL 4650 has_spell ? &can_spell : 4651 # endif 4652 NULL, FALSE); 4653 4654 if (did_emsg) 4655 { 4656 wp->w_s->b_syn_error = TRUE; 4657 has_syntax = FALSE; 4658 } 4659 else 4660 did_emsg = save_did_emsg; 4661 #ifdef SYN_TIME_LIMIT 4662 if (wp->w_s->b_syn_slow) 4663 has_syntax = FALSE; 4664 #endif 4665 4666 /* Need to get the line again, a multi-line regexp may 4667 * have made it invalid. */ 4668 line = ml_get_buf(wp->w_buffer, lnum, FALSE); 4669 ptr = line + v; 4670 4671 # ifdef FEAT_TEXT_PROP 4672 // Text properties overrule syntax highlighting or combine. 4673 if (text_prop_attr == 0 || text_prop_combine) 4674 # endif 4675 { 4676 int comb_attr = syntax_attr; 4677 # ifdef FEAT_TEXT_PROP 4678 comb_attr = hl_combine_attr(text_prop_attr, comb_attr); 4679 # endif 4680 if (!attr_pri) 4681 char_attr = comb_attr; 4682 else 4683 char_attr = hl_combine_attr(comb_attr, char_attr); 4684 } 4685 # ifdef FEAT_CONCEAL 4686 /* no concealing past the end of the line, it interferes 4687 * with line highlighting */ 4688 if (c == NUL) 4689 syntax_flags = 0; 4690 else 4691 syntax_flags = get_syntax_info(&syntax_seqnr); 4692 # endif 4693 } 4694 #endif 4695 4696 #ifdef FEAT_SPELL 4697 /* Check spelling (unless at the end of the line). 4698 * Only do this when there is no syntax highlighting, the 4699 * @Spell cluster is not used or the current syntax item 4700 * contains the @Spell cluster. */ 4701 if (has_spell && v >= word_end && v > cur_checked_col) 4702 { 4703 spell_attr = 0; 4704 if (c != 0 && ( 4705 # ifdef FEAT_SYN_HL 4706 !has_syntax || 4707 # endif 4708 can_spell)) 4709 { 4710 char_u *prev_ptr, *p; 4711 int len; 4712 hlf_T spell_hlf = HLF_COUNT; 4713 if (has_mbyte) 4714 { 4715 prev_ptr = ptr - mb_l; 4716 v -= mb_l - 1; 4717 } 4718 else 4719 prev_ptr = ptr - 1; 4720 4721 /* Use nextline[] if possible, it has the start of the 4722 * next line concatenated. */ 4723 if ((prev_ptr - line) - nextlinecol >= 0) 4724 p = nextline + (prev_ptr - line) - nextlinecol; 4725 else 4726 p = prev_ptr; 4727 cap_col -= (int)(prev_ptr - line); 4728 len = spell_check(wp, p, &spell_hlf, &cap_col, 4729 nochange); 4730 word_end = v + len; 4731 4732 /* In Insert mode only highlight a word that 4733 * doesn't touch the cursor. */ 4734 if (spell_hlf != HLF_COUNT 4735 && (State & INSERT) != 0 4736 && wp->w_cursor.lnum == lnum 4737 && wp->w_cursor.col >= 4738 (colnr_T)(prev_ptr - line) 4739 && wp->w_cursor.col < (colnr_T)word_end) 4740 { 4741 spell_hlf = HLF_COUNT; 4742 spell_redraw_lnum = lnum; 4743 } 4744 4745 if (spell_hlf == HLF_COUNT && p != prev_ptr 4746 && (p - nextline) + len > nextline_idx) 4747 { 4748 /* Remember that the good word continues at the 4749 * start of the next line. */ 4750 checked_lnum = lnum + 1; 4751 checked_col = (int)((p - nextline) + len - nextline_idx); 4752 } 4753 4754 /* Turn index into actual attributes. */ 4755 if (spell_hlf != HLF_COUNT) 4756 spell_attr = highlight_attr[spell_hlf]; 4757 4758 if (cap_col > 0) 4759 { 4760 if (p != prev_ptr 4761 && (p - nextline) + cap_col >= nextline_idx) 4762 { 4763 /* Remember that the word in the next line 4764 * must start with a capital. */ 4765 capcol_lnum = lnum + 1; 4766 cap_col = (int)((p - nextline) + cap_col 4767 - nextline_idx); 4768 } 4769 else 4770 /* Compute the actual column. */ 4771 cap_col += (int)(prev_ptr - line); 4772 } 4773 } 4774 } 4775 if (spell_attr != 0) 4776 { 4777 if (!attr_pri) 4778 char_attr = hl_combine_attr(char_attr, spell_attr); 4779 else 4780 char_attr = hl_combine_attr(spell_attr, char_attr); 4781 } 4782 #endif 4783 #ifdef FEAT_LINEBREAK 4784 /* 4785 * Found last space before word: check for line break. 4786 */ 4787 if (wp->w_p_lbr && c0 == c 4788 && VIM_ISBREAK(c) && !VIM_ISBREAK((int)*ptr)) 4789 { 4790 int mb_off = has_mbyte ? (*mb_head_off)(line, ptr - 1) : 0; 4791 char_u *p = ptr - (mb_off + 1); 4792 4793 /* TODO: is passing p for start of the line OK? */ 4794 n_extra = win_lbr_chartabsize(wp, line, p, (colnr_T)vcol, 4795 NULL) - 1; 4796 if (c == TAB && n_extra + col > wp->w_width) 4797 # ifdef FEAT_VARTABS 4798 n_extra = tabstop_padding(vcol, wp->w_buffer->b_p_ts, 4799 wp->w_buffer->b_p_vts_array) - 1; 4800 # else 4801 n_extra = (int)wp->w_buffer->b_p_ts 4802 - vcol % (int)wp->w_buffer->b_p_ts - 1; 4803 # endif 4804 4805 c_extra = mb_off > 0 ? MB_FILLER_CHAR : ' '; 4806 c_final = NUL; 4807 if (VIM_ISWHITE(c)) 4808 { 4809 #ifdef FEAT_CONCEAL 4810 if (c == TAB) 4811 /* See "Tab alignment" below. */ 4812 FIX_FOR_BOGUSCOLS; 4813 #endif 4814 if (!wp->w_p_list) 4815 c = ' '; 4816 } 4817 } 4818 #endif 4819 4820 // 'list': Change char 160 to lcs_nbsp and space to lcs_space. 4821 // But not when the character is followed by a composing 4822 // character (use mb_l to check that). 4823 if (wp->w_p_list 4824 && ((((c == 160 && mb_l == 1) 4825 || (mb_utf8 4826 && ((mb_c == 160 && mb_l == 2) 4827 || (mb_c == 0x202f && mb_l == 3)))) 4828 && lcs_nbsp) 4829 || (c == ' ' 4830 && mb_l == 1 4831 && lcs_space 4832 && ptr - line <= trailcol))) 4833 { 4834 c = (c == ' ') ? lcs_space : lcs_nbsp; 4835 if (area_attr == 0 && search_attr == 0) 4836 { 4837 n_attr = 1; 4838 extra_attr = HL_ATTR(HLF_8); 4839 saved_attr2 = char_attr; /* save current attr */ 4840 } 4841 mb_c = c; 4842 if (enc_utf8 && utf_char2len(c) > 1) 4843 { 4844 mb_utf8 = TRUE; 4845 u8cc[0] = 0; 4846 c = 0xc0; 4847 } 4848 else 4849 mb_utf8 = FALSE; 4850 } 4851 4852 if (trailcol != MAXCOL && ptr > line + trailcol && c == ' ') 4853 { 4854 c = lcs_trail; 4855 if (!attr_pri) 4856 { 4857 n_attr = 1; 4858 extra_attr = HL_ATTR(HLF_8); 4859 saved_attr2 = char_attr; /* save current attr */ 4860 } 4861 mb_c = c; 4862 if (enc_utf8 && utf_char2len(c) > 1) 4863 { 4864 mb_utf8 = TRUE; 4865 u8cc[0] = 0; 4866 c = 0xc0; 4867 } 4868 else 4869 mb_utf8 = FALSE; 4870 } 4871 } 4872 4873 /* 4874 * Handling of non-printable characters. 4875 */ 4876 if (!vim_isprintc(c)) 4877 { 4878 /* 4879 * when getting a character from the file, we may have to 4880 * turn it into something else on the way to putting it 4881 * into "ScreenLines". 4882 */ 4883 if (c == TAB && (!wp->w_p_list || lcs_tab1)) 4884 { 4885 int tab_len = 0; 4886 long vcol_adjusted = vcol; /* removed showbreak length */ 4887 #ifdef FEAT_LINEBREAK 4888 /* only adjust the tab_len, when at the first column 4889 * after the showbreak value was drawn */ 4890 if (*p_sbr != NUL && vcol == vcol_sbr && wp->w_p_wrap) 4891 vcol_adjusted = vcol - MB_CHARLEN(p_sbr); 4892 #endif 4893 /* tab amount depends on current column */ 4894 #ifdef FEAT_VARTABS 4895 tab_len = tabstop_padding(vcol_adjusted, 4896 wp->w_buffer->b_p_ts, 4897 wp->w_buffer->b_p_vts_array) - 1; 4898 #else 4899 tab_len = (int)wp->w_buffer->b_p_ts 4900 - vcol_adjusted % (int)wp->w_buffer->b_p_ts - 1; 4901 #endif 4902 4903 #ifdef FEAT_LINEBREAK 4904 if (!wp->w_p_lbr || !wp->w_p_list) 4905 #endif 4906 /* tab amount depends on current column */ 4907 n_extra = tab_len; 4908 #ifdef FEAT_LINEBREAK 4909 else 4910 { 4911 char_u *p; 4912 int len; 4913 int i; 4914 int saved_nextra = n_extra; 4915 4916 #ifdef FEAT_CONCEAL 4917 if (vcol_off > 0) 4918 /* there are characters to conceal */ 4919 tab_len += vcol_off; 4920 /* boguscols before FIX_FOR_BOGUSCOLS macro from above 4921 */ 4922 if (wp->w_p_list && lcs_tab1 && old_boguscols > 0 4923 && n_extra > tab_len) 4924 tab_len += n_extra - tab_len; 4925 #endif 4926 4927 /* if n_extra > 0, it gives the number of chars, to 4928 * use for a tab, else we need to calculate the width 4929 * for a tab */ 4930 len = (tab_len * mb_char2len(lcs_tab2)); 4931 if (n_extra > 0) 4932 len += n_extra - tab_len; 4933 c = lcs_tab1; 4934 p = alloc((unsigned)(len + 1)); 4935 vim_memset(p, ' ', len); 4936 p[len] = NUL; 4937 vim_free(p_extra_free); 4938 p_extra_free = p; 4939 for (i = 0; i < tab_len; i++) 4940 { 4941 if (*p == NUL) 4942 { 4943 tab_len = i; 4944 break; 4945 } 4946 mb_char2bytes(lcs_tab2, p); 4947 p += mb_char2len(lcs_tab2); 4948 n_extra += mb_char2len(lcs_tab2) 4949 - (saved_nextra > 0 ? 1 : 0); 4950 } 4951 p_extra = p_extra_free; 4952 #ifdef FEAT_CONCEAL 4953 /* n_extra will be increased by FIX_FOX_BOGUSCOLS 4954 * macro below, so need to adjust for that here */ 4955 if (vcol_off > 0) 4956 n_extra -= vcol_off; 4957 #endif 4958 } 4959 #endif 4960 #ifdef FEAT_CONCEAL 4961 { 4962 int vc_saved = vcol_off; 4963 4964 /* Tab alignment should be identical regardless of 4965 * 'conceallevel' value. So tab compensates of all 4966 * previous concealed characters, and thus resets 4967 * vcol_off and boguscols accumulated so far in the 4968 * line. Note that the tab can be longer than 4969 * 'tabstop' when there are concealed characters. */ 4970 FIX_FOR_BOGUSCOLS; 4971 4972 /* Make sure, the highlighting for the tab char will be 4973 * correctly set further below (effectively reverts the 4974 * FIX_FOR_BOGSUCOLS macro */ 4975 if (n_extra == tab_len + vc_saved && wp->w_p_list 4976 && lcs_tab1) 4977 tab_len += vc_saved; 4978 } 4979 #endif 4980 mb_utf8 = FALSE; /* don't draw as UTF-8 */ 4981 if (wp->w_p_list) 4982 { 4983 c = (n_extra == 0 && lcs_tab3) ? lcs_tab3 : lcs_tab1; 4984 #ifdef FEAT_LINEBREAK 4985 if (wp->w_p_lbr) 4986 c_extra = NUL; /* using p_extra from above */ 4987 else 4988 #endif 4989 c_extra = lcs_tab2; 4990 c_final = lcs_tab3; 4991 n_attr = tab_len + 1; 4992 extra_attr = HL_ATTR(HLF_8); 4993 saved_attr2 = char_attr; /* save current attr */ 4994 mb_c = c; 4995 if (enc_utf8 && utf_char2len(c) > 1) 4996 { 4997 mb_utf8 = TRUE; 4998 u8cc[0] = 0; 4999 c = 0xc0; 5000 } 5001 } 5002 else 5003 { 5004 c_final = NUL; 5005 c_extra = ' '; 5006 c = ' '; 5007 } 5008 } 5009 else if (c == NUL 5010 && (wp->w_p_list 5011 || ((fromcol >= 0 || fromcol_prev >= 0) 5012 && tocol > vcol 5013 && VIsual_mode != Ctrl_V 5014 && ( 5015 # ifdef FEAT_RIGHTLEFT 5016 wp->w_p_rl ? (col >= 0) : 5017 # endif 5018 (col < wp->w_width)) 5019 && !(noinvcur 5020 && lnum == wp->w_cursor.lnum 5021 && (colnr_T)vcol == wp->w_virtcol))) 5022 && lcs_eol_one > 0) 5023 { 5024 /* Display a '$' after the line or highlight an extra 5025 * character if the line break is included. */ 5026 #if defined(FEAT_DIFF) || defined(LINE_ATTR) 5027 /* For a diff line the highlighting continues after the 5028 * "$". */ 5029 if ( 5030 # ifdef FEAT_DIFF 5031 diff_hlf == (hlf_T)0 5032 # ifdef LINE_ATTR 5033 && 5034 # endif 5035 # endif 5036 # ifdef LINE_ATTR 5037 line_attr == 0 5038 # endif 5039 ) 5040 #endif 5041 { 5042 /* In virtualedit, visual selections may extend 5043 * beyond end of line. */ 5044 if (area_highlighting && virtual_active() 5045 && tocol != MAXCOL && vcol < tocol) 5046 n_extra = 0; 5047 else 5048 { 5049 p_extra = at_end_str; 5050 n_extra = 1; 5051 c_extra = NUL; 5052 c_final = NUL; 5053 } 5054 } 5055 if (wp->w_p_list && lcs_eol > 0) 5056 c = lcs_eol; 5057 else 5058 c = ' '; 5059 lcs_eol_one = -1; 5060 --ptr; /* put it back at the NUL */ 5061 if (!attr_pri) 5062 { 5063 extra_attr = HL_ATTR(HLF_AT); 5064 n_attr = 1; 5065 } 5066 mb_c = c; 5067 if (enc_utf8 && utf_char2len(c) > 1) 5068 { 5069 mb_utf8 = TRUE; 5070 u8cc[0] = 0; 5071 c = 0xc0; 5072 } 5073 else 5074 mb_utf8 = FALSE; /* don't draw as UTF-8 */ 5075 } 5076 else if (c != NUL) 5077 { 5078 p_extra = transchar(c); 5079 if (n_extra == 0) 5080 n_extra = byte2cells(c) - 1; 5081 #ifdef FEAT_RIGHTLEFT 5082 if ((dy_flags & DY_UHEX) && wp->w_p_rl) 5083 rl_mirror(p_extra); /* reverse "<12>" */ 5084 #endif 5085 c_extra = NUL; 5086 c_final = NUL; 5087 #ifdef FEAT_LINEBREAK 5088 if (wp->w_p_lbr) 5089 { 5090 char_u *p; 5091 5092 c = *p_extra; 5093 p = alloc((unsigned)n_extra + 1); 5094 vim_memset(p, ' ', n_extra); 5095 STRNCPY(p, p_extra + 1, STRLEN(p_extra) - 1); 5096 p[n_extra] = NUL; 5097 vim_free(p_extra_free); 5098 p_extra_free = p_extra = p; 5099 } 5100 else 5101 #endif 5102 { 5103 n_extra = byte2cells(c) - 1; 5104 c = *p_extra++; 5105 } 5106 if (!attr_pri) 5107 { 5108 n_attr = n_extra + 1; 5109 extra_attr = HL_ATTR(HLF_8); 5110 saved_attr2 = char_attr; /* save current attr */ 5111 } 5112 mb_utf8 = FALSE; /* don't draw as UTF-8 */ 5113 } 5114 else if (VIsual_active 5115 && (VIsual_mode == Ctrl_V 5116 || VIsual_mode == 'v') 5117 && virtual_active() 5118 && tocol != MAXCOL 5119 && vcol < tocol 5120 && ( 5121 #ifdef FEAT_RIGHTLEFT 5122 wp->w_p_rl ? (col >= 0) : 5123 #endif 5124 (col < wp->w_width))) 5125 { 5126 c = ' '; 5127 --ptr; /* put it back at the NUL */ 5128 } 5129 #if defined(LINE_ATTR) 5130 else if (( 5131 # ifdef FEAT_DIFF 5132 diff_hlf != (hlf_T)0 || 5133 # endif 5134 # ifdef FEAT_TERMINAL 5135 term_attr != 0 || 5136 # endif 5137 line_attr != 0 5138 ) && ( 5139 # ifdef FEAT_RIGHTLEFT 5140 wp->w_p_rl ? (col >= 0) : 5141 # endif 5142 (col 5143 # ifdef FEAT_CONCEAL 5144 - boguscols 5145 # endif 5146 < wp->w_width))) 5147 { 5148 /* Highlight until the right side of the window */ 5149 c = ' '; 5150 --ptr; /* put it back at the NUL */ 5151 5152 /* Remember we do the char for line highlighting. */ 5153 ++did_line_attr; 5154 5155 /* don't do search HL for the rest of the line */ 5156 if (line_attr != 0 && char_attr == search_attr 5157 && (did_line_attr > 1 5158 || (wp->w_p_list && lcs_eol > 0))) 5159 char_attr = line_attr; 5160 # ifdef FEAT_DIFF 5161 if (diff_hlf == HLF_TXD) 5162 { 5163 diff_hlf = HLF_CHD; 5164 if (attr == 0 || char_attr != attr) 5165 { 5166 char_attr = HL_ATTR(diff_hlf); 5167 if (wp->w_p_cul && lnum == wp->w_cursor.lnum) 5168 char_attr = hl_combine_attr(char_attr, 5169 HL_ATTR(HLF_CUL)); 5170 } 5171 } 5172 # endif 5173 # ifdef FEAT_TERMINAL 5174 if (term_attr != 0) 5175 { 5176 char_attr = term_attr; 5177 if (wp->w_p_cul && lnum == wp->w_cursor.lnum) 5178 char_attr = hl_combine_attr(char_attr, 5179 HL_ATTR(HLF_CUL)); 5180 } 5181 # endif 5182 } 5183 #endif 5184 } 5185 5186 #ifdef FEAT_CONCEAL 5187 if ( wp->w_p_cole > 0 5188 && (wp != curwin || lnum != wp->w_cursor.lnum || 5189 conceal_cursor_line(wp)) 5190 && ((syntax_flags & HL_CONCEAL) != 0 || has_match_conc > 0) 5191 && !(lnum_in_visual_area 5192 && vim_strchr(wp->w_p_cocu, 'v') == NULL)) 5193 { 5194 char_attr = conceal_attr; 5195 if ((prev_syntax_id != syntax_seqnr || has_match_conc > 1) 5196 && (syn_get_sub_char() != NUL || match_conc 5197 || wp->w_p_cole == 1) 5198 && wp->w_p_cole != 3) 5199 { 5200 /* First time at this concealed item: display one 5201 * character. */ 5202 if (match_conc) 5203 c = match_conc; 5204 else if (syn_get_sub_char() != NUL) 5205 c = syn_get_sub_char(); 5206 else if (lcs_conceal != NUL) 5207 c = lcs_conceal; 5208 else 5209 c = ' '; 5210 5211 prev_syntax_id = syntax_seqnr; 5212 5213 if (n_extra > 0) 5214 vcol_off += n_extra; 5215 vcol += n_extra; 5216 if (wp->w_p_wrap && n_extra > 0) 5217 { 5218 # ifdef FEAT_RIGHTLEFT 5219 if (wp->w_p_rl) 5220 { 5221 col -= n_extra; 5222 boguscols -= n_extra; 5223 } 5224 else 5225 # endif 5226 { 5227 boguscols += n_extra; 5228 col += n_extra; 5229 } 5230 } 5231 n_extra = 0; 5232 n_attr = 0; 5233 } 5234 else if (n_skip == 0) 5235 { 5236 is_concealing = TRUE; 5237 n_skip = 1; 5238 } 5239 mb_c = c; 5240 if (enc_utf8 && utf_char2len(c) > 1) 5241 { 5242 mb_utf8 = TRUE; 5243 u8cc[0] = 0; 5244 c = 0xc0; 5245 } 5246 else 5247 mb_utf8 = FALSE; /* don't draw as UTF-8 */ 5248 } 5249 else 5250 { 5251 prev_syntax_id = 0; 5252 is_concealing = FALSE; 5253 } 5254 #endif /* FEAT_CONCEAL */ 5255 } 5256 5257 #ifdef FEAT_CONCEAL 5258 /* In the cursor line and we may be concealing characters: correct 5259 * the cursor column when we reach its position. */ 5260 if (!did_wcol && draw_state == WL_LINE 5261 && wp == curwin && lnum == wp->w_cursor.lnum 5262 && conceal_cursor_line(wp) 5263 && (int)wp->w_virtcol <= vcol + n_skip) 5264 { 5265 # ifdef FEAT_RIGHTLEFT 5266 if (wp->w_p_rl) 5267 wp->w_wcol = wp->w_width - col + boguscols - 1; 5268 else 5269 # endif 5270 wp->w_wcol = col - boguscols; 5271 wp->w_wrow = row; 5272 did_wcol = TRUE; 5273 } 5274 #endif 5275 5276 /* Don't override visual selection highlighting. */ 5277 if (n_attr > 0 5278 && draw_state == WL_LINE 5279 && !attr_pri) 5280 char_attr = extra_attr; 5281 5282 #if defined(FEAT_XIM) && defined(FEAT_GUI_GTK) 5283 /* XIM don't send preedit_start and preedit_end, but they send 5284 * preedit_changed and commit. Thus Vim can't set "im_is_active", use 5285 * im_is_preediting() here. */ 5286 if (p_imst == IM_ON_THE_SPOT 5287 && xic != NULL 5288 && lnum == wp->w_cursor.lnum 5289 && (State & INSERT) 5290 && !p_imdisable 5291 && im_is_preediting() 5292 && draw_state == WL_LINE) 5293 { 5294 colnr_T tcol; 5295 5296 if (preedit_end_col == MAXCOL) 5297 getvcol(curwin, &(wp->w_cursor), &tcol, NULL, NULL); 5298 else 5299 tcol = preedit_end_col; 5300 if ((long)preedit_start_col <= vcol && vcol < (long)tcol) 5301 { 5302 if (feedback_old_attr < 0) 5303 { 5304 feedback_col = 0; 5305 feedback_old_attr = char_attr; 5306 } 5307 char_attr = im_get_feedback_attr(feedback_col); 5308 if (char_attr < 0) 5309 char_attr = feedback_old_attr; 5310 feedback_col++; 5311 } 5312 else if (feedback_old_attr >= 0) 5313 { 5314 char_attr = feedback_old_attr; 5315 feedback_old_attr = -1; 5316 feedback_col = 0; 5317 } 5318 } 5319 #endif 5320 /* 5321 * Handle the case where we are in column 0 but not on the first 5322 * character of the line and the user wants us to show us a 5323 * special character (via 'listchars' option "precedes:<char>". 5324 */ 5325 if (lcs_prec_todo != NUL 5326 && wp->w_p_list 5327 && (wp->w_p_wrap ? wp->w_skipcol > 0 : wp->w_leftcol > 0) 5328 #ifdef FEAT_DIFF 5329 && filler_todo <= 0 5330 #endif 5331 && draw_state > WL_NR 5332 && c != NUL) 5333 { 5334 c = lcs_prec; 5335 lcs_prec_todo = NUL; 5336 if (has_mbyte && (*mb_char2cells)(mb_c) > 1) 5337 { 5338 /* Double-width character being overwritten by the "precedes" 5339 * character, need to fill up half the character. */ 5340 c_extra = MB_FILLER_CHAR; 5341 c_final = NUL; 5342 n_extra = 1; 5343 n_attr = 2; 5344 extra_attr = HL_ATTR(HLF_AT); 5345 } 5346 mb_c = c; 5347 if (enc_utf8 && utf_char2len(c) > 1) 5348 { 5349 mb_utf8 = TRUE; 5350 u8cc[0] = 0; 5351 c = 0xc0; 5352 } 5353 else 5354 mb_utf8 = FALSE; /* don't draw as UTF-8 */ 5355 if (!attr_pri) 5356 { 5357 saved_attr3 = char_attr; /* save current attr */ 5358 char_attr = HL_ATTR(HLF_AT); /* later copied to char_attr */ 5359 n_attr3 = 1; 5360 } 5361 } 5362 5363 /* 5364 * At end of the text line or just after the last character. 5365 */ 5366 if (c == NUL 5367 #if defined(LINE_ATTR) 5368 || did_line_attr == 1 5369 #endif 5370 ) 5371 { 5372 #ifdef FEAT_SEARCH_EXTRA 5373 long prevcol = (long)(ptr - line) - (c == NUL); 5374 5375 /* we're not really at that column when skipping some text */ 5376 if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol) 5377 ++prevcol; 5378 #endif 5379 5380 /* Invert at least one char, used for Visual and empty line or 5381 * highlight match at end of line. If it's beyond the last 5382 * char on the screen, just overwrite that one (tricky!) Not 5383 * needed when a '$' was displayed for 'list'. */ 5384 #ifdef FEAT_SEARCH_EXTRA 5385 prevcol_hl_flag = FALSE; 5386 if (!search_hl.is_addpos && prevcol == (long)search_hl.startcol) 5387 prevcol_hl_flag = TRUE; 5388 else 5389 { 5390 cur = wp->w_match_head; 5391 while (cur != NULL) 5392 { 5393 if (!cur->hl.is_addpos && prevcol == (long)cur->hl.startcol) 5394 { 5395 prevcol_hl_flag = TRUE; 5396 break; 5397 } 5398 cur = cur->next; 5399 } 5400 } 5401 #endif 5402 if (lcs_eol == lcs_eol_one 5403 && ((area_attr != 0 && vcol == fromcol 5404 && (VIsual_mode != Ctrl_V 5405 || lnum == VIsual.lnum 5406 || lnum == curwin->w_cursor.lnum) 5407 && c == NUL) 5408 #ifdef FEAT_SEARCH_EXTRA 5409 /* highlight 'hlsearch' match at end of line */ 5410 || (prevcol_hl_flag == TRUE 5411 # ifdef FEAT_SYN_HL 5412 && !(wp->w_p_cul && lnum == wp->w_cursor.lnum 5413 && !(wp == curwin && VIsual_active)) 5414 # endif 5415 # ifdef FEAT_DIFF 5416 && diff_hlf == (hlf_T)0 5417 # endif 5418 # if defined(LINE_ATTR) 5419 && did_line_attr <= 1 5420 # endif 5421 ) 5422 #endif 5423 )) 5424 { 5425 int n = 0; 5426 5427 #ifdef FEAT_RIGHTLEFT 5428 if (wp->w_p_rl) 5429 { 5430 if (col < 0) 5431 n = 1; 5432 } 5433 else 5434 #endif 5435 { 5436 if (col >= wp->w_width) 5437 n = -1; 5438 } 5439 if (n != 0) 5440 { 5441 /* At the window boundary, highlight the last character 5442 * instead (better than nothing). */ 5443 off += n; 5444 col += n; 5445 } 5446 else 5447 { 5448 /* Add a blank character to highlight. */ 5449 ScreenLines[off] = ' '; 5450 if (enc_utf8) 5451 ScreenLinesUC[off] = 0; 5452 } 5453 #ifdef FEAT_SEARCH_EXTRA 5454 if (area_attr == 0) 5455 { 5456 /* Use attributes from match with highest priority among 5457 * 'search_hl' and the match list. */ 5458 char_attr = search_hl.attr; 5459 cur = wp->w_match_head; 5460 shl_flag = FALSE; 5461 while (cur != NULL || shl_flag == FALSE) 5462 { 5463 if (shl_flag == FALSE 5464 && ((cur != NULL 5465 && cur->priority > SEARCH_HL_PRIORITY) 5466 || cur == NULL)) 5467 { 5468 shl = &search_hl; 5469 shl_flag = TRUE; 5470 } 5471 else 5472 shl = &cur->hl; 5473 if ((ptr - line) - 1 == (long)shl->startcol 5474 && (shl == &search_hl || !shl->is_addpos)) 5475 char_attr = shl->attr; 5476 if (shl != &search_hl && cur != NULL) 5477 cur = cur->next; 5478 } 5479 } 5480 #endif 5481 ScreenAttrs[off] = char_attr; 5482 #ifdef FEAT_RIGHTLEFT 5483 if (wp->w_p_rl) 5484 { 5485 --col; 5486 --off; 5487 } 5488 else 5489 #endif 5490 { 5491 ++col; 5492 ++off; 5493 } 5494 ++vcol; 5495 #ifdef FEAT_SYN_HL 5496 eol_hl_off = 1; 5497 #endif 5498 } 5499 } 5500 5501 /* 5502 * At end of the text line. 5503 */ 5504 if (c == NUL) 5505 { 5506 #ifdef FEAT_SYN_HL 5507 /* Highlight 'cursorcolumn' & 'colorcolumn' past end of the line. */ 5508 if (wp->w_p_wrap) 5509 v = wp->w_skipcol; 5510 else 5511 v = wp->w_leftcol; 5512 5513 /* check if line ends before left margin */ 5514 if (vcol < v + col - win_col_off(wp)) 5515 vcol = v + col - win_col_off(wp); 5516 #ifdef FEAT_CONCEAL 5517 // Get rid of the boguscols now, we want to draw until the right 5518 // edge for 'cursorcolumn'. 5519 col -= boguscols; 5520 boguscols = 0; 5521 #endif 5522 5523 if (draw_color_col) 5524 draw_color_col = advance_color_col(VCOL_HLC, &color_cols); 5525 5526 if (((wp->w_p_cuc 5527 && (int)wp->w_virtcol >= VCOL_HLC - eol_hl_off 5528 && (int)wp->w_virtcol < 5529 wp->w_width * (row - startrow + 1) + v 5530 && lnum != wp->w_cursor.lnum) 5531 || draw_color_col) 5532 # ifdef FEAT_RIGHTLEFT 5533 && !wp->w_p_rl 5534 # endif 5535 ) 5536 { 5537 int rightmost_vcol = 0; 5538 int i; 5539 5540 if (wp->w_p_cuc) 5541 rightmost_vcol = wp->w_virtcol; 5542 if (draw_color_col) 5543 /* determine rightmost colorcolumn to possibly draw */ 5544 for (i = 0; color_cols[i] >= 0; ++i) 5545 if (rightmost_vcol < color_cols[i]) 5546 rightmost_vcol = color_cols[i]; 5547 5548 while (col < wp->w_width) 5549 { 5550 ScreenLines[off] = ' '; 5551 if (enc_utf8) 5552 ScreenLinesUC[off] = 0; 5553 ++col; 5554 if (draw_color_col) 5555 draw_color_col = advance_color_col(VCOL_HLC, 5556 &color_cols); 5557 5558 if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol) 5559 ScreenAttrs[off++] = HL_ATTR(HLF_CUC); 5560 else if (draw_color_col && VCOL_HLC == *color_cols) 5561 ScreenAttrs[off++] = HL_ATTR(HLF_MC); 5562 else 5563 ScreenAttrs[off++] = 0; 5564 5565 if (VCOL_HLC >= rightmost_vcol) 5566 break; 5567 5568 ++vcol; 5569 } 5570 } 5571 #endif 5572 5573 screen_line(screen_row, wp->w_wincol, col, 5574 (int)wp->w_width, HAS_RIGHTLEFT(wp->w_p_rl)); 5575 row++; 5576 5577 /* 5578 * Update w_cline_height and w_cline_folded if the cursor line was 5579 * updated (saves a call to plines() later). 5580 */ 5581 if (wp == curwin && lnum == curwin->w_cursor.lnum) 5582 { 5583 curwin->w_cline_row = startrow; 5584 curwin->w_cline_height = row - startrow; 5585 #ifdef FEAT_FOLDING 5586 curwin->w_cline_folded = FALSE; 5587 #endif 5588 curwin->w_valid |= (VALID_CHEIGHT|VALID_CROW); 5589 } 5590 5591 break; 5592 } 5593 5594 // Show "extends" character from 'listchars' if beyond the line end and 5595 // 'list' is set. 5596 if (lcs_ext != NUL 5597 && wp->w_p_list 5598 && !wp->w_p_wrap 5599 #ifdef FEAT_DIFF 5600 && filler_todo <= 0 5601 #endif 5602 && ( 5603 #ifdef FEAT_RIGHTLEFT 5604 wp->w_p_rl ? col == 0 : 5605 #endif 5606 col == wp->w_width - 1) 5607 && (*ptr != NUL 5608 || (wp->w_p_list && lcs_eol_one > 0) 5609 || (n_extra && (c_extra != NUL || *p_extra != NUL)))) 5610 { 5611 c = lcs_ext; 5612 char_attr = HL_ATTR(HLF_AT); 5613 mb_c = c; 5614 if (enc_utf8 && utf_char2len(c) > 1) 5615 { 5616 mb_utf8 = TRUE; 5617 u8cc[0] = 0; 5618 c = 0xc0; 5619 } 5620 else 5621 mb_utf8 = FALSE; 5622 } 5623 5624 #ifdef FEAT_SYN_HL 5625 /* advance to the next 'colorcolumn' */ 5626 if (draw_color_col) 5627 draw_color_col = advance_color_col(VCOL_HLC, &color_cols); 5628 5629 /* Highlight the cursor column if 'cursorcolumn' is set. But don't 5630 * highlight the cursor position itself. 5631 * Also highlight the 'colorcolumn' if it is different than 5632 * 'cursorcolumn' */ 5633 vcol_save_attr = -1; 5634 if (draw_state == WL_LINE && !lnum_in_visual_area 5635 && search_attr == 0 && area_attr == 0) 5636 { 5637 if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol 5638 && lnum != wp->w_cursor.lnum) 5639 { 5640 vcol_save_attr = char_attr; 5641 char_attr = hl_combine_attr(char_attr, HL_ATTR(HLF_CUC)); 5642 } 5643 else if (draw_color_col && VCOL_HLC == *color_cols) 5644 { 5645 vcol_save_attr = char_attr; 5646 char_attr = hl_combine_attr(char_attr, HL_ATTR(HLF_MC)); 5647 } 5648 } 5649 #endif 5650 5651 /* 5652 * Store character to be displayed. 5653 * Skip characters that are left of the screen for 'nowrap'. 5654 */ 5655 vcol_prev = vcol; 5656 if (draw_state < WL_LINE || n_skip <= 0) 5657 { 5658 /* 5659 * Store the character. 5660 */ 5661 #if defined(FEAT_RIGHTLEFT) 5662 if (has_mbyte && wp->w_p_rl && (*mb_char2cells)(mb_c) > 1) 5663 { 5664 /* A double-wide character is: put first halve in left cell. */ 5665 --off; 5666 --col; 5667 } 5668 #endif 5669 ScreenLines[off] = c; 5670 if (enc_dbcs == DBCS_JPNU) 5671 { 5672 if ((mb_c & 0xff00) == 0x8e00) 5673 ScreenLines[off] = 0x8e; 5674 ScreenLines2[off] = mb_c & 0xff; 5675 } 5676 else if (enc_utf8) 5677 { 5678 if (mb_utf8) 5679 { 5680 int i; 5681 5682 ScreenLinesUC[off] = mb_c; 5683 if ((c & 0xff) == 0) 5684 ScreenLines[off] = 0x80; /* avoid storing zero */ 5685 for (i = 0; i < Screen_mco; ++i) 5686 { 5687 ScreenLinesC[i][off] = u8cc[i]; 5688 if (u8cc[i] == 0) 5689 break; 5690 } 5691 } 5692 else 5693 ScreenLinesUC[off] = 0; 5694 } 5695 if (multi_attr) 5696 { 5697 ScreenAttrs[off] = multi_attr; 5698 multi_attr = 0; 5699 } 5700 else 5701 ScreenAttrs[off] = char_attr; 5702 5703 if (has_mbyte && (*mb_char2cells)(mb_c) > 1) 5704 { 5705 /* Need to fill two screen columns. */ 5706 ++off; 5707 ++col; 5708 if (enc_utf8) 5709 /* UTF-8: Put a 0 in the second screen char. */ 5710 ScreenLines[off] = 0; 5711 else 5712 /* DBCS: Put second byte in the second screen char. */ 5713 ScreenLines[off] = mb_c & 0xff; 5714 if (draw_state > WL_NR 5715 #ifdef FEAT_DIFF 5716 && filler_todo <= 0 5717 #endif 5718 ) 5719 ++vcol; 5720 /* When "tocol" is halfway a character, set it to the end of 5721 * the character, otherwise highlighting won't stop. */ 5722 if (tocol == vcol) 5723 ++tocol; 5724 #ifdef FEAT_RIGHTLEFT 5725 if (wp->w_p_rl) 5726 { 5727 /* now it's time to backup one cell */ 5728 --off; 5729 --col; 5730 } 5731 #endif 5732 } 5733 #ifdef FEAT_RIGHTLEFT 5734 if (wp->w_p_rl) 5735 { 5736 --off; 5737 --col; 5738 } 5739 else 5740 #endif 5741 { 5742 ++off; 5743 ++col; 5744 } 5745 } 5746 #ifdef FEAT_CONCEAL 5747 else if (wp->w_p_cole > 0 && is_concealing) 5748 { 5749 --n_skip; 5750 ++vcol_off; 5751 if (n_extra > 0) 5752 vcol_off += n_extra; 5753 if (wp->w_p_wrap) 5754 { 5755 /* 5756 * Special voodoo required if 'wrap' is on. 5757 * 5758 * Advance the column indicator to force the line 5759 * drawing to wrap early. This will make the line 5760 * take up the same screen space when parts are concealed, 5761 * so that cursor line computations aren't messed up. 5762 * 5763 * To avoid the fictitious advance of 'col' causing 5764 * trailing junk to be written out of the screen line 5765 * we are building, 'boguscols' keeps track of the number 5766 * of bad columns we have advanced. 5767 */ 5768 if (n_extra > 0) 5769 { 5770 vcol += n_extra; 5771 # ifdef FEAT_RIGHTLEFT 5772 if (wp->w_p_rl) 5773 { 5774 col -= n_extra; 5775 boguscols -= n_extra; 5776 } 5777 else 5778 # endif 5779 { 5780 col += n_extra; 5781 boguscols += n_extra; 5782 } 5783 n_extra = 0; 5784 n_attr = 0; 5785 } 5786 5787 5788 if (has_mbyte && (*mb_char2cells)(mb_c) > 1) 5789 { 5790 /* Need to fill two screen columns. */ 5791 # ifdef FEAT_RIGHTLEFT 5792 if (wp->w_p_rl) 5793 { 5794 --boguscols; 5795 --col; 5796 } 5797 else 5798 # endif 5799 { 5800 ++boguscols; 5801 ++col; 5802 } 5803 } 5804 5805 # ifdef FEAT_RIGHTLEFT 5806 if (wp->w_p_rl) 5807 { 5808 --boguscols; 5809 --col; 5810 } 5811 else 5812 # endif 5813 { 5814 ++boguscols; 5815 ++col; 5816 } 5817 } 5818 else 5819 { 5820 if (n_extra > 0) 5821 { 5822 vcol += n_extra; 5823 n_extra = 0; 5824 n_attr = 0; 5825 } 5826 } 5827 5828 } 5829 #endif /* FEAT_CONCEAL */ 5830 else 5831 --n_skip; 5832 5833 /* Only advance the "vcol" when after the 'number' or 'relativenumber' 5834 * column. */ 5835 if (draw_state > WL_NR 5836 #ifdef FEAT_DIFF 5837 && filler_todo <= 0 5838 #endif 5839 ) 5840 ++vcol; 5841 5842 #ifdef FEAT_SYN_HL 5843 if (vcol_save_attr >= 0) 5844 char_attr = vcol_save_attr; 5845 #endif 5846 5847 /* restore attributes after "predeces" in 'listchars' */ 5848 if (draw_state > WL_NR && n_attr3 > 0 && --n_attr3 == 0) 5849 char_attr = saved_attr3; 5850 5851 /* restore attributes after last 'listchars' or 'number' char */ 5852 if (n_attr > 0 && draw_state == WL_LINE && --n_attr == 0) 5853 char_attr = saved_attr2; 5854 5855 /* 5856 * At end of screen line and there is more to come: Display the line 5857 * so far. If there is no more to display it is caught above. 5858 */ 5859 if (( 5860 #ifdef FEAT_RIGHTLEFT 5861 wp->w_p_rl ? (col < 0) : 5862 #endif 5863 (col >= wp->w_width)) 5864 && (*ptr != NUL 5865 #ifdef FEAT_DIFF 5866 || filler_todo > 0 5867 #endif 5868 || (wp->w_p_list && lcs_eol != NUL && p_extra != at_end_str) 5869 || (n_extra != 0 && (c_extra != NUL || *p_extra != NUL))) 5870 ) 5871 { 5872 #ifdef FEAT_CONCEAL 5873 screen_line(screen_row, wp->w_wincol, col - boguscols, 5874 (int)wp->w_width, HAS_RIGHTLEFT(wp->w_p_rl)); 5875 boguscols = 0; 5876 #else 5877 screen_line(screen_row, wp->w_wincol, col, 5878 (int)wp->w_width, HAS_RIGHTLEFT(wp->w_p_rl)); 5879 #endif 5880 ++row; 5881 ++screen_row; 5882 5883 /* When not wrapping and finished diff lines, or when displayed 5884 * '$' and highlighting until last column, break here. */ 5885 if ((!wp->w_p_wrap 5886 #ifdef FEAT_DIFF 5887 && filler_todo <= 0 5888 #endif 5889 ) || lcs_eol_one == -1) 5890 break; 5891 5892 /* When the window is too narrow draw all "@" lines. */ 5893 if (draw_state != WL_LINE 5894 #ifdef FEAT_DIFF 5895 && filler_todo <= 0 5896 #endif 5897 ) 5898 { 5899 win_draw_end(wp, '@', ' ', TRUE, row, wp->w_height, HLF_AT); 5900 draw_vsep_win(wp, row); 5901 row = endrow; 5902 } 5903 5904 /* When line got too long for screen break here. */ 5905 if (row == endrow) 5906 { 5907 ++row; 5908 break; 5909 } 5910 5911 if (screen_cur_row == screen_row - 1 5912 #ifdef FEAT_DIFF 5913 && filler_todo <= 0 5914 #endif 5915 && wp->w_width == Columns) 5916 { 5917 /* Remember that the line wraps, used for modeless copy. */ 5918 LineWraps[screen_row - 1] = TRUE; 5919 5920 /* 5921 * Special trick to make copy/paste of wrapped lines work with 5922 * xterm/screen: write an extra character beyond the end of 5923 * the line. This will work with all terminal types 5924 * (regardless of the xn,am settings). 5925 * Only do this on a fast tty. 5926 * Only do this if the cursor is on the current line 5927 * (something has been written in it). 5928 * Don't do this for the GUI. 5929 * Don't do this for double-width characters. 5930 * Don't do this for a window not at the right screen border. 5931 */ 5932 if (p_tf 5933 #ifdef FEAT_GUI 5934 && !gui.in_use 5935 #endif 5936 && !(has_mbyte 5937 && ((*mb_off2cells)(LineOffset[screen_row], 5938 LineOffset[screen_row] + screen_Columns) 5939 == 2 5940 || (*mb_off2cells)(LineOffset[screen_row - 1] 5941 + (int)Columns - 2, 5942 LineOffset[screen_row] + screen_Columns) 5943 == 2))) 5944 { 5945 /* First make sure we are at the end of the screen line, 5946 * then output the same character again to let the 5947 * terminal know about the wrap. If the terminal doesn't 5948 * auto-wrap, we overwrite the character. */ 5949 if (screen_cur_col != wp->w_width) 5950 screen_char(LineOffset[screen_row - 1] 5951 + (unsigned)Columns - 1, 5952 screen_row - 1, (int)(Columns - 1)); 5953 5954 /* When there is a multi-byte character, just output a 5955 * space to keep it simple. */ 5956 if (has_mbyte && MB_BYTE2LEN(ScreenLines[LineOffset[ 5957 screen_row - 1] + (Columns - 1)]) > 1) 5958 out_char(' '); 5959 else 5960 out_char(ScreenLines[LineOffset[screen_row - 1] 5961 + (Columns - 1)]); 5962 /* force a redraw of the first char on the next line */ 5963 ScreenAttrs[LineOffset[screen_row]] = (sattr_T)-1; 5964 screen_start(); /* don't know where cursor is now */ 5965 } 5966 } 5967 5968 col = 0; 5969 off = (unsigned)(current_ScreenLine - ScreenLines); 5970 #ifdef FEAT_RIGHTLEFT 5971 if (wp->w_p_rl) 5972 { 5973 col = wp->w_width - 1; /* col is not used if breaking! */ 5974 off += col; 5975 } 5976 #endif 5977 5978 /* reset the drawing state for the start of a wrapped line */ 5979 draw_state = WL_START; 5980 saved_n_extra = n_extra; 5981 saved_p_extra = p_extra; 5982 saved_c_extra = c_extra; 5983 saved_c_final = c_final; 5984 saved_char_attr = char_attr; 5985 n_extra = 0; 5986 lcs_prec_todo = lcs_prec; 5987 #ifdef FEAT_LINEBREAK 5988 # ifdef FEAT_DIFF 5989 if (filler_todo <= 0) 5990 # endif 5991 need_showbreak = TRUE; 5992 #endif 5993 #ifdef FEAT_DIFF 5994 --filler_todo; 5995 /* When the filler lines are actually below the last line of the 5996 * file, don't draw the line itself, break here. */ 5997 if (filler_todo == 0 && wp->w_botfill) 5998 break; 5999 #endif 6000 } 6001 6002 } /* for every character in the line */ 6003 6004 #ifdef FEAT_SPELL 6005 /* After an empty line check first word for capital. */ 6006 if (*skipwhite(line) == NUL) 6007 { 6008 capcol_lnum = lnum + 1; 6009 cap_col = 0; 6010 } 6011 #endif 6012 #ifdef FEAT_TEXT_PROP 6013 vim_free(text_props); 6014 vim_free(text_prop_idxs); 6015 #endif 6016 6017 vim_free(p_extra_free); 6018 return row; 6019 } 6020 6021 /* 6022 * Return if the composing characters at "off_from" and "off_to" differ. 6023 * Only to be used when ScreenLinesUC[off_from] != 0. 6024 */ 6025 static int 6026 comp_char_differs(int off_from, int off_to) 6027 { 6028 int i; 6029 6030 for (i = 0; i < Screen_mco; ++i) 6031 { 6032 if (ScreenLinesC[i][off_from] != ScreenLinesC[i][off_to]) 6033 return TRUE; 6034 if (ScreenLinesC[i][off_from] == 0) 6035 break; 6036 } 6037 return FALSE; 6038 } 6039 6040 /* 6041 * Check whether the given character needs redrawing: 6042 * - the (first byte of the) character is different 6043 * - the attributes are different 6044 * - the character is multi-byte and the next byte is different 6045 * - the character is two cells wide and the second cell differs. 6046 */ 6047 static int 6048 char_needs_redraw(int off_from, int off_to, int cols) 6049 { 6050 if (cols > 0 6051 && ((ScreenLines[off_from] != ScreenLines[off_to] 6052 || ScreenAttrs[off_from] != ScreenAttrs[off_to]) 6053 || (enc_dbcs != 0 6054 && MB_BYTE2LEN(ScreenLines[off_from]) > 1 6055 && (enc_dbcs == DBCS_JPNU && ScreenLines[off_from] == 0x8e 6056 ? ScreenLines2[off_from] != ScreenLines2[off_to] 6057 : (cols > 1 && ScreenLines[off_from + 1] 6058 != ScreenLines[off_to + 1]))) 6059 || (enc_utf8 6060 && (ScreenLinesUC[off_from] != ScreenLinesUC[off_to] 6061 || (ScreenLinesUC[off_from] != 0 6062 && comp_char_differs(off_from, off_to)) 6063 || ((*mb_off2cells)(off_from, off_from + cols) > 1 6064 && ScreenLines[off_from + 1] 6065 != ScreenLines[off_to + 1]))))) 6066 return TRUE; 6067 return FALSE; 6068 } 6069 6070 #if defined(FEAT_TERMINAL) || defined(PROTO) 6071 /* 6072 * Return the index in ScreenLines[] for the current screen line. 6073 */ 6074 int 6075 screen_get_current_line_off() 6076 { 6077 return (int)(current_ScreenLine - ScreenLines); 6078 } 6079 #endif 6080 6081 /* 6082 * Move one "cooked" screen line to the screen, but only the characters that 6083 * have actually changed. Handle insert/delete character. 6084 * "coloff" gives the first column on the screen for this line. 6085 * "endcol" gives the columns where valid characters are. 6086 * "clear_width" is the width of the window. It's > 0 if the rest of the line 6087 * needs to be cleared, negative otherwise. 6088 * "rlflag" is TRUE in a rightleft window: 6089 * When TRUE and "clear_width" > 0, clear columns 0 to "endcol" 6090 * When FALSE and "clear_width" > 0, clear columns "endcol" to "clear_width" 6091 */ 6092 void 6093 screen_line( 6094 int row, 6095 int coloff, 6096 int endcol, 6097 int clear_width, 6098 int rlflag UNUSED) 6099 { 6100 unsigned off_from; 6101 unsigned off_to; 6102 unsigned max_off_from; 6103 unsigned max_off_to; 6104 int col = 0; 6105 int hl; 6106 int force = FALSE; /* force update rest of the line */ 6107 int redraw_this /* bool: does character need redraw? */ 6108 #ifdef FEAT_GUI 6109 = TRUE /* For GUI when while-loop empty */ 6110 #endif 6111 ; 6112 int redraw_next; /* redraw_this for next character */ 6113 int clear_next = FALSE; 6114 int char_cells; /* 1: normal char */ 6115 /* 2: occupies two display cells */ 6116 # define CHAR_CELLS char_cells 6117 6118 /* Check for illegal row and col, just in case. */ 6119 if (row >= Rows) 6120 row = Rows - 1; 6121 if (endcol > Columns) 6122 endcol = Columns; 6123 6124 # ifdef FEAT_CLIPBOARD 6125 clip_may_clear_selection(row, row); 6126 # endif 6127 6128 off_from = (unsigned)(current_ScreenLine - ScreenLines); 6129 off_to = LineOffset[row] + coloff; 6130 max_off_from = off_from + screen_Columns; 6131 max_off_to = LineOffset[row] + screen_Columns; 6132 6133 #ifdef FEAT_RIGHTLEFT 6134 if (rlflag) 6135 { 6136 /* Clear rest first, because it's left of the text. */ 6137 if (clear_width > 0) 6138 { 6139 while (col <= endcol && ScreenLines[off_to] == ' ' 6140 && ScreenAttrs[off_to] == 0 6141 && (!enc_utf8 || ScreenLinesUC[off_to] == 0)) 6142 { 6143 ++off_to; 6144 ++col; 6145 } 6146 if (col <= endcol) 6147 screen_fill(row, row + 1, col + coloff, 6148 endcol + coloff + 1, ' ', ' ', 0); 6149 } 6150 col = endcol + 1; 6151 off_to = LineOffset[row] + col + coloff; 6152 off_from += col; 6153 endcol = (clear_width > 0 ? clear_width : -clear_width); 6154 } 6155 #endif /* FEAT_RIGHTLEFT */ 6156 6157 redraw_next = char_needs_redraw(off_from, off_to, endcol - col); 6158 6159 while (col < endcol) 6160 { 6161 if (has_mbyte && (col + 1 < endcol)) 6162 char_cells = (*mb_off2cells)(off_from, max_off_from); 6163 else 6164 char_cells = 1; 6165 6166 redraw_this = redraw_next; 6167 redraw_next = force || char_needs_redraw(off_from + CHAR_CELLS, 6168 off_to + CHAR_CELLS, endcol - col - CHAR_CELLS); 6169 6170 #ifdef FEAT_GUI 6171 /* If the next character was bold, then redraw the current character to 6172 * remove any pixels that might have spilt over into us. This only 6173 * happens in the GUI. 6174 */ 6175 if (redraw_next && gui.in_use) 6176 { 6177 hl = ScreenAttrs[off_to + CHAR_CELLS]; 6178 if (hl > HL_ALL) 6179 hl = syn_attr2attr(hl); 6180 if (hl & HL_BOLD) 6181 redraw_this = TRUE; 6182 } 6183 #endif 6184 6185 if (redraw_this) 6186 { 6187 /* 6188 * Special handling when 'xs' termcap flag set (hpterm): 6189 * Attributes for characters are stored at the position where the 6190 * cursor is when writing the highlighting code. The 6191 * start-highlighting code must be written with the cursor on the 6192 * first highlighted character. The stop-highlighting code must 6193 * be written with the cursor just after the last highlighted 6194 * character. 6195 * Overwriting a character doesn't remove its highlighting. Need 6196 * to clear the rest of the line, and force redrawing it 6197 * completely. 6198 */ 6199 if ( p_wiv 6200 && !force 6201 #ifdef FEAT_GUI 6202 && !gui.in_use 6203 #endif 6204 && ScreenAttrs[off_to] != 0 6205 && ScreenAttrs[off_from] != ScreenAttrs[off_to]) 6206 { 6207 /* 6208 * Need to remove highlighting attributes here. 6209 */ 6210 windgoto(row, col + coloff); 6211 out_str(T_CE); /* clear rest of this screen line */ 6212 screen_start(); /* don't know where cursor is now */ 6213 force = TRUE; /* force redraw of rest of the line */ 6214 redraw_next = TRUE; /* or else next char would miss out */ 6215 6216 /* 6217 * If the previous character was highlighted, need to stop 6218 * highlighting at this character. 6219 */ 6220 if (col + coloff > 0 && ScreenAttrs[off_to - 1] != 0) 6221 { 6222 screen_attr = ScreenAttrs[off_to - 1]; 6223 term_windgoto(row, col + coloff); 6224 screen_stop_highlight(); 6225 } 6226 else 6227 screen_attr = 0; /* highlighting has stopped */ 6228 } 6229 if (enc_dbcs != 0) 6230 { 6231 /* Check if overwriting a double-byte with a single-byte or 6232 * the other way around requires another character to be 6233 * redrawn. For UTF-8 this isn't needed, because comparing 6234 * ScreenLinesUC[] is sufficient. */ 6235 if (char_cells == 1 6236 && col + 1 < endcol 6237 && (*mb_off2cells)(off_to, max_off_to) > 1) 6238 { 6239 /* Writing a single-cell character over a double-cell 6240 * character: need to redraw the next cell. */ 6241 ScreenLines[off_to + 1] = 0; 6242 redraw_next = TRUE; 6243 } 6244 else if (char_cells == 2 6245 && col + 2 < endcol 6246 && (*mb_off2cells)(off_to, max_off_to) == 1 6247 && (*mb_off2cells)(off_to + 1, max_off_to) > 1) 6248 { 6249 /* Writing the second half of a double-cell character over 6250 * a double-cell character: need to redraw the second 6251 * cell. */ 6252 ScreenLines[off_to + 2] = 0; 6253 redraw_next = TRUE; 6254 } 6255 6256 if (enc_dbcs == DBCS_JPNU) 6257 ScreenLines2[off_to] = ScreenLines2[off_from]; 6258 } 6259 /* When writing a single-width character over a double-width 6260 * character and at the end of the redrawn text, need to clear out 6261 * the right halve of the old character. 6262 * Also required when writing the right halve of a double-width 6263 * char over the left halve of an existing one. */ 6264 if (has_mbyte && col + char_cells == endcol 6265 && ((char_cells == 1 6266 && (*mb_off2cells)(off_to, max_off_to) > 1) 6267 || (char_cells == 2 6268 && (*mb_off2cells)(off_to, max_off_to) == 1 6269 && (*mb_off2cells)(off_to + 1, max_off_to) > 1))) 6270 clear_next = TRUE; 6271 6272 ScreenLines[off_to] = ScreenLines[off_from]; 6273 if (enc_utf8) 6274 { 6275 ScreenLinesUC[off_to] = ScreenLinesUC[off_from]; 6276 if (ScreenLinesUC[off_from] != 0) 6277 { 6278 int i; 6279 6280 for (i = 0; i < Screen_mco; ++i) 6281 ScreenLinesC[i][off_to] = ScreenLinesC[i][off_from]; 6282 } 6283 } 6284 if (char_cells == 2) 6285 ScreenLines[off_to + 1] = ScreenLines[off_from + 1]; 6286 6287 #if defined(FEAT_GUI) || defined(UNIX) 6288 /* The bold trick makes a single column of pixels appear in the 6289 * next character. When a bold character is removed, the next 6290 * character should be redrawn too. This happens for our own GUI 6291 * and for some xterms. */ 6292 if ( 6293 # ifdef FEAT_GUI 6294 gui.in_use 6295 # endif 6296 # if defined(FEAT_GUI) && defined(UNIX) 6297 || 6298 # endif 6299 # ifdef UNIX 6300 term_is_xterm 6301 # endif 6302 ) 6303 { 6304 hl = ScreenAttrs[off_to]; 6305 if (hl > HL_ALL) 6306 hl = syn_attr2attr(hl); 6307 if (hl & HL_BOLD) 6308 redraw_next = TRUE; 6309 } 6310 #endif 6311 ScreenAttrs[off_to] = ScreenAttrs[off_from]; 6312 6313 /* For simplicity set the attributes of second half of a 6314 * double-wide character equal to the first half. */ 6315 if (char_cells == 2) 6316 ScreenAttrs[off_to + 1] = ScreenAttrs[off_from]; 6317 6318 if (enc_dbcs != 0 && char_cells == 2) 6319 screen_char_2(off_to, row, col + coloff); 6320 else 6321 screen_char(off_to, row, col + coloff); 6322 } 6323 else if ( p_wiv 6324 #ifdef FEAT_GUI 6325 && !gui.in_use 6326 #endif 6327 && col + coloff > 0) 6328 { 6329 if (ScreenAttrs[off_to] == ScreenAttrs[off_to - 1]) 6330 { 6331 /* 6332 * Don't output stop-highlight when moving the cursor, it will 6333 * stop the highlighting when it should continue. 6334 */ 6335 screen_attr = 0; 6336 } 6337 else if (screen_attr != 0) 6338 screen_stop_highlight(); 6339 } 6340 6341 off_to += CHAR_CELLS; 6342 off_from += CHAR_CELLS; 6343 col += CHAR_CELLS; 6344 } 6345 6346 if (clear_next) 6347 { 6348 /* Clear the second half of a double-wide character of which the left 6349 * half was overwritten with a single-wide character. */ 6350 ScreenLines[off_to] = ' '; 6351 if (enc_utf8) 6352 ScreenLinesUC[off_to] = 0; 6353 screen_char(off_to, row, col + coloff); 6354 } 6355 6356 if (clear_width > 0 6357 #ifdef FEAT_RIGHTLEFT 6358 && !rlflag 6359 #endif 6360 ) 6361 { 6362 #ifdef FEAT_GUI 6363 int startCol = col; 6364 #endif 6365 6366 /* blank out the rest of the line */ 6367 while (col < clear_width && ScreenLines[off_to] == ' ' 6368 && ScreenAttrs[off_to] == 0 6369 && (!enc_utf8 || ScreenLinesUC[off_to] == 0)) 6370 { 6371 ++off_to; 6372 ++col; 6373 } 6374 if (col < clear_width) 6375 { 6376 #ifdef FEAT_GUI 6377 /* 6378 * In the GUI, clearing the rest of the line may leave pixels 6379 * behind if the first character cleared was bold. Some bold 6380 * fonts spill over the left. In this case we redraw the previous 6381 * character too. If we didn't skip any blanks above, then we 6382 * only redraw if the character wasn't already redrawn anyway. 6383 */ 6384 if (gui.in_use && (col > startCol || !redraw_this)) 6385 { 6386 hl = ScreenAttrs[off_to]; 6387 if (hl > HL_ALL || (hl & HL_BOLD)) 6388 { 6389 int prev_cells = 1; 6390 6391 if (enc_utf8) 6392 /* for utf-8, ScreenLines[char_offset + 1] == 0 means 6393 * that its width is 2. */ 6394 prev_cells = ScreenLines[off_to - 1] == 0 ? 2 : 1; 6395 else if (enc_dbcs != 0) 6396 { 6397 /* find previous character by counting from first 6398 * column and get its width. */ 6399 unsigned off = LineOffset[row]; 6400 unsigned max_off = LineOffset[row] + screen_Columns; 6401 6402 while (off < off_to) 6403 { 6404 prev_cells = (*mb_off2cells)(off, max_off); 6405 off += prev_cells; 6406 } 6407 } 6408 6409 if (enc_dbcs != 0 && prev_cells > 1) 6410 screen_char_2(off_to - prev_cells, row, 6411 col + coloff - prev_cells); 6412 else 6413 screen_char(off_to - prev_cells, row, 6414 col + coloff - prev_cells); 6415 } 6416 } 6417 #endif 6418 screen_fill(row, row + 1, col + coloff, clear_width + coloff, 6419 ' ', ' ', 0); 6420 off_to += clear_width - col; 6421 col = clear_width; 6422 } 6423 } 6424 6425 if (clear_width > 0) 6426 { 6427 /* For a window that's left of another, draw the separator char. */ 6428 if (col + coloff < Columns) 6429 { 6430 int c; 6431 6432 c = fillchar_vsep(&hl); 6433 if (ScreenLines[off_to] != (schar_T)c 6434 || (enc_utf8 && (int)ScreenLinesUC[off_to] 6435 != (c >= 0x80 ? c : 0)) 6436 || ScreenAttrs[off_to] != hl) 6437 { 6438 ScreenLines[off_to] = c; 6439 ScreenAttrs[off_to] = hl; 6440 if (enc_utf8) 6441 { 6442 if (c >= 0x80) 6443 { 6444 ScreenLinesUC[off_to] = c; 6445 ScreenLinesC[0][off_to] = 0; 6446 } 6447 else 6448 ScreenLinesUC[off_to] = 0; 6449 } 6450 screen_char(off_to, row, col + coloff); 6451 } 6452 } 6453 else 6454 LineWraps[row] = FALSE; 6455 } 6456 } 6457 6458 #if defined(FEAT_RIGHTLEFT) || defined(PROTO) 6459 /* 6460 * Mirror text "str" for right-left displaying. 6461 * Only works for single-byte characters (e.g., numbers). 6462 */ 6463 void 6464 rl_mirror(char_u *str) 6465 { 6466 char_u *p1, *p2; 6467 int t; 6468 6469 for (p1 = str, p2 = str + STRLEN(str) - 1; p1 < p2; ++p1, --p2) 6470 { 6471 t = *p1; 6472 *p1 = *p2; 6473 *p2 = t; 6474 } 6475 } 6476 #endif 6477 6478 /* 6479 * mark all status lines for redraw; used after first :cd 6480 */ 6481 void 6482 status_redraw_all(void) 6483 { 6484 win_T *wp; 6485 6486 FOR_ALL_WINDOWS(wp) 6487 if (wp->w_status_height) 6488 { 6489 wp->w_redr_status = TRUE; 6490 redraw_later(VALID); 6491 } 6492 } 6493 6494 /* 6495 * mark all status lines of the current buffer for redraw 6496 */ 6497 void 6498 status_redraw_curbuf(void) 6499 { 6500 win_T *wp; 6501 6502 FOR_ALL_WINDOWS(wp) 6503 if (wp->w_status_height != 0 && wp->w_buffer == curbuf) 6504 { 6505 wp->w_redr_status = TRUE; 6506 redraw_later(VALID); 6507 } 6508 } 6509 6510 /* 6511 * Redraw all status lines that need to be redrawn. 6512 */ 6513 void 6514 redraw_statuslines(void) 6515 { 6516 win_T *wp; 6517 6518 FOR_ALL_WINDOWS(wp) 6519 if (wp->w_redr_status) 6520 win_redr_status(wp, FALSE); 6521 if (redraw_tabline) 6522 draw_tabline(); 6523 } 6524 6525 #if defined(FEAT_WILDMENU) || defined(PROTO) 6526 /* 6527 * Redraw all status lines at the bottom of frame "frp". 6528 */ 6529 void 6530 win_redraw_last_status(frame_T *frp) 6531 { 6532 if (frp->fr_layout == FR_LEAF) 6533 frp->fr_win->w_redr_status = TRUE; 6534 else if (frp->fr_layout == FR_ROW) 6535 { 6536 FOR_ALL_FRAMES(frp, frp->fr_child) 6537 win_redraw_last_status(frp); 6538 } 6539 else /* frp->fr_layout == FR_COL */ 6540 { 6541 frp = frp->fr_child; 6542 while (frp->fr_next != NULL) 6543 frp = frp->fr_next; 6544 win_redraw_last_status(frp); 6545 } 6546 } 6547 #endif 6548 6549 /* 6550 * Draw the verticap separator right of window "wp" starting with line "row". 6551 */ 6552 static void 6553 draw_vsep_win(win_T *wp, int row) 6554 { 6555 int hl; 6556 int c; 6557 6558 if (wp->w_vsep_width) 6559 { 6560 /* draw the vertical separator right of this window */ 6561 c = fillchar_vsep(&hl); 6562 screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + wp->w_height, 6563 W_ENDCOL(wp), W_ENDCOL(wp) + 1, 6564 c, ' ', hl); 6565 } 6566 } 6567 6568 #ifdef FEAT_WILDMENU 6569 static int skip_status_match_char(expand_T *xp, char_u *s); 6570 6571 /* 6572 * Get the length of an item as it will be shown in the status line. 6573 */ 6574 static int 6575 status_match_len(expand_T *xp, char_u *s) 6576 { 6577 int len = 0; 6578 6579 #ifdef FEAT_MENU 6580 int emenu = (xp->xp_context == EXPAND_MENUS 6581 || xp->xp_context == EXPAND_MENUNAMES); 6582 6583 /* Check for menu separators - replace with '|'. */ 6584 if (emenu && menu_is_separator(s)) 6585 return 1; 6586 #endif 6587 6588 while (*s != NUL) 6589 { 6590 s += skip_status_match_char(xp, s); 6591 len += ptr2cells(s); 6592 MB_PTR_ADV(s); 6593 } 6594 6595 return len; 6596 } 6597 6598 /* 6599 * Return the number of characters that should be skipped in a status match. 6600 * These are backslashes used for escaping. Do show backslashes in help tags. 6601 */ 6602 static int 6603 skip_status_match_char(expand_T *xp, char_u *s) 6604 { 6605 if ((rem_backslash(s) && xp->xp_context != EXPAND_HELP) 6606 #ifdef FEAT_MENU 6607 || ((xp->xp_context == EXPAND_MENUS 6608 || xp->xp_context == EXPAND_MENUNAMES) 6609 && (s[0] == '\t' || (s[0] == '\\' && s[1] != NUL))) 6610 #endif 6611 ) 6612 { 6613 #ifndef BACKSLASH_IN_FILENAME 6614 if (xp->xp_shell && csh_like_shell() && s[1] == '\\' && s[2] == '!') 6615 return 2; 6616 #endif 6617 return 1; 6618 } 6619 return 0; 6620 } 6621 6622 /* 6623 * Show wildchar matches in the status line. 6624 * Show at least the "match" item. 6625 * We start at item 'first_match' in the list and show all matches that fit. 6626 * 6627 * If inversion is possible we use it. Else '=' characters are used. 6628 */ 6629 void 6630 win_redr_status_matches( 6631 expand_T *xp, 6632 int num_matches, 6633 char_u **matches, /* list of matches */ 6634 int match, 6635 int showtail) 6636 { 6637 #define L_MATCH(m) (showtail ? sm_gettail(matches[m]) : matches[m]) 6638 int row; 6639 char_u *buf; 6640 int len; 6641 int clen; /* length in screen cells */ 6642 int fillchar; 6643 int attr; 6644 int i; 6645 int highlight = TRUE; 6646 char_u *selstart = NULL; 6647 int selstart_col = 0; 6648 char_u *selend = NULL; 6649 static int first_match = 0; 6650 int add_left = FALSE; 6651 char_u *s; 6652 #ifdef FEAT_MENU 6653 int emenu; 6654 #endif 6655 int l; 6656 6657 if (matches == NULL) /* interrupted completion? */ 6658 return; 6659 6660 if (has_mbyte) 6661 buf = alloc((unsigned)Columns * MB_MAXBYTES + 1); 6662 else 6663 buf = alloc((unsigned)Columns + 1); 6664 if (buf == NULL) 6665 return; 6666 6667 if (match == -1) /* don't show match but original text */ 6668 { 6669 match = 0; 6670 highlight = FALSE; 6671 } 6672 /* count 1 for the ending ">" */ 6673 clen = status_match_len(xp, L_MATCH(match)) + 3; 6674 if (match == 0) 6675 first_match = 0; 6676 else if (match < first_match) 6677 { 6678 /* jumping left, as far as we can go */ 6679 first_match = match; 6680 add_left = TRUE; 6681 } 6682 else 6683 { 6684 /* check if match fits on the screen */ 6685 for (i = first_match; i < match; ++i) 6686 clen += status_match_len(xp, L_MATCH(i)) + 2; 6687 if (first_match > 0) 6688 clen += 2; 6689 /* jumping right, put match at the left */ 6690 if ((long)clen > Columns) 6691 { 6692 first_match = match; 6693 /* if showing the last match, we can add some on the left */ 6694 clen = 2; 6695 for (i = match; i < num_matches; ++i) 6696 { 6697 clen += status_match_len(xp, L_MATCH(i)) + 2; 6698 if ((long)clen >= Columns) 6699 break; 6700 } 6701 if (i == num_matches) 6702 add_left = TRUE; 6703 } 6704 } 6705 if (add_left) 6706 while (first_match > 0) 6707 { 6708 clen += status_match_len(xp, L_MATCH(first_match - 1)) + 2; 6709 if ((long)clen >= Columns) 6710 break; 6711 --first_match; 6712 } 6713 6714 fillchar = fillchar_status(&attr, curwin); 6715 6716 if (first_match == 0) 6717 { 6718 *buf = NUL; 6719 len = 0; 6720 } 6721 else 6722 { 6723 STRCPY(buf, "< "); 6724 len = 2; 6725 } 6726 clen = len; 6727 6728 i = first_match; 6729 while ((long)(clen + status_match_len(xp, L_MATCH(i)) + 2) < Columns) 6730 { 6731 if (i == match) 6732 { 6733 selstart = buf + len; 6734 selstart_col = clen; 6735 } 6736 6737 s = L_MATCH(i); 6738 /* Check for menu separators - replace with '|' */ 6739 #ifdef FEAT_MENU 6740 emenu = (xp->xp_context == EXPAND_MENUS 6741 || xp->xp_context == EXPAND_MENUNAMES); 6742 if (emenu && menu_is_separator(s)) 6743 { 6744 STRCPY(buf + len, transchar('|')); 6745 l = (int)STRLEN(buf + len); 6746 len += l; 6747 clen += l; 6748 } 6749 else 6750 #endif 6751 for ( ; *s != NUL; ++s) 6752 { 6753 s += skip_status_match_char(xp, s); 6754 clen += ptr2cells(s); 6755 if (has_mbyte && (l = (*mb_ptr2len)(s)) > 1) 6756 { 6757 STRNCPY(buf + len, s, l); 6758 s += l - 1; 6759 len += l; 6760 } 6761 else 6762 { 6763 STRCPY(buf + len, transchar_byte(*s)); 6764 len += (int)STRLEN(buf + len); 6765 } 6766 } 6767 if (i == match) 6768 selend = buf + len; 6769 6770 *(buf + len++) = ' '; 6771 *(buf + len++) = ' '; 6772 clen += 2; 6773 if (++i == num_matches) 6774 break; 6775 } 6776 6777 if (i != num_matches) 6778 { 6779 *(buf + len++) = '>'; 6780 ++clen; 6781 } 6782 6783 buf[len] = NUL; 6784 6785 row = cmdline_row - 1; 6786 if (row >= 0) 6787 { 6788 if (wild_menu_showing == 0) 6789 { 6790 if (msg_scrolled > 0) 6791 { 6792 /* Put the wildmenu just above the command line. If there is 6793 * no room, scroll the screen one line up. */ 6794 if (cmdline_row == Rows - 1) 6795 { 6796 screen_del_lines(0, 0, 1, (int)Rows, TRUE, 0, NULL); 6797 ++msg_scrolled; 6798 } 6799 else 6800 { 6801 ++cmdline_row; 6802 ++row; 6803 } 6804 wild_menu_showing = WM_SCROLLED; 6805 } 6806 else 6807 { 6808 /* Create status line if needed by setting 'laststatus' to 2. 6809 * Set 'winminheight' to zero to avoid that the window is 6810 * resized. */ 6811 if (lastwin->w_status_height == 0) 6812 { 6813 save_p_ls = p_ls; 6814 save_p_wmh = p_wmh; 6815 p_ls = 2; 6816 p_wmh = 0; 6817 last_status(FALSE); 6818 } 6819 wild_menu_showing = WM_SHOWN; 6820 } 6821 } 6822 6823 screen_puts(buf, row, 0, attr); 6824 if (selstart != NULL && highlight) 6825 { 6826 *selend = NUL; 6827 screen_puts(selstart, row, selstart_col, HL_ATTR(HLF_WM)); 6828 } 6829 6830 screen_fill(row, row + 1, clen, (int)Columns, fillchar, fillchar, attr); 6831 } 6832 6833 win_redraw_last_status(topframe); 6834 vim_free(buf); 6835 } 6836 #endif 6837 6838 /* 6839 * Redraw the status line of window wp. 6840 * 6841 * If inversion is possible we use it. Else '=' characters are used. 6842 * If "ignore_pum" is TRUE, also redraw statusline when the popup menu is 6843 * displayed. 6844 */ 6845 static void 6846 win_redr_status(win_T *wp, int ignore_pum UNUSED) 6847 { 6848 int row; 6849 char_u *p; 6850 int len; 6851 int fillchar; 6852 int attr; 6853 int this_ru_col; 6854 static int busy = FALSE; 6855 6856 /* It's possible to get here recursively when 'statusline' (indirectly) 6857 * invokes ":redrawstatus". Simply ignore the call then. */ 6858 if (busy) 6859 return; 6860 busy = TRUE; 6861 6862 wp->w_redr_status = FALSE; 6863 if (wp->w_status_height == 0) 6864 { 6865 /* no status line, can only be last window */ 6866 redraw_cmdline = TRUE; 6867 } 6868 else if (!redrawing() 6869 #ifdef FEAT_INS_EXPAND 6870 // don't update status line when popup menu is visible and may be 6871 // drawn over it, unless it will be redrawn later 6872 || (!ignore_pum && pum_visible()) 6873 #endif 6874 ) 6875 { 6876 /* Don't redraw right now, do it later. */ 6877 wp->w_redr_status = TRUE; 6878 } 6879 #ifdef FEAT_STL_OPT 6880 else if (*p_stl != NUL || *wp->w_p_stl != NUL) 6881 { 6882 /* redraw custom status line */ 6883 redraw_custom_statusline(wp); 6884 } 6885 #endif 6886 else 6887 { 6888 fillchar = fillchar_status(&attr, wp); 6889 6890 get_trans_bufname(wp->w_buffer); 6891 p = NameBuff; 6892 len = (int)STRLEN(p); 6893 6894 if (bt_help(wp->w_buffer) 6895 #ifdef FEAT_QUICKFIX 6896 || wp->w_p_pvw 6897 #endif 6898 || bufIsChanged(wp->w_buffer) 6899 || wp->w_buffer->b_p_ro) 6900 *(p + len++) = ' '; 6901 if (bt_help(wp->w_buffer)) 6902 { 6903 STRCPY(p + len, _("[Help]")); 6904 len += (int)STRLEN(p + len); 6905 } 6906 #ifdef FEAT_QUICKFIX 6907 if (wp->w_p_pvw) 6908 { 6909 STRCPY(p + len, _("[Preview]")); 6910 len += (int)STRLEN(p + len); 6911 } 6912 #endif 6913 if (bufIsChanged(wp->w_buffer) 6914 #ifdef FEAT_TERMINAL 6915 && !bt_terminal(wp->w_buffer) 6916 #endif 6917 ) 6918 { 6919 STRCPY(p + len, "[+]"); 6920 len += 3; 6921 } 6922 if (wp->w_buffer->b_p_ro) 6923 { 6924 STRCPY(p + len, _("[RO]")); 6925 len += (int)STRLEN(p + len); 6926 } 6927 6928 this_ru_col = ru_col - (Columns - wp->w_width); 6929 if (this_ru_col < (wp->w_width + 1) / 2) 6930 this_ru_col = (wp->w_width + 1) / 2; 6931 if (this_ru_col <= 1) 6932 { 6933 p = (char_u *)"<"; /* No room for file name! */ 6934 len = 1; 6935 } 6936 else if (has_mbyte) 6937 { 6938 int clen = 0, i; 6939 6940 /* Count total number of display cells. */ 6941 clen = mb_string2cells(p, -1); 6942 6943 /* Find first character that will fit. 6944 * Going from start to end is much faster for DBCS. */ 6945 for (i = 0; p[i] != NUL && clen >= this_ru_col - 1; 6946 i += (*mb_ptr2len)(p + i)) 6947 clen -= (*mb_ptr2cells)(p + i); 6948 len = clen; 6949 if (i > 0) 6950 { 6951 p = p + i - 1; 6952 *p = '<'; 6953 ++len; 6954 } 6955 6956 } 6957 else if (len > this_ru_col - 1) 6958 { 6959 p += len - (this_ru_col - 1); 6960 *p = '<'; 6961 len = this_ru_col - 1; 6962 } 6963 6964 row = W_WINROW(wp) + wp->w_height; 6965 screen_puts(p, row, wp->w_wincol, attr); 6966 screen_fill(row, row + 1, len + wp->w_wincol, 6967 this_ru_col + wp->w_wincol, fillchar, fillchar, attr); 6968 6969 if (get_keymap_str(wp, (char_u *)"<%s>", NameBuff, MAXPATHL) 6970 && (int)(this_ru_col - len) > (int)(STRLEN(NameBuff) + 1)) 6971 screen_puts(NameBuff, row, (int)(this_ru_col - STRLEN(NameBuff) 6972 - 1 + wp->w_wincol), attr); 6973 6974 #ifdef FEAT_CMDL_INFO 6975 win_redr_ruler(wp, TRUE, ignore_pum); 6976 #endif 6977 } 6978 6979 /* 6980 * May need to draw the character below the vertical separator. 6981 */ 6982 if (wp->w_vsep_width != 0 && wp->w_status_height != 0 && redrawing()) 6983 { 6984 if (stl_connected(wp)) 6985 fillchar = fillchar_status(&attr, wp); 6986 else 6987 fillchar = fillchar_vsep(&attr); 6988 screen_putchar(fillchar, W_WINROW(wp) + wp->w_height, W_ENDCOL(wp), 6989 attr); 6990 } 6991 busy = FALSE; 6992 } 6993 6994 #ifdef FEAT_STL_OPT 6995 /* 6996 * Redraw the status line according to 'statusline' and take care of any 6997 * errors encountered. 6998 */ 6999 static void 7000 redraw_custom_statusline(win_T *wp) 7001 { 7002 static int entered = FALSE; 7003 int saved_did_emsg = did_emsg; 7004 7005 /* When called recursively return. This can happen when the statusline 7006 * contains an expression that triggers a redraw. */ 7007 if (entered) 7008 return; 7009 entered = TRUE; 7010 7011 did_emsg = FALSE; 7012 win_redr_custom(wp, FALSE); 7013 if (did_emsg) 7014 { 7015 /* When there is an error disable the statusline, otherwise the 7016 * display is messed up with errors and a redraw triggers the problem 7017 * again and again. */ 7018 set_string_option_direct((char_u *)"statusline", -1, 7019 (char_u *)"", OPT_FREE | (*wp->w_p_stl != NUL 7020 ? OPT_LOCAL : OPT_GLOBAL), SID_ERROR); 7021 } 7022 did_emsg |= saved_did_emsg; 7023 entered = FALSE; 7024 } 7025 #endif 7026 7027 /* 7028 * Return TRUE if the status line of window "wp" is connected to the status 7029 * line of the window right of it. If not, then it's a vertical separator. 7030 * Only call if (wp->w_vsep_width != 0). 7031 */ 7032 int 7033 stl_connected(win_T *wp) 7034 { 7035 frame_T *fr; 7036 7037 fr = wp->w_frame; 7038 while (fr->fr_parent != NULL) 7039 { 7040 if (fr->fr_parent->fr_layout == FR_COL) 7041 { 7042 if (fr->fr_next != NULL) 7043 break; 7044 } 7045 else 7046 { 7047 if (fr->fr_next != NULL) 7048 return TRUE; 7049 } 7050 fr = fr->fr_parent; 7051 } 7052 return FALSE; 7053 } 7054 7055 7056 /* 7057 * Get the value to show for the language mappings, active 'keymap'. 7058 */ 7059 int 7060 get_keymap_str( 7061 win_T *wp, 7062 char_u *fmt, /* format string containing one %s item */ 7063 char_u *buf, /* buffer for the result */ 7064 int len) /* length of buffer */ 7065 { 7066 char_u *p; 7067 7068 if (wp->w_buffer->b_p_iminsert != B_IMODE_LMAP) 7069 return FALSE; 7070 7071 { 7072 #ifdef FEAT_EVAL 7073 buf_T *old_curbuf = curbuf; 7074 win_T *old_curwin = curwin; 7075 char_u *s; 7076 7077 curbuf = wp->w_buffer; 7078 curwin = wp; 7079 STRCPY(buf, "b:keymap_name"); /* must be writable */ 7080 ++emsg_skip; 7081 s = p = eval_to_string(buf, NULL, FALSE); 7082 --emsg_skip; 7083 curbuf = old_curbuf; 7084 curwin = old_curwin; 7085 if (p == NULL || *p == NUL) 7086 #endif 7087 { 7088 #ifdef FEAT_KEYMAP 7089 if (wp->w_buffer->b_kmap_state & KEYMAP_LOADED) 7090 p = wp->w_buffer->b_p_keymap; 7091 else 7092 #endif 7093 p = (char_u *)"lang"; 7094 } 7095 if (vim_snprintf((char *)buf, len, (char *)fmt, p) > len - 1) 7096 buf[0] = NUL; 7097 #ifdef FEAT_EVAL 7098 vim_free(s); 7099 #endif 7100 } 7101 return buf[0] != NUL; 7102 } 7103 7104 #if defined(FEAT_STL_OPT) || defined(PROTO) 7105 /* 7106 * Redraw the status line or ruler of window "wp". 7107 * When "wp" is NULL redraw the tab pages line from 'tabline'. 7108 */ 7109 static void 7110 win_redr_custom( 7111 win_T *wp, 7112 int draw_ruler) /* TRUE or FALSE */ 7113 { 7114 static int entered = FALSE; 7115 int attr; 7116 int curattr; 7117 int row; 7118 int col = 0; 7119 int maxwidth; 7120 int width; 7121 int n; 7122 int len; 7123 int fillchar; 7124 char_u buf[MAXPATHL]; 7125 char_u *stl; 7126 char_u *p; 7127 struct stl_hlrec hltab[STL_MAX_ITEM]; 7128 struct stl_hlrec tabtab[STL_MAX_ITEM]; 7129 int use_sandbox = FALSE; 7130 win_T *ewp; 7131 int p_crb_save; 7132 7133 /* There is a tiny chance that this gets called recursively: When 7134 * redrawing a status line triggers redrawing the ruler or tabline. 7135 * Avoid trouble by not allowing recursion. */ 7136 if (entered) 7137 return; 7138 entered = TRUE; 7139 7140 /* setup environment for the task at hand */ 7141 if (wp == NULL) 7142 { 7143 /* Use 'tabline'. Always at the first line of the screen. */ 7144 stl = p_tal; 7145 row = 0; 7146 fillchar = ' '; 7147 attr = HL_ATTR(HLF_TPF); 7148 maxwidth = Columns; 7149 # ifdef FEAT_EVAL 7150 use_sandbox = was_set_insecurely((char_u *)"tabline", 0); 7151 # endif 7152 } 7153 else 7154 { 7155 row = W_WINROW(wp) + wp->w_height; 7156 fillchar = fillchar_status(&attr, wp); 7157 maxwidth = wp->w_width; 7158 7159 if (draw_ruler) 7160 { 7161 stl = p_ruf; 7162 /* advance past any leading group spec - implicit in ru_col */ 7163 if (*stl == '%') 7164 { 7165 if (*++stl == '-') 7166 stl++; 7167 if (atoi((char *)stl)) 7168 while (VIM_ISDIGIT(*stl)) 7169 stl++; 7170 if (*stl++ != '(') 7171 stl = p_ruf; 7172 } 7173 col = ru_col - (Columns - wp->w_width); 7174 if (col < (wp->w_width + 1) / 2) 7175 col = (wp->w_width + 1) / 2; 7176 maxwidth = wp->w_width - col; 7177 if (!wp->w_status_height) 7178 { 7179 row = Rows - 1; 7180 --maxwidth; /* writing in last column may cause scrolling */ 7181 fillchar = ' '; 7182 attr = 0; 7183 } 7184 7185 # ifdef FEAT_EVAL 7186 use_sandbox = was_set_insecurely((char_u *)"rulerformat", 0); 7187 # endif 7188 } 7189 else 7190 { 7191 if (*wp->w_p_stl != NUL) 7192 stl = wp->w_p_stl; 7193 else 7194 stl = p_stl; 7195 # ifdef FEAT_EVAL 7196 use_sandbox = was_set_insecurely((char_u *)"statusline", 7197 *wp->w_p_stl == NUL ? 0 : OPT_LOCAL); 7198 # endif 7199 } 7200 7201 col += wp->w_wincol; 7202 } 7203 7204 if (maxwidth <= 0) 7205 goto theend; 7206 7207 /* Temporarily reset 'cursorbind', we don't want a side effect from moving 7208 * the cursor away and back. */ 7209 ewp = wp == NULL ? curwin : wp; 7210 p_crb_save = ewp->w_p_crb; 7211 ewp->w_p_crb = FALSE; 7212 7213 /* Make a copy, because the statusline may include a function call that 7214 * might change the option value and free the memory. */ 7215 stl = vim_strsave(stl); 7216 width = build_stl_str_hl(ewp, buf, sizeof(buf), 7217 stl, use_sandbox, 7218 fillchar, maxwidth, hltab, tabtab); 7219 vim_free(stl); 7220 ewp->w_p_crb = p_crb_save; 7221 7222 /* Make all characters printable. */ 7223 p = transstr(buf); 7224 if (p != NULL) 7225 { 7226 vim_strncpy(buf, p, sizeof(buf) - 1); 7227 vim_free(p); 7228 } 7229 7230 /* fill up with "fillchar" */ 7231 len = (int)STRLEN(buf); 7232 while (width < maxwidth && len < (int)sizeof(buf) - 1) 7233 { 7234 len += (*mb_char2bytes)(fillchar, buf + len); 7235 ++width; 7236 } 7237 buf[len] = NUL; 7238 7239 /* 7240 * Draw each snippet with the specified highlighting. 7241 */ 7242 curattr = attr; 7243 p = buf; 7244 for (n = 0; hltab[n].start != NULL; n++) 7245 { 7246 len = (int)(hltab[n].start - p); 7247 screen_puts_len(p, len, row, col, curattr); 7248 col += vim_strnsize(p, len); 7249 p = hltab[n].start; 7250 7251 if (hltab[n].userhl == 0) 7252 curattr = attr; 7253 else if (hltab[n].userhl < 0) 7254 curattr = syn_id2attr(-hltab[n].userhl); 7255 #ifdef FEAT_TERMINAL 7256 else if (wp != NULL && wp != curwin && bt_terminal(wp->w_buffer) 7257 && wp->w_status_height != 0) 7258 curattr = highlight_stltermnc[hltab[n].userhl - 1]; 7259 else if (wp != NULL && bt_terminal(wp->w_buffer) 7260 && wp->w_status_height != 0) 7261 curattr = highlight_stlterm[hltab[n].userhl - 1]; 7262 #endif 7263 else if (wp != NULL && wp != curwin && wp->w_status_height != 0) 7264 curattr = highlight_stlnc[hltab[n].userhl - 1]; 7265 else 7266 curattr = highlight_user[hltab[n].userhl - 1]; 7267 } 7268 screen_puts(p, row, col, curattr); 7269 7270 if (wp == NULL) 7271 { 7272 /* Fill the TabPageIdxs[] array for clicking in the tab pagesline. */ 7273 col = 0; 7274 len = 0; 7275 p = buf; 7276 fillchar = 0; 7277 for (n = 0; tabtab[n].start != NULL; n++) 7278 { 7279 len += vim_strnsize(p, (int)(tabtab[n].start - p)); 7280 while (col < len) 7281 TabPageIdxs[col++] = fillchar; 7282 p = tabtab[n].start; 7283 fillchar = tabtab[n].userhl; 7284 } 7285 while (col < Columns) 7286 TabPageIdxs[col++] = fillchar; 7287 } 7288 7289 theend: 7290 entered = FALSE; 7291 } 7292 7293 #endif /* FEAT_STL_OPT */ 7294 7295 /* 7296 * Output a single character directly to the screen and update ScreenLines. 7297 */ 7298 void 7299 screen_putchar(int c, int row, int col, int attr) 7300 { 7301 char_u buf[MB_MAXBYTES + 1]; 7302 7303 if (has_mbyte) 7304 buf[(*mb_char2bytes)(c, buf)] = NUL; 7305 else 7306 { 7307 buf[0] = c; 7308 buf[1] = NUL; 7309 } 7310 screen_puts(buf, row, col, attr); 7311 } 7312 7313 /* 7314 * Get a single character directly from ScreenLines into "bytes[]". 7315 * Also return its attribute in *attrp; 7316 */ 7317 void 7318 screen_getbytes(int row, int col, char_u *bytes, int *attrp) 7319 { 7320 unsigned off; 7321 7322 /* safety check */ 7323 if (ScreenLines != NULL && row < screen_Rows && col < screen_Columns) 7324 { 7325 off = LineOffset[row] + col; 7326 *attrp = ScreenAttrs[off]; 7327 bytes[0] = ScreenLines[off]; 7328 bytes[1] = NUL; 7329 7330 if (enc_utf8 && ScreenLinesUC[off] != 0) 7331 bytes[utfc_char2bytes(off, bytes)] = NUL; 7332 else if (enc_dbcs == DBCS_JPNU && ScreenLines[off] == 0x8e) 7333 { 7334 bytes[0] = ScreenLines[off]; 7335 bytes[1] = ScreenLines2[off]; 7336 bytes[2] = NUL; 7337 } 7338 else if (enc_dbcs && MB_BYTE2LEN(bytes[0]) > 1) 7339 { 7340 bytes[1] = ScreenLines[off + 1]; 7341 bytes[2] = NUL; 7342 } 7343 } 7344 } 7345 7346 /* 7347 * Return TRUE if composing characters for screen posn "off" differs from 7348 * composing characters in "u8cc". 7349 * Only to be used when ScreenLinesUC[off] != 0. 7350 */ 7351 static int 7352 screen_comp_differs(int off, int *u8cc) 7353 { 7354 int i; 7355 7356 for (i = 0; i < Screen_mco; ++i) 7357 { 7358 if (ScreenLinesC[i][off] != (u8char_T)u8cc[i]) 7359 return TRUE; 7360 if (u8cc[i] == 0) 7361 break; 7362 } 7363 return FALSE; 7364 } 7365 7366 /* 7367 * Put string '*text' on the screen at position 'row' and 'col', with 7368 * attributes 'attr', and update ScreenLines[] and ScreenAttrs[]. 7369 * Note: only outputs within one row, message is truncated at screen boundary! 7370 * Note: if ScreenLines[], row and/or col is invalid, nothing is done. 7371 */ 7372 void 7373 screen_puts( 7374 char_u *text, 7375 int row, 7376 int col, 7377 int attr) 7378 { 7379 screen_puts_len(text, -1, row, col, attr); 7380 } 7381 7382 /* 7383 * Like screen_puts(), but output "text[len]". When "len" is -1 output up to 7384 * a NUL. 7385 */ 7386 void 7387 screen_puts_len( 7388 char_u *text, 7389 int textlen, 7390 int row, 7391 int col, 7392 int attr) 7393 { 7394 unsigned off; 7395 char_u *ptr = text; 7396 int len = textlen; 7397 int c; 7398 unsigned max_off; 7399 int mbyte_blen = 1; 7400 int mbyte_cells = 1; 7401 int u8c = 0; 7402 int u8cc[MAX_MCO]; 7403 int clear_next_cell = FALSE; 7404 #ifdef FEAT_ARABIC 7405 int prev_c = 0; /* previous Arabic character */ 7406 int pc, nc, nc1; 7407 int pcc[MAX_MCO]; 7408 #endif 7409 int force_redraw_this; 7410 int force_redraw_next = FALSE; 7411 int need_redraw; 7412 7413 if (ScreenLines == NULL || row >= screen_Rows) /* safety check */ 7414 return; 7415 off = LineOffset[row] + col; 7416 7417 /* When drawing over the right halve of a double-wide char clear out the 7418 * left halve. Only needed in a terminal. */ 7419 if (has_mbyte && col > 0 && col < screen_Columns 7420 #ifdef FEAT_GUI 7421 && !gui.in_use 7422 #endif 7423 && mb_fix_col(col, row) != col) 7424 { 7425 ScreenLines[off - 1] = ' '; 7426 ScreenAttrs[off - 1] = 0; 7427 if (enc_utf8) 7428 { 7429 ScreenLinesUC[off - 1] = 0; 7430 ScreenLinesC[0][off - 1] = 0; 7431 } 7432 /* redraw the previous cell, make it empty */ 7433 screen_char(off - 1, row, col - 1); 7434 /* force the cell at "col" to be redrawn */ 7435 force_redraw_next = TRUE; 7436 } 7437 7438 max_off = LineOffset[row] + screen_Columns; 7439 while (col < screen_Columns 7440 && (len < 0 || (int)(ptr - text) < len) 7441 && *ptr != NUL) 7442 { 7443 c = *ptr; 7444 /* check if this is the first byte of a multibyte */ 7445 if (has_mbyte) 7446 { 7447 if (enc_utf8 && len > 0) 7448 mbyte_blen = utfc_ptr2len_len(ptr, (int)((text + len) - ptr)); 7449 else 7450 mbyte_blen = (*mb_ptr2len)(ptr); 7451 if (enc_dbcs == DBCS_JPNU && c == 0x8e) 7452 mbyte_cells = 1; 7453 else if (enc_dbcs != 0) 7454 mbyte_cells = mbyte_blen; 7455 else /* enc_utf8 */ 7456 { 7457 if (len >= 0) 7458 u8c = utfc_ptr2char_len(ptr, u8cc, 7459 (int)((text + len) - ptr)); 7460 else 7461 u8c = utfc_ptr2char(ptr, u8cc); 7462 mbyte_cells = utf_char2cells(u8c); 7463 #ifdef FEAT_ARABIC 7464 if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) 7465 { 7466 /* Do Arabic shaping. */ 7467 if (len >= 0 && (int)(ptr - text) + mbyte_blen >= len) 7468 { 7469 /* Past end of string to be displayed. */ 7470 nc = NUL; 7471 nc1 = NUL; 7472 } 7473 else 7474 { 7475 nc = utfc_ptr2char_len(ptr + mbyte_blen, pcc, 7476 (int)((text + len) - ptr - mbyte_blen)); 7477 nc1 = pcc[0]; 7478 } 7479 pc = prev_c; 7480 prev_c = u8c; 7481 u8c = arabic_shape(u8c, &c, &u8cc[0], nc, nc1, pc); 7482 } 7483 else 7484 prev_c = u8c; 7485 #endif 7486 if (col + mbyte_cells > screen_Columns) 7487 { 7488 /* Only 1 cell left, but character requires 2 cells: 7489 * display a '>' in the last column to avoid wrapping. */ 7490 c = '>'; 7491 mbyte_cells = 1; 7492 } 7493 } 7494 } 7495 7496 force_redraw_this = force_redraw_next; 7497 force_redraw_next = FALSE; 7498 7499 need_redraw = ScreenLines[off] != c 7500 || (mbyte_cells == 2 7501 && ScreenLines[off + 1] != (enc_dbcs ? ptr[1] : 0)) 7502 || (enc_dbcs == DBCS_JPNU 7503 && c == 0x8e 7504 && ScreenLines2[off] != ptr[1]) 7505 || (enc_utf8 7506 && (ScreenLinesUC[off] != 7507 (u8char_T)(c < 0x80 && u8cc[0] == 0 ? 0 : u8c) 7508 || (ScreenLinesUC[off] != 0 7509 && screen_comp_differs(off, u8cc)))) 7510 || ScreenAttrs[off] != attr 7511 || exmode_active; 7512 7513 if (need_redraw || force_redraw_this) 7514 { 7515 #if defined(FEAT_GUI) || defined(UNIX) 7516 /* The bold trick makes a single row of pixels appear in the next 7517 * character. When a bold character is removed, the next 7518 * character should be redrawn too. This happens for our own GUI 7519 * and for some xterms. */ 7520 if (need_redraw && ScreenLines[off] != ' ' && ( 7521 # ifdef FEAT_GUI 7522 gui.in_use 7523 # endif 7524 # if defined(FEAT_GUI) && defined(UNIX) 7525 || 7526 # endif 7527 # ifdef UNIX 7528 term_is_xterm 7529 # endif 7530 )) 7531 { 7532 int n = ScreenAttrs[off]; 7533 7534 if (n > HL_ALL) 7535 n = syn_attr2attr(n); 7536 if (n & HL_BOLD) 7537 force_redraw_next = TRUE; 7538 } 7539 #endif 7540 /* When at the end of the text and overwriting a two-cell 7541 * character with a one-cell character, need to clear the next 7542 * cell. Also when overwriting the left halve of a two-cell char 7543 * with the right halve of a two-cell char. Do this only once 7544 * (mb_off2cells() may return 2 on the right halve). */ 7545 if (clear_next_cell) 7546 clear_next_cell = FALSE; 7547 else if (has_mbyte 7548 && (len < 0 ? ptr[mbyte_blen] == NUL 7549 : ptr + mbyte_blen >= text + len) 7550 && ((mbyte_cells == 1 && (*mb_off2cells)(off, max_off) > 1) 7551 || (mbyte_cells == 2 7552 && (*mb_off2cells)(off, max_off) == 1 7553 && (*mb_off2cells)(off + 1, max_off) > 1))) 7554 clear_next_cell = TRUE; 7555 7556 /* Make sure we never leave a second byte of a double-byte behind, 7557 * it confuses mb_off2cells(). */ 7558 if (enc_dbcs 7559 && ((mbyte_cells == 1 && (*mb_off2cells)(off, max_off) > 1) 7560 || (mbyte_cells == 2 7561 && (*mb_off2cells)(off, max_off) == 1 7562 && (*mb_off2cells)(off + 1, max_off) > 1))) 7563 ScreenLines[off + mbyte_blen] = 0; 7564 ScreenLines[off] = c; 7565 ScreenAttrs[off] = attr; 7566 if (enc_utf8) 7567 { 7568 if (c < 0x80 && u8cc[0] == 0) 7569 ScreenLinesUC[off] = 0; 7570 else 7571 { 7572 int i; 7573 7574 ScreenLinesUC[off] = u8c; 7575 for (i = 0; i < Screen_mco; ++i) 7576 { 7577 ScreenLinesC[i][off] = u8cc[i]; 7578 if (u8cc[i] == 0) 7579 break; 7580 } 7581 } 7582 if (mbyte_cells == 2) 7583 { 7584 ScreenLines[off + 1] = 0; 7585 ScreenAttrs[off + 1] = attr; 7586 } 7587 screen_char(off, row, col); 7588 } 7589 else if (mbyte_cells == 2) 7590 { 7591 ScreenLines[off + 1] = ptr[1]; 7592 ScreenAttrs[off + 1] = attr; 7593 screen_char_2(off, row, col); 7594 } 7595 else if (enc_dbcs == DBCS_JPNU && c == 0x8e) 7596 { 7597 ScreenLines2[off] = ptr[1]; 7598 screen_char(off, row, col); 7599 } 7600 else 7601 screen_char(off, row, col); 7602 } 7603 if (has_mbyte) 7604 { 7605 off += mbyte_cells; 7606 col += mbyte_cells; 7607 ptr += mbyte_blen; 7608 if (clear_next_cell) 7609 { 7610 /* This only happens at the end, display one space next. */ 7611 ptr = (char_u *)" "; 7612 len = -1; 7613 } 7614 } 7615 else 7616 { 7617 ++off; 7618 ++col; 7619 ++ptr; 7620 } 7621 } 7622 7623 /* If we detected the next character needs to be redrawn, but the text 7624 * doesn't extend up to there, update the character here. */ 7625 if (force_redraw_next && col < screen_Columns) 7626 { 7627 if (enc_dbcs != 0 && dbcs_off2cells(off, max_off) > 1) 7628 screen_char_2(off, row, col); 7629 else 7630 screen_char(off, row, col); 7631 } 7632 } 7633 7634 #ifdef FEAT_SEARCH_EXTRA 7635 /* 7636 * Prepare for 'hlsearch' highlighting. 7637 */ 7638 static void 7639 start_search_hl(void) 7640 { 7641 if (p_hls && !no_hlsearch) 7642 { 7643 last_pat_prog(&search_hl.rm); 7644 search_hl.attr = HL_ATTR(HLF_L); 7645 # ifdef FEAT_RELTIME 7646 /* Set the time limit to 'redrawtime'. */ 7647 profile_setlimit(p_rdt, &search_hl.tm); 7648 # endif 7649 } 7650 } 7651 7652 /* 7653 * Clean up for 'hlsearch' highlighting. 7654 */ 7655 static void 7656 end_search_hl(void) 7657 { 7658 if (search_hl.rm.regprog != NULL) 7659 { 7660 vim_regfree(search_hl.rm.regprog); 7661 search_hl.rm.regprog = NULL; 7662 } 7663 } 7664 7665 /* 7666 * Init for calling prepare_search_hl(). 7667 */ 7668 static void 7669 init_search_hl(win_T *wp) 7670 { 7671 matchitem_T *cur; 7672 7673 /* Setup for match and 'hlsearch' highlighting. Disable any previous 7674 * match */ 7675 cur = wp->w_match_head; 7676 while (cur != NULL) 7677 { 7678 cur->hl.rm = cur->match; 7679 if (cur->hlg_id == 0) 7680 cur->hl.attr = 0; 7681 else 7682 cur->hl.attr = syn_id2attr(cur->hlg_id); 7683 cur->hl.buf = wp->w_buffer; 7684 cur->hl.lnum = 0; 7685 cur->hl.first_lnum = 0; 7686 # ifdef FEAT_RELTIME 7687 /* Set the time limit to 'redrawtime'. */ 7688 profile_setlimit(p_rdt, &(cur->hl.tm)); 7689 # endif 7690 cur = cur->next; 7691 } 7692 search_hl.buf = wp->w_buffer; 7693 search_hl.lnum = 0; 7694 search_hl.first_lnum = 0; 7695 /* time limit is set at the toplevel, for all windows */ 7696 } 7697 7698 /* 7699 * Advance to the match in window "wp" line "lnum" or past it. 7700 */ 7701 static void 7702 prepare_search_hl(win_T *wp, linenr_T lnum) 7703 { 7704 matchitem_T *cur; /* points to the match list */ 7705 match_T *shl; /* points to search_hl or a match */ 7706 int shl_flag; /* flag to indicate whether search_hl 7707 has been processed or not */ 7708 int pos_inprogress; /* marks that position match search is 7709 in progress */ 7710 int n; 7711 7712 /* 7713 * When using a multi-line pattern, start searching at the top 7714 * of the window or just after a closed fold. 7715 * Do this both for search_hl and the match list. 7716 */ 7717 cur = wp->w_match_head; 7718 shl_flag = FALSE; 7719 while (cur != NULL || shl_flag == FALSE) 7720 { 7721 if (shl_flag == FALSE) 7722 { 7723 shl = &search_hl; 7724 shl_flag = TRUE; 7725 } 7726 else 7727 shl = &cur->hl; 7728 if (shl->rm.regprog != NULL 7729 && shl->lnum == 0 7730 && re_multiline(shl->rm.regprog)) 7731 { 7732 if (shl->first_lnum == 0) 7733 { 7734 # ifdef FEAT_FOLDING 7735 for (shl->first_lnum = lnum; 7736 shl->first_lnum > wp->w_topline; --shl->first_lnum) 7737 if (hasFoldingWin(wp, shl->first_lnum - 1, 7738 NULL, NULL, TRUE, NULL)) 7739 break; 7740 # else 7741 shl->first_lnum = wp->w_topline; 7742 # endif 7743 } 7744 if (cur != NULL) 7745 cur->pos.cur = 0; 7746 pos_inprogress = TRUE; 7747 n = 0; 7748 while (shl->first_lnum < lnum && (shl->rm.regprog != NULL 7749 || (cur != NULL && pos_inprogress))) 7750 { 7751 next_search_hl(wp, shl, shl->first_lnum, (colnr_T)n, 7752 shl == &search_hl ? NULL : cur); 7753 pos_inprogress = cur == NULL || cur->pos.cur == 0 7754 ? FALSE : TRUE; 7755 if (shl->lnum != 0) 7756 { 7757 shl->first_lnum = shl->lnum 7758 + shl->rm.endpos[0].lnum 7759 - shl->rm.startpos[0].lnum; 7760 n = shl->rm.endpos[0].col; 7761 } 7762 else 7763 { 7764 ++shl->first_lnum; 7765 n = 0; 7766 } 7767 } 7768 } 7769 if (shl != &search_hl && cur != NULL) 7770 cur = cur->next; 7771 } 7772 } 7773 7774 /* 7775 * Search for a next 'hlsearch' or match. 7776 * Uses shl->buf. 7777 * Sets shl->lnum and shl->rm contents. 7778 * Note: Assumes a previous match is always before "lnum", unless 7779 * shl->lnum is zero. 7780 * Careful: Any pointers for buffer lines will become invalid. 7781 */ 7782 static void 7783 next_search_hl( 7784 win_T *win, 7785 match_T *shl, /* points to search_hl or a match */ 7786 linenr_T lnum, 7787 colnr_T mincol, /* minimal column for a match */ 7788 matchitem_T *cur) /* to retrieve match positions if any */ 7789 { 7790 linenr_T l; 7791 colnr_T matchcol; 7792 long nmatched; 7793 int save_called_emsg = called_emsg; 7794 7795 // for :{range}s/pat only highlight inside the range 7796 if (lnum < search_first_line || lnum > search_last_line) 7797 { 7798 shl->lnum = 0; 7799 return; 7800 } 7801 7802 if (shl->lnum != 0) 7803 { 7804 /* Check for three situations: 7805 * 1. If the "lnum" is below a previous match, start a new search. 7806 * 2. If the previous match includes "mincol", use it. 7807 * 3. Continue after the previous match. 7808 */ 7809 l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum; 7810 if (lnum > l) 7811 shl->lnum = 0; 7812 else if (lnum < l || shl->rm.endpos[0].col > mincol) 7813 return; 7814 } 7815 7816 /* 7817 * Repeat searching for a match until one is found that includes "mincol" 7818 * or none is found in this line. 7819 */ 7820 called_emsg = FALSE; 7821 for (;;) 7822 { 7823 #ifdef FEAT_RELTIME 7824 /* Stop searching after passing the time limit. */ 7825 if (profile_passed_limit(&(shl->tm))) 7826 { 7827 shl->lnum = 0; /* no match found in time */ 7828 break; 7829 } 7830 #endif 7831 /* Three situations: 7832 * 1. No useful previous match: search from start of line. 7833 * 2. Not Vi compatible or empty match: continue at next character. 7834 * Break the loop if this is beyond the end of the line. 7835 * 3. Vi compatible searching: continue at end of previous match. 7836 */ 7837 if (shl->lnum == 0) 7838 matchcol = 0; 7839 else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL 7840 || (shl->rm.endpos[0].lnum == 0 7841 && shl->rm.endpos[0].col <= shl->rm.startpos[0].col)) 7842 { 7843 char_u *ml; 7844 7845 matchcol = shl->rm.startpos[0].col; 7846 ml = ml_get_buf(shl->buf, lnum, FALSE) + matchcol; 7847 if (*ml == NUL) 7848 { 7849 ++matchcol; 7850 shl->lnum = 0; 7851 break; 7852 } 7853 if (has_mbyte) 7854 matchcol += mb_ptr2len(ml); 7855 else 7856 ++matchcol; 7857 } 7858 else 7859 matchcol = shl->rm.endpos[0].col; 7860 7861 shl->lnum = lnum; 7862 if (shl->rm.regprog != NULL) 7863 { 7864 /* Remember whether shl->rm is using a copy of the regprog in 7865 * cur->match. */ 7866 int regprog_is_copy = (shl != &search_hl && cur != NULL 7867 && shl == &cur->hl 7868 && cur->match.regprog == cur->hl.rm.regprog); 7869 int timed_out = FALSE; 7870 7871 nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum, 7872 matchcol, 7873 #ifdef FEAT_RELTIME 7874 &(shl->tm), &timed_out 7875 #else 7876 NULL, NULL 7877 #endif 7878 ); 7879 /* Copy the regprog, in case it got freed and recompiled. */ 7880 if (regprog_is_copy) 7881 cur->match.regprog = cur->hl.rm.regprog; 7882 7883 if (called_emsg || got_int || timed_out) 7884 { 7885 /* Error while handling regexp: stop using this regexp. */ 7886 if (shl == &search_hl) 7887 { 7888 /* don't free regprog in the match list, it's a copy */ 7889 vim_regfree(shl->rm.regprog); 7890 set_no_hlsearch(TRUE); 7891 } 7892 shl->rm.regprog = NULL; 7893 shl->lnum = 0; 7894 got_int = FALSE; /* avoid the "Type :quit to exit Vim" 7895 message */ 7896 break; 7897 } 7898 } 7899 else if (cur != NULL) 7900 nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol); 7901 else 7902 nmatched = 0; 7903 if (nmatched == 0) 7904 { 7905 shl->lnum = 0; /* no match found */ 7906 break; 7907 } 7908 if (shl->rm.startpos[0].lnum > 0 7909 || shl->rm.startpos[0].col >= mincol 7910 || nmatched > 1 7911 || shl->rm.endpos[0].col > mincol) 7912 { 7913 shl->lnum += shl->rm.startpos[0].lnum; 7914 break; /* useful match found */ 7915 } 7916 } 7917 7918 // Restore called_emsg for assert_fails(). 7919 called_emsg = save_called_emsg; 7920 } 7921 7922 /* 7923 * If there is a match fill "shl" and return one. 7924 * Return zero otherwise. 7925 */ 7926 static int 7927 next_search_hl_pos( 7928 match_T *shl, /* points to a match */ 7929 linenr_T lnum, 7930 posmatch_T *posmatch, /* match positions */ 7931 colnr_T mincol) /* minimal column for a match */ 7932 { 7933 int i; 7934 int found = -1; 7935 7936 for (i = posmatch->cur; i < MAXPOSMATCH; i++) 7937 { 7938 llpos_T *pos = &posmatch->pos[i]; 7939 7940 if (pos->lnum == 0) 7941 break; 7942 if (pos->len == 0 && pos->col < mincol) 7943 continue; 7944 if (pos->lnum == lnum) 7945 { 7946 if (found >= 0) 7947 { 7948 /* if this match comes before the one at "found" then swap 7949 * them */ 7950 if (pos->col < posmatch->pos[found].col) 7951 { 7952 llpos_T tmp = *pos; 7953 7954 *pos = posmatch->pos[found]; 7955 posmatch->pos[found] = tmp; 7956 } 7957 } 7958 else 7959 found = i; 7960 } 7961 } 7962 posmatch->cur = 0; 7963 if (found >= 0) 7964 { 7965 colnr_T start = posmatch->pos[found].col == 0 7966 ? 0 : posmatch->pos[found].col - 1; 7967 colnr_T end = posmatch->pos[found].col == 0 7968 ? MAXCOL : start + posmatch->pos[found].len; 7969 7970 shl->lnum = lnum; 7971 shl->rm.startpos[0].lnum = 0; 7972 shl->rm.startpos[0].col = start; 7973 shl->rm.endpos[0].lnum = 0; 7974 shl->rm.endpos[0].col = end; 7975 shl->is_addpos = TRUE; 7976 posmatch->cur = found + 1; 7977 return 1; 7978 } 7979 return 0; 7980 } 7981 #endif 7982 7983 static void 7984 screen_start_highlight(int attr) 7985 { 7986 attrentry_T *aep = NULL; 7987 7988 screen_attr = attr; 7989 if (full_screen 7990 #ifdef MSWIN 7991 && termcap_active 7992 #endif 7993 ) 7994 { 7995 #ifdef FEAT_GUI 7996 if (gui.in_use) 7997 { 7998 char buf[20]; 7999 8000 /* The GUI handles this internally. */ 8001 sprintf(buf, IF_EB("\033|%dh", ESC_STR "|%dh"), attr); 8002 OUT_STR(buf); 8003 } 8004 else 8005 #endif 8006 { 8007 if (attr > HL_ALL) /* special HL attr. */ 8008 { 8009 if (IS_CTERM) 8010 aep = syn_cterm_attr2entry(attr); 8011 else 8012 aep = syn_term_attr2entry(attr); 8013 if (aep == NULL) /* did ":syntax clear" */ 8014 attr = 0; 8015 else 8016 attr = aep->ae_attr; 8017 } 8018 if ((attr & HL_BOLD) && *T_MD != NUL) /* bold */ 8019 out_str(T_MD); 8020 else if (aep != NULL && cterm_normal_fg_bold && ( 8021 #ifdef FEAT_TERMGUICOLORS 8022 p_tgc && aep->ae_u.cterm.fg_rgb != CTERMCOLOR 8023 ? aep->ae_u.cterm.fg_rgb != INVALCOLOR 8024 : 8025 #endif 8026 t_colors > 1 && aep->ae_u.cterm.fg_color)) 8027 /* If the Normal FG color has BOLD attribute and the new HL 8028 * has a FG color defined, clear BOLD. */ 8029 out_str(T_ME); 8030 if ((attr & HL_STANDOUT) && *T_SO != NUL) /* standout */ 8031 out_str(T_SO); 8032 if ((attr & HL_UNDERCURL) && *T_UCS != NUL) /* undercurl */ 8033 out_str(T_UCS); 8034 if (((attr & HL_UNDERLINE) /* underline or undercurl */ 8035 || ((attr & HL_UNDERCURL) && *T_UCS == NUL)) 8036 && *T_US != NUL) 8037 out_str(T_US); 8038 if ((attr & HL_ITALIC) && *T_CZH != NUL) /* italic */ 8039 out_str(T_CZH); 8040 if ((attr & HL_INVERSE) && *T_MR != NUL) /* inverse (reverse) */ 8041 out_str(T_MR); 8042 if ((attr & HL_STRIKETHROUGH) && *T_STS != NUL) /* strike */ 8043 out_str(T_STS); 8044 8045 /* 8046 * Output the color or start string after bold etc., in case the 8047 * bold etc. override the color setting. 8048 */ 8049 if (aep != NULL) 8050 { 8051 #ifdef FEAT_TERMGUICOLORS 8052 /* When 'termguicolors' is set but fg or bg is unset, 8053 * fall back to the cterm colors. This helps for SpellBad, 8054 * where the GUI uses a red undercurl. */ 8055 if (p_tgc && aep->ae_u.cterm.fg_rgb != CTERMCOLOR) 8056 { 8057 if (aep->ae_u.cterm.fg_rgb != INVALCOLOR) 8058 term_fg_rgb_color(aep->ae_u.cterm.fg_rgb); 8059 } 8060 else 8061 #endif 8062 if (t_colors > 1) 8063 { 8064 if (aep->ae_u.cterm.fg_color) 8065 term_fg_color(aep->ae_u.cterm.fg_color - 1); 8066 } 8067 #ifdef FEAT_TERMGUICOLORS 8068 if (p_tgc && aep->ae_u.cterm.bg_rgb != CTERMCOLOR) 8069 { 8070 if (aep->ae_u.cterm.bg_rgb != INVALCOLOR) 8071 term_bg_rgb_color(aep->ae_u.cterm.bg_rgb); 8072 } 8073 else 8074 #endif 8075 if (t_colors > 1) 8076 { 8077 if (aep->ae_u.cterm.bg_color) 8078 term_bg_color(aep->ae_u.cterm.bg_color - 1); 8079 } 8080 8081 if (!IS_CTERM) 8082 { 8083 if (aep->ae_u.term.start != NULL) 8084 out_str(aep->ae_u.term.start); 8085 } 8086 } 8087 } 8088 } 8089 } 8090 8091 void 8092 screen_stop_highlight(void) 8093 { 8094 int do_ME = FALSE; /* output T_ME code */ 8095 8096 if (screen_attr != 0 8097 #ifdef MSWIN 8098 && termcap_active 8099 #endif 8100 ) 8101 { 8102 #ifdef FEAT_GUI 8103 if (gui.in_use) 8104 { 8105 char buf[20]; 8106 8107 /* use internal GUI code */ 8108 sprintf(buf, IF_EB("\033|%dH", ESC_STR "|%dH"), screen_attr); 8109 OUT_STR(buf); 8110 } 8111 else 8112 #endif 8113 { 8114 if (screen_attr > HL_ALL) /* special HL attr. */ 8115 { 8116 attrentry_T *aep; 8117 8118 if (IS_CTERM) 8119 { 8120 /* 8121 * Assume that t_me restores the original colors! 8122 */ 8123 aep = syn_cterm_attr2entry(screen_attr); 8124 if (aep != NULL && (( 8125 #ifdef FEAT_TERMGUICOLORS 8126 p_tgc && aep->ae_u.cterm.fg_rgb != CTERMCOLOR 8127 ? aep->ae_u.cterm.fg_rgb != INVALCOLOR 8128 : 8129 #endif 8130 aep->ae_u.cterm.fg_color) || ( 8131 #ifdef FEAT_TERMGUICOLORS 8132 p_tgc && aep->ae_u.cterm.bg_rgb != CTERMCOLOR 8133 ? aep->ae_u.cterm.bg_rgb != INVALCOLOR 8134 : 8135 #endif 8136 aep->ae_u.cterm.bg_color))) 8137 do_ME = TRUE; 8138 } 8139 else 8140 { 8141 aep = syn_term_attr2entry(screen_attr); 8142 if (aep != NULL && aep->ae_u.term.stop != NULL) 8143 { 8144 if (STRCMP(aep->ae_u.term.stop, T_ME) == 0) 8145 do_ME = TRUE; 8146 else 8147 out_str(aep->ae_u.term.stop); 8148 } 8149 } 8150 if (aep == NULL) /* did ":syntax clear" */ 8151 screen_attr = 0; 8152 else 8153 screen_attr = aep->ae_attr; 8154 } 8155 8156 /* 8157 * Often all ending-codes are equal to T_ME. Avoid outputting the 8158 * same sequence several times. 8159 */ 8160 if (screen_attr & HL_STANDOUT) 8161 { 8162 if (STRCMP(T_SE, T_ME) == 0) 8163 do_ME = TRUE; 8164 else 8165 out_str(T_SE); 8166 } 8167 if ((screen_attr & HL_UNDERCURL) && *T_UCE != NUL) 8168 { 8169 if (STRCMP(T_UCE, T_ME) == 0) 8170 do_ME = TRUE; 8171 else 8172 out_str(T_UCE); 8173 } 8174 if ((screen_attr & HL_UNDERLINE) 8175 || ((screen_attr & HL_UNDERCURL) && *T_UCE == NUL)) 8176 { 8177 if (STRCMP(T_UE, T_ME) == 0) 8178 do_ME = TRUE; 8179 else 8180 out_str(T_UE); 8181 } 8182 if (screen_attr & HL_ITALIC) 8183 { 8184 if (STRCMP(T_CZR, T_ME) == 0) 8185 do_ME = TRUE; 8186 else 8187 out_str(T_CZR); 8188 } 8189 if (screen_attr & HL_STRIKETHROUGH) 8190 { 8191 if (STRCMP(T_STE, T_ME) == 0) 8192 do_ME = TRUE; 8193 else 8194 out_str(T_STE); 8195 } 8196 if (do_ME || (screen_attr & (HL_BOLD | HL_INVERSE))) 8197 out_str(T_ME); 8198 8199 #ifdef FEAT_TERMGUICOLORS 8200 if (p_tgc) 8201 { 8202 if (cterm_normal_fg_gui_color != INVALCOLOR) 8203 term_fg_rgb_color(cterm_normal_fg_gui_color); 8204 if (cterm_normal_bg_gui_color != INVALCOLOR) 8205 term_bg_rgb_color(cterm_normal_bg_gui_color); 8206 } 8207 else 8208 #endif 8209 { 8210 if (t_colors > 1) 8211 { 8212 /* set Normal cterm colors */ 8213 if (cterm_normal_fg_color != 0) 8214 term_fg_color(cterm_normal_fg_color - 1); 8215 if (cterm_normal_bg_color != 0) 8216 term_bg_color(cterm_normal_bg_color - 1); 8217 if (cterm_normal_fg_bold) 8218 out_str(T_MD); 8219 } 8220 } 8221 } 8222 } 8223 screen_attr = 0; 8224 } 8225 8226 /* 8227 * Reset the colors for a cterm. Used when leaving Vim. 8228 * The machine specific code may override this again. 8229 */ 8230 void 8231 reset_cterm_colors(void) 8232 { 8233 if (IS_CTERM) 8234 { 8235 /* set Normal cterm colors */ 8236 #ifdef FEAT_TERMGUICOLORS 8237 if (p_tgc ? (cterm_normal_fg_gui_color != INVALCOLOR 8238 || cterm_normal_bg_gui_color != INVALCOLOR) 8239 : (cterm_normal_fg_color > 0 || cterm_normal_bg_color > 0)) 8240 #else 8241 if (cterm_normal_fg_color > 0 || cterm_normal_bg_color > 0) 8242 #endif 8243 { 8244 out_str(T_OP); 8245 screen_attr = -1; 8246 } 8247 if (cterm_normal_fg_bold) 8248 { 8249 out_str(T_ME); 8250 screen_attr = -1; 8251 } 8252 } 8253 } 8254 8255 /* 8256 * Put character ScreenLines["off"] on the screen at position "row" and "col", 8257 * using the attributes from ScreenAttrs["off"]. 8258 */ 8259 static void 8260 screen_char(unsigned off, int row, int col) 8261 { 8262 int attr; 8263 8264 /* Check for illegal values, just in case (could happen just after 8265 * resizing). */ 8266 if (row >= screen_Rows || col >= screen_Columns) 8267 return; 8268 8269 #ifdef FEAT_INS_EXPAND 8270 if (pum_under_menu(row, col)) 8271 return; 8272 #endif 8273 /* Outputting a character in the last cell on the screen may scroll the 8274 * screen up. Only do it when the "xn" termcap property is set, otherwise 8275 * mark the character invalid (update it when scrolled up). */ 8276 if (*T_XN == NUL 8277 && row == screen_Rows - 1 && col == screen_Columns - 1 8278 #ifdef FEAT_RIGHTLEFT 8279 /* account for first command-line character in rightleft mode */ 8280 && !cmdmsg_rl 8281 #endif 8282 ) 8283 { 8284 ScreenAttrs[off] = (sattr_T)-1; 8285 return; 8286 } 8287 8288 /* 8289 * Stop highlighting first, so it's easier to move the cursor. 8290 */ 8291 if (screen_char_attr != 0) 8292 attr = screen_char_attr; 8293 else 8294 attr = ScreenAttrs[off]; 8295 if (screen_attr != attr) 8296 screen_stop_highlight(); 8297 8298 windgoto(row, col); 8299 8300 if (screen_attr != attr) 8301 screen_start_highlight(attr); 8302 8303 if (enc_utf8 && ScreenLinesUC[off] != 0) 8304 { 8305 char_u buf[MB_MAXBYTES + 1]; 8306 8307 if (utf_ambiguous_width(ScreenLinesUC[off])) 8308 { 8309 if (*p_ambw == 'd' 8310 #ifdef FEAT_GUI 8311 && !gui.in_use 8312 #endif 8313 ) 8314 { 8315 /* Clear the two screen cells. If the character is actually 8316 * single width it won't change the second cell. */ 8317 out_str((char_u *)" "); 8318 term_windgoto(row, col); 8319 } 8320 /* not sure where the cursor is after drawing the ambiguous width 8321 * character */ 8322 screen_cur_col = 9999; 8323 } 8324 else if (utf_char2cells(ScreenLinesUC[off]) > 1) 8325 ++screen_cur_col; 8326 8327 /* Convert the UTF-8 character to bytes and write it. */ 8328 buf[utfc_char2bytes(off, buf)] = NUL; 8329 out_str(buf); 8330 } 8331 else 8332 { 8333 out_flush_check(); 8334 out_char(ScreenLines[off]); 8335 /* double-byte character in single-width cell */ 8336 if (enc_dbcs == DBCS_JPNU && ScreenLines[off] == 0x8e) 8337 out_char(ScreenLines2[off]); 8338 } 8339 8340 screen_cur_col++; 8341 } 8342 8343 /* 8344 * Used for enc_dbcs only: Put one double-wide character at ScreenLines["off"] 8345 * on the screen at position 'row' and 'col'. 8346 * The attributes of the first byte is used for all. This is required to 8347 * output the two bytes of a double-byte character with nothing in between. 8348 */ 8349 static void 8350 screen_char_2(unsigned off, int row, int col) 8351 { 8352 /* Check for illegal values (could be wrong when screen was resized). */ 8353 if (off + 1 >= (unsigned)(screen_Rows * screen_Columns)) 8354 return; 8355 8356 /* Outputting the last character on the screen may scrollup the screen. 8357 * Don't to it! Mark the character invalid (update it when scrolled up) */ 8358 if (row == screen_Rows - 1 && col >= screen_Columns - 2) 8359 { 8360 ScreenAttrs[off] = (sattr_T)-1; 8361 return; 8362 } 8363 8364 /* Output the first byte normally (positions the cursor), then write the 8365 * second byte directly. */ 8366 screen_char(off, row, col); 8367 out_char(ScreenLines[off + 1]); 8368 ++screen_cur_col; 8369 } 8370 8371 /* 8372 * Draw a rectangle of the screen, inverted when "invert" is TRUE. 8373 * This uses the contents of ScreenLines[] and doesn't change it. 8374 */ 8375 void 8376 screen_draw_rectangle( 8377 int row, 8378 int col, 8379 int height, 8380 int width, 8381 int invert) 8382 { 8383 int r, c; 8384 int off; 8385 int max_off; 8386 8387 /* Can't use ScreenLines unless initialized */ 8388 if (ScreenLines == NULL) 8389 return; 8390 8391 if (invert) 8392 screen_char_attr = HL_INVERSE; 8393 for (r = row; r < row + height; ++r) 8394 { 8395 off = LineOffset[r]; 8396 max_off = off + screen_Columns; 8397 for (c = col; c < col + width; ++c) 8398 { 8399 if (enc_dbcs != 0 && dbcs_off2cells(off + c, max_off) > 1) 8400 { 8401 screen_char_2(off + c, r, c); 8402 ++c; 8403 } 8404 else 8405 { 8406 screen_char(off + c, r, c); 8407 if (utf_off2cells(off + c, max_off) > 1) 8408 ++c; 8409 } 8410 } 8411 } 8412 screen_char_attr = 0; 8413 } 8414 8415 /* 8416 * Redraw the characters for a vertically split window. 8417 */ 8418 static void 8419 redraw_block(int row, int end, win_T *wp) 8420 { 8421 int col; 8422 int width; 8423 8424 # ifdef FEAT_CLIPBOARD 8425 clip_may_clear_selection(row, end - 1); 8426 # endif 8427 8428 if (wp == NULL) 8429 { 8430 col = 0; 8431 width = Columns; 8432 } 8433 else 8434 { 8435 col = wp->w_wincol; 8436 width = wp->w_width; 8437 } 8438 screen_draw_rectangle(row, col, end - row, width, FALSE); 8439 } 8440 8441 static void 8442 space_to_screenline(int off, int attr) 8443 { 8444 ScreenLines[off] = ' '; 8445 ScreenAttrs[off] = attr; 8446 if (enc_utf8) 8447 ScreenLinesUC[off] = 0; 8448 } 8449 8450 /* 8451 * Fill the screen from 'start_row' to 'end_row', from 'start_col' to 'end_col' 8452 * with character 'c1' in first column followed by 'c2' in the other columns. 8453 * Use attributes 'attr'. 8454 */ 8455 void 8456 screen_fill( 8457 int start_row, 8458 int end_row, 8459 int start_col, 8460 int end_col, 8461 int c1, 8462 int c2, 8463 int attr) 8464 { 8465 int row; 8466 int col; 8467 int off; 8468 int end_off; 8469 int did_delete; 8470 int c; 8471 int norm_term; 8472 #if defined(FEAT_GUI) || defined(UNIX) 8473 int force_next = FALSE; 8474 #endif 8475 8476 if (end_row > screen_Rows) /* safety check */ 8477 end_row = screen_Rows; 8478 if (end_col > screen_Columns) /* safety check */ 8479 end_col = screen_Columns; 8480 if (ScreenLines == NULL 8481 || start_row >= end_row 8482 || start_col >= end_col) /* nothing to do */ 8483 return; 8484 8485 /* it's a "normal" terminal when not in a GUI or cterm */ 8486 norm_term = ( 8487 #ifdef FEAT_GUI 8488 !gui.in_use && 8489 #endif 8490 !IS_CTERM); 8491 for (row = start_row; row < end_row; ++row) 8492 { 8493 if (has_mbyte 8494 #ifdef FEAT_GUI 8495 && !gui.in_use 8496 #endif 8497 ) 8498 { 8499 /* When drawing over the right halve of a double-wide char clear 8500 * out the left halve. When drawing over the left halve of a 8501 * double wide-char clear out the right halve. Only needed in a 8502 * terminal. */ 8503 if (start_col > 0 && mb_fix_col(start_col, row) != start_col) 8504 screen_puts_len((char_u *)" ", 1, row, start_col - 1, 0); 8505 if (end_col < screen_Columns && mb_fix_col(end_col, row) != end_col) 8506 screen_puts_len((char_u *)" ", 1, row, end_col, 0); 8507 } 8508 /* 8509 * Try to use delete-line termcap code, when no attributes or in a 8510 * "normal" terminal, where a bold/italic space is just a 8511 * space. 8512 */ 8513 did_delete = FALSE; 8514 if (c2 == ' ' 8515 && end_col == Columns 8516 && can_clear(T_CE) 8517 && (attr == 0 8518 || (norm_term 8519 && attr <= HL_ALL 8520 && ((attr & ~(HL_BOLD | HL_ITALIC)) == 0)))) 8521 { 8522 /* 8523 * check if we really need to clear something 8524 */ 8525 col = start_col; 8526 if (c1 != ' ') /* don't clear first char */ 8527 ++col; 8528 8529 off = LineOffset[row] + col; 8530 end_off = LineOffset[row] + end_col; 8531 8532 /* skip blanks (used often, keep it fast!) */ 8533 if (enc_utf8) 8534 while (off < end_off && ScreenLines[off] == ' ' 8535 && ScreenAttrs[off] == 0 && ScreenLinesUC[off] == 0) 8536 ++off; 8537 else 8538 while (off < end_off && ScreenLines[off] == ' ' 8539 && ScreenAttrs[off] == 0) 8540 ++off; 8541 if (off < end_off) /* something to be cleared */ 8542 { 8543 col = off - LineOffset[row]; 8544 screen_stop_highlight(); 8545 term_windgoto(row, col);/* clear rest of this screen line */ 8546 out_str(T_CE); 8547 screen_start(); /* don't know where cursor is now */ 8548 col = end_col - col; 8549 while (col--) /* clear chars in ScreenLines */ 8550 { 8551 space_to_screenline(off, 0); 8552 ++off; 8553 } 8554 } 8555 did_delete = TRUE; /* the chars are cleared now */ 8556 } 8557 8558 off = LineOffset[row] + start_col; 8559 c = c1; 8560 for (col = start_col; col < end_col; ++col) 8561 { 8562 if (ScreenLines[off] != c 8563 || (enc_utf8 && (int)ScreenLinesUC[off] 8564 != (c >= 0x80 ? c : 0)) 8565 || ScreenAttrs[off] != attr 8566 #if defined(FEAT_GUI) || defined(UNIX) 8567 || force_next 8568 #endif 8569 ) 8570 { 8571 #if defined(FEAT_GUI) || defined(UNIX) 8572 /* The bold trick may make a single row of pixels appear in 8573 * the next character. When a bold character is removed, the 8574 * next character should be redrawn too. This happens for our 8575 * own GUI and for some xterms. */ 8576 if ( 8577 # ifdef FEAT_GUI 8578 gui.in_use 8579 # endif 8580 # if defined(FEAT_GUI) && defined(UNIX) 8581 || 8582 # endif 8583 # ifdef UNIX 8584 term_is_xterm 8585 # endif 8586 ) 8587 { 8588 if (ScreenLines[off] != ' ' 8589 && (ScreenAttrs[off] > HL_ALL 8590 || ScreenAttrs[off] & HL_BOLD)) 8591 force_next = TRUE; 8592 else 8593 force_next = FALSE; 8594 } 8595 #endif 8596 ScreenLines[off] = c; 8597 if (enc_utf8) 8598 { 8599 if (c >= 0x80) 8600 { 8601 ScreenLinesUC[off] = c; 8602 ScreenLinesC[0][off] = 0; 8603 } 8604 else 8605 ScreenLinesUC[off] = 0; 8606 } 8607 ScreenAttrs[off] = attr; 8608 if (!did_delete || c != ' ') 8609 screen_char(off, row, col); 8610 } 8611 ++off; 8612 if (col == start_col) 8613 { 8614 if (did_delete) 8615 break; 8616 c = c2; 8617 } 8618 } 8619 if (end_col == Columns) 8620 LineWraps[row] = FALSE; 8621 if (row == Rows - 1) /* overwritten the command line */ 8622 { 8623 redraw_cmdline = TRUE; 8624 if (start_col == 0 && end_col == Columns 8625 && c1 == ' ' && c2 == ' ' && attr == 0) 8626 clear_cmdline = FALSE; /* command line has been cleared */ 8627 if (start_col == 0) 8628 mode_displayed = FALSE; /* mode cleared or overwritten */ 8629 } 8630 } 8631 } 8632 8633 /* 8634 * Check if there should be a delay. Used before clearing or redrawing the 8635 * screen or the command line. 8636 */ 8637 void 8638 check_for_delay(int check_msg_scroll) 8639 { 8640 if ((emsg_on_display || (check_msg_scroll && msg_scroll)) 8641 && !did_wait_return 8642 && emsg_silent == 0) 8643 { 8644 out_flush(); 8645 ui_delay(1000L, TRUE); 8646 emsg_on_display = FALSE; 8647 if (check_msg_scroll) 8648 msg_scroll = FALSE; 8649 } 8650 } 8651 8652 /* 8653 * Init TabPageIdxs[] to zero: Clicking outside of tabs has no effect. 8654 */ 8655 static void 8656 clear_TabPageIdxs(void) 8657 { 8658 int scol; 8659 8660 for (scol = 0; scol < Columns; ++scol) 8661 TabPageIdxs[scol] = 0; 8662 } 8663 8664 /* 8665 * screen_valid - allocate screen buffers if size changed 8666 * If "doclear" is TRUE: clear screen if it has been resized. 8667 * Returns TRUE if there is a valid screen to write to. 8668 * Returns FALSE when starting up and screen not initialized yet. 8669 */ 8670 int 8671 screen_valid(int doclear) 8672 { 8673 screenalloc(doclear); /* allocate screen buffers if size changed */ 8674 return (ScreenLines != NULL); 8675 } 8676 8677 /* 8678 * Resize the shell to Rows and Columns. 8679 * Allocate ScreenLines[] and associated items. 8680 * 8681 * There may be some time between setting Rows and Columns and (re)allocating 8682 * ScreenLines[]. This happens when starting up and when (manually) changing 8683 * the shell size. Always use screen_Rows and screen_Columns to access items 8684 * in ScreenLines[]. Use Rows and Columns for positioning text etc. where the 8685 * final size of the shell is needed. 8686 */ 8687 void 8688 screenalloc(int doclear) 8689 { 8690 int new_row, old_row; 8691 #ifdef FEAT_GUI 8692 int old_Rows; 8693 #endif 8694 win_T *wp; 8695 int outofmem = FALSE; 8696 int len; 8697 schar_T *new_ScreenLines; 8698 u8char_T *new_ScreenLinesUC = NULL; 8699 u8char_T *new_ScreenLinesC[MAX_MCO]; 8700 schar_T *new_ScreenLines2 = NULL; 8701 int i; 8702 sattr_T *new_ScreenAttrs; 8703 unsigned *new_LineOffset; 8704 char_u *new_LineWraps; 8705 short *new_TabPageIdxs; 8706 tabpage_T *tp; 8707 static int entered = FALSE; /* avoid recursiveness */ 8708 static int done_outofmem_msg = FALSE; /* did outofmem message */ 8709 int retry_count = 0; 8710 8711 retry: 8712 /* 8713 * Allocation of the screen buffers is done only when the size changes and 8714 * when Rows and Columns have been set and we have started doing full 8715 * screen stuff. 8716 */ 8717 if ((ScreenLines != NULL 8718 && Rows == screen_Rows 8719 && Columns == screen_Columns 8720 && enc_utf8 == (ScreenLinesUC != NULL) 8721 && (enc_dbcs == DBCS_JPNU) == (ScreenLines2 != NULL) 8722 && p_mco == Screen_mco) 8723 || Rows == 0 8724 || Columns == 0 8725 || (!full_screen && ScreenLines == NULL)) 8726 return; 8727 8728 /* 8729 * It's possible that we produce an out-of-memory message below, which 8730 * will cause this function to be called again. To break the loop, just 8731 * return here. 8732 */ 8733 if (entered) 8734 return; 8735 entered = TRUE; 8736 8737 /* 8738 * Note that the window sizes are updated before reallocating the arrays, 8739 * thus we must not redraw here! 8740 */ 8741 ++RedrawingDisabled; 8742 8743 win_new_shellsize(); /* fit the windows in the new sized shell */ 8744 8745 comp_col(); /* recompute columns for shown command and ruler */ 8746 8747 /* 8748 * We're changing the size of the screen. 8749 * - Allocate new arrays for ScreenLines and ScreenAttrs. 8750 * - Move lines from the old arrays into the new arrays, clear extra 8751 * lines (unless the screen is going to be cleared). 8752 * - Free the old arrays. 8753 * 8754 * If anything fails, make ScreenLines NULL, so we don't do anything! 8755 * Continuing with the old ScreenLines may result in a crash, because the 8756 * size is wrong. 8757 */ 8758 FOR_ALL_TAB_WINDOWS(tp, wp) 8759 win_free_lsize(wp); 8760 if (aucmd_win != NULL) 8761 win_free_lsize(aucmd_win); 8762 8763 new_ScreenLines = (schar_T *)lalloc((long_u)( 8764 (Rows + 1) * Columns * sizeof(schar_T)), FALSE); 8765 vim_memset(new_ScreenLinesC, 0, sizeof(u8char_T *) * MAX_MCO); 8766 if (enc_utf8) 8767 { 8768 new_ScreenLinesUC = (u8char_T *)lalloc((long_u)( 8769 (Rows + 1) * Columns * sizeof(u8char_T)), FALSE); 8770 for (i = 0; i < p_mco; ++i) 8771 new_ScreenLinesC[i] = (u8char_T *)lalloc_clear((long_u)( 8772 (Rows + 1) * Columns * sizeof(u8char_T)), FALSE); 8773 } 8774 if (enc_dbcs == DBCS_JPNU) 8775 new_ScreenLines2 = (schar_T *)lalloc((long_u)( 8776 (Rows + 1) * Columns * sizeof(schar_T)), FALSE); 8777 new_ScreenAttrs = (sattr_T *)lalloc((long_u)( 8778 (Rows + 1) * Columns * sizeof(sattr_T)), FALSE); 8779 new_LineOffset = (unsigned *)lalloc((long_u)( 8780 Rows * sizeof(unsigned)), FALSE); 8781 new_LineWraps = (char_u *)lalloc((long_u)(Rows * sizeof(char_u)), FALSE); 8782 new_TabPageIdxs = (short *)lalloc((long_u)(Columns * sizeof(short)), FALSE); 8783 8784 FOR_ALL_TAB_WINDOWS(tp, wp) 8785 { 8786 if (win_alloc_lines(wp) == FAIL) 8787 { 8788 outofmem = TRUE; 8789 goto give_up; 8790 } 8791 } 8792 if (aucmd_win != NULL && aucmd_win->w_lines == NULL 8793 && win_alloc_lines(aucmd_win) == FAIL) 8794 outofmem = TRUE; 8795 give_up: 8796 8797 for (i = 0; i < p_mco; ++i) 8798 if (new_ScreenLinesC[i] == NULL) 8799 break; 8800 if (new_ScreenLines == NULL 8801 || (enc_utf8 && (new_ScreenLinesUC == NULL || i != p_mco)) 8802 || (enc_dbcs == DBCS_JPNU && new_ScreenLines2 == NULL) 8803 || new_ScreenAttrs == NULL 8804 || new_LineOffset == NULL 8805 || new_LineWraps == NULL 8806 || new_TabPageIdxs == NULL 8807 || outofmem) 8808 { 8809 if (ScreenLines != NULL || !done_outofmem_msg) 8810 { 8811 /* guess the size */ 8812 do_outofmem_msg((long_u)((Rows + 1) * Columns)); 8813 8814 /* Remember we did this to avoid getting outofmem messages over 8815 * and over again. */ 8816 done_outofmem_msg = TRUE; 8817 } 8818 VIM_CLEAR(new_ScreenLines); 8819 VIM_CLEAR(new_ScreenLinesUC); 8820 for (i = 0; i < p_mco; ++i) 8821 VIM_CLEAR(new_ScreenLinesC[i]); 8822 VIM_CLEAR(new_ScreenLines2); 8823 VIM_CLEAR(new_ScreenAttrs); 8824 VIM_CLEAR(new_LineOffset); 8825 VIM_CLEAR(new_LineWraps); 8826 VIM_CLEAR(new_TabPageIdxs); 8827 } 8828 else 8829 { 8830 done_outofmem_msg = FALSE; 8831 8832 for (new_row = 0; new_row < Rows; ++new_row) 8833 { 8834 new_LineOffset[new_row] = new_row * Columns; 8835 new_LineWraps[new_row] = FALSE; 8836 8837 /* 8838 * If the screen is not going to be cleared, copy as much as 8839 * possible from the old screen to the new one and clear the rest 8840 * (used when resizing the window at the "--more--" prompt or when 8841 * executing an external command, for the GUI). 8842 */ 8843 if (!doclear) 8844 { 8845 (void)vim_memset(new_ScreenLines + new_row * Columns, 8846 ' ', (size_t)Columns * sizeof(schar_T)); 8847 if (enc_utf8) 8848 { 8849 (void)vim_memset(new_ScreenLinesUC + new_row * Columns, 8850 0, (size_t)Columns * sizeof(u8char_T)); 8851 for (i = 0; i < p_mco; ++i) 8852 (void)vim_memset(new_ScreenLinesC[i] 8853 + new_row * Columns, 8854 0, (size_t)Columns * sizeof(u8char_T)); 8855 } 8856 if (enc_dbcs == DBCS_JPNU) 8857 (void)vim_memset(new_ScreenLines2 + new_row * Columns, 8858 0, (size_t)Columns * sizeof(schar_T)); 8859 (void)vim_memset(new_ScreenAttrs + new_row * Columns, 8860 0, (size_t)Columns * sizeof(sattr_T)); 8861 old_row = new_row + (screen_Rows - Rows); 8862 if (old_row >= 0 && ScreenLines != NULL) 8863 { 8864 if (screen_Columns < Columns) 8865 len = screen_Columns; 8866 else 8867 len = Columns; 8868 /* When switching to utf-8 don't copy characters, they 8869 * may be invalid now. Also when p_mco changes. */ 8870 if (!(enc_utf8 && ScreenLinesUC == NULL) 8871 && p_mco == Screen_mco) 8872 mch_memmove(new_ScreenLines + new_LineOffset[new_row], 8873 ScreenLines + LineOffset[old_row], 8874 (size_t)len * sizeof(schar_T)); 8875 if (enc_utf8 && ScreenLinesUC != NULL 8876 && p_mco == Screen_mco) 8877 { 8878 mch_memmove(new_ScreenLinesUC + new_LineOffset[new_row], 8879 ScreenLinesUC + LineOffset[old_row], 8880 (size_t)len * sizeof(u8char_T)); 8881 for (i = 0; i < p_mco; ++i) 8882 mch_memmove(new_ScreenLinesC[i] 8883 + new_LineOffset[new_row], 8884 ScreenLinesC[i] + LineOffset[old_row], 8885 (size_t)len * sizeof(u8char_T)); 8886 } 8887 if (enc_dbcs == DBCS_JPNU && ScreenLines2 != NULL) 8888 mch_memmove(new_ScreenLines2 + new_LineOffset[new_row], 8889 ScreenLines2 + LineOffset[old_row], 8890 (size_t)len * sizeof(schar_T)); 8891 mch_memmove(new_ScreenAttrs + new_LineOffset[new_row], 8892 ScreenAttrs + LineOffset[old_row], 8893 (size_t)len * sizeof(sattr_T)); 8894 } 8895 } 8896 } 8897 /* Use the last line of the screen for the current line. */ 8898 current_ScreenLine = new_ScreenLines + Rows * Columns; 8899 } 8900 8901 free_screenlines(); 8902 8903 ScreenLines = new_ScreenLines; 8904 ScreenLinesUC = new_ScreenLinesUC; 8905 for (i = 0; i < p_mco; ++i) 8906 ScreenLinesC[i] = new_ScreenLinesC[i]; 8907 Screen_mco = p_mco; 8908 ScreenLines2 = new_ScreenLines2; 8909 ScreenAttrs = new_ScreenAttrs; 8910 LineOffset = new_LineOffset; 8911 LineWraps = new_LineWraps; 8912 TabPageIdxs = new_TabPageIdxs; 8913 8914 /* It's important that screen_Rows and screen_Columns reflect the actual 8915 * size of ScreenLines[]. Set them before calling anything. */ 8916 #ifdef FEAT_GUI 8917 old_Rows = screen_Rows; 8918 #endif 8919 screen_Rows = Rows; 8920 screen_Columns = Columns; 8921 8922 must_redraw = CLEAR; /* need to clear the screen later */ 8923 if (doclear) 8924 screenclear2(); 8925 #ifdef FEAT_GUI 8926 else if (gui.in_use 8927 && !gui.starting 8928 && ScreenLines != NULL 8929 && old_Rows != Rows) 8930 { 8931 (void)gui_redraw_block(0, 0, (int)Rows - 1, (int)Columns - 1, 0); 8932 /* 8933 * Adjust the position of the cursor, for when executing an external 8934 * command. 8935 */ 8936 if (msg_row >= Rows) /* Rows got smaller */ 8937 msg_row = Rows - 1; /* put cursor at last row */ 8938 else if (Rows > old_Rows) /* Rows got bigger */ 8939 msg_row += Rows - old_Rows; /* put cursor in same place */ 8940 if (msg_col >= Columns) /* Columns got smaller */ 8941 msg_col = Columns - 1; /* put cursor at last column */ 8942 } 8943 #endif 8944 clear_TabPageIdxs(); 8945 8946 entered = FALSE; 8947 --RedrawingDisabled; 8948 8949 /* 8950 * Do not apply autocommands more than 3 times to avoid an endless loop 8951 * in case applying autocommands always changes Rows or Columns. 8952 */ 8953 if (starting == 0 && ++retry_count <= 3) 8954 { 8955 apply_autocmds(EVENT_VIMRESIZED, NULL, NULL, FALSE, curbuf); 8956 /* In rare cases, autocommands may have altered Rows or Columns, 8957 * jump back to check if we need to allocate the screen again. */ 8958 goto retry; 8959 } 8960 } 8961 8962 void 8963 free_screenlines(void) 8964 { 8965 int i; 8966 8967 vim_free(ScreenLinesUC); 8968 for (i = 0; i < Screen_mco; ++i) 8969 vim_free(ScreenLinesC[i]); 8970 vim_free(ScreenLines2); 8971 vim_free(ScreenLines); 8972 vim_free(ScreenAttrs); 8973 vim_free(LineOffset); 8974 vim_free(LineWraps); 8975 vim_free(TabPageIdxs); 8976 } 8977 8978 void 8979 screenclear(void) 8980 { 8981 check_for_delay(FALSE); 8982 screenalloc(FALSE); /* allocate screen buffers if size changed */ 8983 screenclear2(); /* clear the screen */ 8984 } 8985 8986 static void 8987 screenclear2(void) 8988 { 8989 int i; 8990 8991 if (starting == NO_SCREEN || ScreenLines == NULL 8992 #ifdef FEAT_GUI 8993 || (gui.in_use && gui.starting) 8994 #endif 8995 ) 8996 return; 8997 8998 #ifdef FEAT_GUI 8999 if (!gui.in_use) 9000 #endif 9001 screen_attr = -1; /* force setting the Normal colors */ 9002 screen_stop_highlight(); /* don't want highlighting here */ 9003 9004 #ifdef FEAT_CLIPBOARD 9005 /* disable selection without redrawing it */ 9006 clip_scroll_selection(9999); 9007 #endif 9008 9009 /* blank out ScreenLines */ 9010 for (i = 0; i < Rows; ++i) 9011 { 9012 lineclear(LineOffset[i], (int)Columns, 0); 9013 LineWraps[i] = FALSE; 9014 } 9015 9016 if (can_clear(T_CL)) 9017 { 9018 out_str(T_CL); /* clear the display */ 9019 clear_cmdline = FALSE; 9020 mode_displayed = FALSE; 9021 } 9022 else 9023 { 9024 /* can't clear the screen, mark all chars with invalid attributes */ 9025 for (i = 0; i < Rows; ++i) 9026 lineinvalid(LineOffset[i], (int)Columns); 9027 clear_cmdline = TRUE; 9028 } 9029 9030 screen_cleared = TRUE; /* can use contents of ScreenLines now */ 9031 9032 win_rest_invalid(firstwin); 9033 redraw_cmdline = TRUE; 9034 redraw_tabline = TRUE; 9035 if (must_redraw == CLEAR) /* no need to clear again */ 9036 must_redraw = NOT_VALID; 9037 compute_cmdrow(); 9038 msg_row = cmdline_row; /* put cursor on last line for messages */ 9039 msg_col = 0; 9040 screen_start(); /* don't know where cursor is now */ 9041 msg_scrolled = 0; /* can't scroll back */ 9042 msg_didany = FALSE; 9043 msg_didout = FALSE; 9044 } 9045 9046 /* 9047 * Clear one line in ScreenLines. 9048 */ 9049 static void 9050 lineclear(unsigned off, int width, int attr) 9051 { 9052 (void)vim_memset(ScreenLines + off, ' ', (size_t)width * sizeof(schar_T)); 9053 if (enc_utf8) 9054 (void)vim_memset(ScreenLinesUC + off, 0, 9055 (size_t)width * sizeof(u8char_T)); 9056 (void)vim_memset(ScreenAttrs + off, attr, (size_t)width * sizeof(sattr_T)); 9057 } 9058 9059 /* 9060 * Mark one line in ScreenLines invalid by setting the attributes to an 9061 * invalid value. 9062 */ 9063 static void 9064 lineinvalid(unsigned off, int width) 9065 { 9066 (void)vim_memset(ScreenAttrs + off, -1, (size_t)width * sizeof(sattr_T)); 9067 } 9068 9069 /* 9070 * Copy part of a Screenline for vertically split window "wp". 9071 */ 9072 static void 9073 linecopy(int to, int from, win_T *wp) 9074 { 9075 unsigned off_to = LineOffset[to] + wp->w_wincol; 9076 unsigned off_from = LineOffset[from] + wp->w_wincol; 9077 9078 mch_memmove(ScreenLines + off_to, ScreenLines + off_from, 9079 wp->w_width * sizeof(schar_T)); 9080 if (enc_utf8) 9081 { 9082 int i; 9083 9084 mch_memmove(ScreenLinesUC + off_to, ScreenLinesUC + off_from, 9085 wp->w_width * sizeof(u8char_T)); 9086 for (i = 0; i < p_mco; ++i) 9087 mch_memmove(ScreenLinesC[i] + off_to, ScreenLinesC[i] + off_from, 9088 wp->w_width * sizeof(u8char_T)); 9089 } 9090 if (enc_dbcs == DBCS_JPNU) 9091 mch_memmove(ScreenLines2 + off_to, ScreenLines2 + off_from, 9092 wp->w_width * sizeof(schar_T)); 9093 mch_memmove(ScreenAttrs + off_to, ScreenAttrs + off_from, 9094 wp->w_width * sizeof(sattr_T)); 9095 } 9096 9097 /* 9098 * Return TRUE if clearing with term string "p" would work. 9099 * It can't work when the string is empty or it won't set the right background. 9100 */ 9101 int 9102 can_clear(char_u *p) 9103 { 9104 return (*p != NUL && (t_colors <= 1 9105 #ifdef FEAT_GUI 9106 || gui.in_use 9107 #endif 9108 #ifdef FEAT_TERMGUICOLORS 9109 || (p_tgc && cterm_normal_bg_gui_color == INVALCOLOR) 9110 || (!p_tgc && cterm_normal_bg_color == 0) 9111 #else 9112 || cterm_normal_bg_color == 0 9113 #endif 9114 || *T_UT != NUL)); 9115 } 9116 9117 /* 9118 * Reset cursor position. Use whenever cursor was moved because of outputting 9119 * something directly to the screen (shell commands) or a terminal control 9120 * code. 9121 */ 9122 void 9123 screen_start(void) 9124 { 9125 screen_cur_row = screen_cur_col = 9999; 9126 } 9127 9128 /* 9129 * Move the cursor to position "row","col" in the screen. 9130 * This tries to find the most efficient way to move, minimizing the number of 9131 * characters sent to the terminal. 9132 */ 9133 void 9134 windgoto(int row, int col) 9135 { 9136 sattr_T *p; 9137 int i; 9138 int plan; 9139 int cost; 9140 int wouldbe_col; 9141 int noinvcurs; 9142 char_u *bs; 9143 int goto_cost; 9144 int attr; 9145 9146 #define GOTO_COST 7 /* assume a term_windgoto() takes about 7 chars */ 9147 #define HIGHL_COST 5 /* assume unhighlight takes 5 chars */ 9148 9149 #define PLAN_LE 1 9150 #define PLAN_CR 2 9151 #define PLAN_NL 3 9152 #define PLAN_WRITE 4 9153 /* Can't use ScreenLines unless initialized */ 9154 if (ScreenLines == NULL) 9155 return; 9156 9157 if (col != screen_cur_col || row != screen_cur_row) 9158 { 9159 /* Check for valid position. */ 9160 if (row < 0) /* window without text lines? */ 9161 row = 0; 9162 if (row >= screen_Rows) 9163 row = screen_Rows - 1; 9164 if (col >= screen_Columns) 9165 col = screen_Columns - 1; 9166 9167 /* check if no cursor movement is allowed in highlight mode */ 9168 if (screen_attr && *T_MS == NUL) 9169 noinvcurs = HIGHL_COST; 9170 else 9171 noinvcurs = 0; 9172 goto_cost = GOTO_COST + noinvcurs; 9173 9174 /* 9175 * Plan how to do the positioning: 9176 * 1. Use CR to move it to column 0, same row. 9177 * 2. Use T_LE to move it a few columns to the left. 9178 * 3. Use NL to move a few lines down, column 0. 9179 * 4. Move a few columns to the right with T_ND or by writing chars. 9180 * 9181 * Don't do this if the cursor went beyond the last column, the cursor 9182 * position is unknown then (some terminals wrap, some don't ) 9183 * 9184 * First check if the highlighting attributes allow us to write 9185 * characters to move the cursor to the right. 9186 */ 9187 if (row >= screen_cur_row && screen_cur_col < Columns) 9188 { 9189 /* 9190 * If the cursor is in the same row, bigger col, we can use CR 9191 * or T_LE. 9192 */ 9193 bs = NULL; /* init for GCC */ 9194 attr = screen_attr; 9195 if (row == screen_cur_row && col < screen_cur_col) 9196 { 9197 /* "le" is preferred over "bc", because "bc" is obsolete */ 9198 if (*T_LE) 9199 bs = T_LE; /* "cursor left" */ 9200 else 9201 bs = T_BC; /* "backspace character (old) */ 9202 if (*bs) 9203 cost = (screen_cur_col - col) * (int)STRLEN(bs); 9204 else 9205 cost = 999; 9206 if (col + 1 < cost) /* using CR is less characters */ 9207 { 9208 plan = PLAN_CR; 9209 wouldbe_col = 0; 9210 cost = 1; /* CR is just one character */ 9211 } 9212 else 9213 { 9214 plan = PLAN_LE; 9215 wouldbe_col = col; 9216 } 9217 if (noinvcurs) /* will stop highlighting */ 9218 { 9219 cost += noinvcurs; 9220 attr = 0; 9221 } 9222 } 9223 9224 /* 9225 * If the cursor is above where we want to be, we can use CR LF. 9226 */ 9227 else if (row > screen_cur_row) 9228 { 9229 plan = PLAN_NL; 9230 wouldbe_col = 0; 9231 cost = (row - screen_cur_row) * 2; /* CR LF */ 9232 if (noinvcurs) /* will stop highlighting */ 9233 { 9234 cost += noinvcurs; 9235 attr = 0; 9236 } 9237 } 9238 9239 /* 9240 * If the cursor is in the same row, smaller col, just use write. 9241 */ 9242 else 9243 { 9244 plan = PLAN_WRITE; 9245 wouldbe_col = screen_cur_col; 9246 cost = 0; 9247 } 9248 9249 /* 9250 * Check if any characters that need to be written have the 9251 * correct attributes. Also avoid UTF-8 characters. 9252 */ 9253 i = col - wouldbe_col; 9254 if (i > 0) 9255 cost += i; 9256 if (cost < goto_cost && i > 0) 9257 { 9258 /* 9259 * Check if the attributes are correct without additionally 9260 * stopping highlighting. 9261 */ 9262 p = ScreenAttrs + LineOffset[row] + wouldbe_col; 9263 while (i && *p++ == attr) 9264 --i; 9265 if (i != 0) 9266 { 9267 /* 9268 * Try if it works when highlighting is stopped here. 9269 */ 9270 if (*--p == 0) 9271 { 9272 cost += noinvcurs; 9273 while (i && *p++ == 0) 9274 --i; 9275 } 9276 if (i != 0) 9277 cost = 999; /* different attributes, don't do it */ 9278 } 9279 if (enc_utf8) 9280 { 9281 /* Don't use an UTF-8 char for positioning, it's slow. */ 9282 for (i = wouldbe_col; i < col; ++i) 9283 if (ScreenLinesUC[LineOffset[row] + i] != 0) 9284 { 9285 cost = 999; 9286 break; 9287 } 9288 } 9289 } 9290 9291 /* 9292 * We can do it without term_windgoto()! 9293 */ 9294 if (cost < goto_cost) 9295 { 9296 if (plan == PLAN_LE) 9297 { 9298 if (noinvcurs) 9299 screen_stop_highlight(); 9300 while (screen_cur_col > col) 9301 { 9302 out_str(bs); 9303 --screen_cur_col; 9304 } 9305 } 9306 else if (plan == PLAN_CR) 9307 { 9308 if (noinvcurs) 9309 screen_stop_highlight(); 9310 out_char('\r'); 9311 screen_cur_col = 0; 9312 } 9313 else if (plan == PLAN_NL) 9314 { 9315 if (noinvcurs) 9316 screen_stop_highlight(); 9317 while (screen_cur_row < row) 9318 { 9319 out_char('\n'); 9320 ++screen_cur_row; 9321 } 9322 screen_cur_col = 0; 9323 } 9324 9325 i = col - screen_cur_col; 9326 if (i > 0) 9327 { 9328 /* 9329 * Use cursor-right if it's one character only. Avoids 9330 * removing a line of pixels from the last bold char, when 9331 * using the bold trick in the GUI. 9332 */ 9333 if (T_ND[0] != NUL && T_ND[1] == NUL) 9334 { 9335 while (i-- > 0) 9336 out_char(*T_ND); 9337 } 9338 else 9339 { 9340 int off; 9341 9342 off = LineOffset[row] + screen_cur_col; 9343 while (i-- > 0) 9344 { 9345 if (ScreenAttrs[off] != screen_attr) 9346 screen_stop_highlight(); 9347 out_flush_check(); 9348 out_char(ScreenLines[off]); 9349 if (enc_dbcs == DBCS_JPNU 9350 && ScreenLines[off] == 0x8e) 9351 out_char(ScreenLines2[off]); 9352 ++off; 9353 } 9354 } 9355 } 9356 } 9357 } 9358 else 9359 cost = 999; 9360 9361 if (cost >= goto_cost) 9362 { 9363 if (noinvcurs) 9364 screen_stop_highlight(); 9365 if (row == screen_cur_row && (col > screen_cur_col) 9366 && *T_CRI != NUL) 9367 term_cursor_right(col - screen_cur_col); 9368 else 9369 term_windgoto(row, col); 9370 } 9371 screen_cur_row = row; 9372 screen_cur_col = col; 9373 } 9374 } 9375 9376 /* 9377 * Set cursor to its position in the current window. 9378 */ 9379 void 9380 setcursor(void) 9381 { 9382 setcursor_mayforce(FALSE); 9383 } 9384 9385 /* 9386 * Set cursor to its position in the current window. 9387 * When "force" is TRUE also when not redrawing. 9388 */ 9389 void 9390 setcursor_mayforce(int force) 9391 { 9392 if (force || redrawing()) 9393 { 9394 validate_cursor(); 9395 windgoto(W_WINROW(curwin) + curwin->w_wrow, 9396 curwin->w_wincol + ( 9397 #ifdef FEAT_RIGHTLEFT 9398 /* With 'rightleft' set and the cursor on a double-wide 9399 * character, position it on the leftmost column. */ 9400 curwin->w_p_rl ? ((int)curwin->w_width - curwin->w_wcol 9401 - ((has_mbyte 9402 && (*mb_ptr2cells)(ml_get_cursor()) == 2 9403 && vim_isprintc(gchar_cursor())) ? 2 : 1)) : 9404 #endif 9405 curwin->w_wcol)); 9406 } 9407 } 9408 9409 9410 /* 9411 * Insert 'line_count' lines at 'row' in window 'wp'. 9412 * If 'invalid' is TRUE the wp->w_lines[].wl_lnum is invalidated. 9413 * If 'mayclear' is TRUE the screen will be cleared if it is faster than 9414 * scrolling. 9415 * Returns FAIL if the lines are not inserted, OK for success. 9416 */ 9417 int 9418 win_ins_lines( 9419 win_T *wp, 9420 int row, 9421 int line_count, 9422 int invalid, 9423 int mayclear) 9424 { 9425 int did_delete; 9426 int nextrow; 9427 int lastrow; 9428 int retval; 9429 9430 if (invalid) 9431 wp->w_lines_valid = 0; 9432 9433 if (wp->w_height < 5) 9434 return FAIL; 9435 9436 if (line_count > wp->w_height - row) 9437 line_count = wp->w_height - row; 9438 9439 retval = win_do_lines(wp, row, line_count, mayclear, FALSE, 0); 9440 if (retval != MAYBE) 9441 return retval; 9442 9443 /* 9444 * If there is a next window or a status line, we first try to delete the 9445 * lines at the bottom to avoid messing what is after the window. 9446 * If this fails and there are following windows, don't do anything to avoid 9447 * messing up those windows, better just redraw. 9448 */ 9449 did_delete = FALSE; 9450 if (wp->w_next != NULL || wp->w_status_height) 9451 { 9452 if (screen_del_lines(0, W_WINROW(wp) + wp->w_height - line_count, 9453 line_count, (int)Rows, FALSE, 0, NULL) == OK) 9454 did_delete = TRUE; 9455 else if (wp->w_next) 9456 return FAIL; 9457 } 9458 /* 9459 * if no lines deleted, blank the lines that will end up below the window 9460 */ 9461 if (!did_delete) 9462 { 9463 wp->w_redr_status = TRUE; 9464 redraw_cmdline = TRUE; 9465 nextrow = W_WINROW(wp) + wp->w_height + wp->w_status_height; 9466 lastrow = nextrow + line_count; 9467 if (lastrow > Rows) 9468 lastrow = Rows; 9469 screen_fill(nextrow - line_count, lastrow - line_count, 9470 wp->w_wincol, (int)W_ENDCOL(wp), 9471 ' ', ' ', 0); 9472 } 9473 9474 if (screen_ins_lines(0, W_WINROW(wp) + row, line_count, (int)Rows, 0, NULL) 9475 == FAIL) 9476 { 9477 /* deletion will have messed up other windows */ 9478 if (did_delete) 9479 { 9480 wp->w_redr_status = TRUE; 9481 win_rest_invalid(W_NEXT(wp)); 9482 } 9483 return FAIL; 9484 } 9485 9486 return OK; 9487 } 9488 9489 /* 9490 * Delete "line_count" window lines at "row" in window "wp". 9491 * If "invalid" is TRUE curwin->w_lines[] is invalidated. 9492 * If "mayclear" is TRUE the screen will be cleared if it is faster than 9493 * scrolling 9494 * Return OK for success, FAIL if the lines are not deleted. 9495 */ 9496 int 9497 win_del_lines( 9498 win_T *wp, 9499 int row, 9500 int line_count, 9501 int invalid, 9502 int mayclear, 9503 int clear_attr) /* for clearing lines */ 9504 { 9505 int retval; 9506 9507 if (invalid) 9508 wp->w_lines_valid = 0; 9509 9510 if (line_count > wp->w_height - row) 9511 line_count = wp->w_height - row; 9512 9513 retval = win_do_lines(wp, row, line_count, mayclear, TRUE, clear_attr); 9514 if (retval != MAYBE) 9515 return retval; 9516 9517 if (screen_del_lines(0, W_WINROW(wp) + row, line_count, 9518 (int)Rows, FALSE, clear_attr, NULL) == FAIL) 9519 return FAIL; 9520 9521 /* 9522 * If there are windows or status lines below, try to put them at the 9523 * correct place. If we can't do that, they have to be redrawn. 9524 */ 9525 if (wp->w_next || wp->w_status_height || cmdline_row < Rows - 1) 9526 { 9527 if (screen_ins_lines(0, W_WINROW(wp) + wp->w_height - line_count, 9528 line_count, (int)Rows, clear_attr, NULL) == FAIL) 9529 { 9530 wp->w_redr_status = TRUE; 9531 win_rest_invalid(wp->w_next); 9532 } 9533 } 9534 /* 9535 * If this is the last window and there is no status line, redraw the 9536 * command line later. 9537 */ 9538 else 9539 redraw_cmdline = TRUE; 9540 return OK; 9541 } 9542 9543 /* 9544 * Common code for win_ins_lines() and win_del_lines(). 9545 * Returns OK or FAIL when the work has been done. 9546 * Returns MAYBE when not finished yet. 9547 */ 9548 static int 9549 win_do_lines( 9550 win_T *wp, 9551 int row, 9552 int line_count, 9553 int mayclear, 9554 int del, 9555 int clear_attr) 9556 { 9557 int retval; 9558 9559 if (!redrawing() || line_count <= 0) 9560 return FAIL; 9561 9562 /* When inserting lines would result in loss of command output, just redraw 9563 * the lines. */ 9564 if (no_win_do_lines_ins && !del) 9565 return FAIL; 9566 9567 /* only a few lines left: redraw is faster */ 9568 if (mayclear && Rows - line_count < 5 && wp->w_width == Columns) 9569 { 9570 if (!no_win_do_lines_ins) 9571 screenclear(); /* will set wp->w_lines_valid to 0 */ 9572 return FAIL; 9573 } 9574 9575 /* 9576 * Delete all remaining lines 9577 */ 9578 if (row + line_count >= wp->w_height) 9579 { 9580 screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + wp->w_height, 9581 wp->w_wincol, (int)W_ENDCOL(wp), 9582 ' ', ' ', 0); 9583 return OK; 9584 } 9585 9586 /* 9587 * When scrolling, the message on the command line should be cleared, 9588 * otherwise it will stay there forever. 9589 * Don't do this when avoiding to insert lines. 9590 */ 9591 if (!no_win_do_lines_ins) 9592 clear_cmdline = TRUE; 9593 9594 /* 9595 * If the terminal can set a scroll region, use that. 9596 * Always do this in a vertically split window. This will redraw from 9597 * ScreenLines[] when t_CV isn't defined. That's faster than using 9598 * win_line(). 9599 * Don't use a scroll region when we are going to redraw the text, writing 9600 * a character in the lower right corner of the scroll region may cause a 9601 * scroll-up . 9602 */ 9603 if (scroll_region || wp->w_width != Columns) 9604 { 9605 if (scroll_region && (wp->w_width == Columns || *T_CSV != NUL)) 9606 scroll_region_set(wp, row); 9607 if (del) 9608 retval = screen_del_lines(W_WINROW(wp) + row, 0, line_count, 9609 wp->w_height - row, FALSE, clear_attr, wp); 9610 else 9611 retval = screen_ins_lines(W_WINROW(wp) + row, 0, line_count, 9612 wp->w_height - row, clear_attr, wp); 9613 if (scroll_region && (wp->w_width == Columns || *T_CSV != NUL)) 9614 scroll_region_reset(); 9615 return retval; 9616 } 9617 9618 if (wp->w_next != NULL && p_tf) /* don't delete/insert on fast terminal */ 9619 return FAIL; 9620 9621 return MAYBE; 9622 } 9623 9624 /* 9625 * window 'wp' and everything after it is messed up, mark it for redraw 9626 */ 9627 static void 9628 win_rest_invalid(win_T *wp) 9629 { 9630 while (wp != NULL) 9631 { 9632 redraw_win_later(wp, NOT_VALID); 9633 wp->w_redr_status = TRUE; 9634 wp = wp->w_next; 9635 } 9636 redraw_cmdline = TRUE; 9637 } 9638 9639 /* 9640 * The rest of the routines in this file perform screen manipulations. The 9641 * given operation is performed physically on the screen. The corresponding 9642 * change is also made to the internal screen image. In this way, the editor 9643 * anticipates the effect of editing changes on the appearance of the screen. 9644 * That way, when we call screenupdate a complete redraw isn't usually 9645 * necessary. Another advantage is that we can keep adding code to anticipate 9646 * screen changes, and in the meantime, everything still works. 9647 */ 9648 9649 /* 9650 * types for inserting or deleting lines 9651 */ 9652 #define USE_T_CAL 1 9653 #define USE_T_CDL 2 9654 #define USE_T_AL 3 9655 #define USE_T_CE 4 9656 #define USE_T_DL 5 9657 #define USE_T_SR 6 9658 #define USE_NL 7 9659 #define USE_T_CD 8 9660 #define USE_REDRAW 9 9661 9662 /* 9663 * insert lines on the screen and update ScreenLines[] 9664 * 'end' is the line after the scrolled part. Normally it is Rows. 9665 * When scrolling region used 'off' is the offset from the top for the region. 9666 * 'row' and 'end' are relative to the start of the region. 9667 * 9668 * return FAIL for failure, OK for success. 9669 */ 9670 int 9671 screen_ins_lines( 9672 int off, 9673 int row, 9674 int line_count, 9675 int end, 9676 int clear_attr, 9677 win_T *wp) /* NULL or window to use width from */ 9678 { 9679 int i; 9680 int j; 9681 unsigned temp; 9682 int cursor_row; 9683 int cursor_col = 0; 9684 int type; 9685 int result_empty; 9686 int can_ce = can_clear(T_CE); 9687 9688 /* 9689 * FAIL if 9690 * - there is no valid screen 9691 * - the screen has to be redrawn completely 9692 * - the line count is less than one 9693 * - the line count is more than 'ttyscroll' 9694 * - redrawing for a callback and there is a modeless selection 9695 */ 9696 if (!screen_valid(TRUE) || line_count <= 0 || line_count > p_ttyscroll 9697 #ifdef FEAT_CLIPBOARD 9698 || (clip_star.state != SELECT_CLEARED 9699 && redrawing_for_callback > 0) 9700 #endif 9701 ) 9702 return FAIL; 9703 9704 /* 9705 * There are seven ways to insert lines: 9706 * 0. When in a vertically split window and t_CV isn't set, redraw the 9707 * characters from ScreenLines[]. 9708 * 1. Use T_CD (clear to end of display) if it exists and the result of 9709 * the insert is just empty lines 9710 * 2. Use T_CAL (insert multiple lines) if it exists and T_AL is not 9711 * present or line_count > 1. It looks better if we do all the inserts 9712 * at once. 9713 * 3. Use T_CDL (delete multiple lines) if it exists and the result of the 9714 * insert is just empty lines and T_CE is not present or line_count > 9715 * 1. 9716 * 4. Use T_AL (insert line) if it exists. 9717 * 5. Use T_CE (erase line) if it exists and the result of the insert is 9718 * just empty lines. 9719 * 6. Use T_DL (delete line) if it exists and the result of the insert is 9720 * just empty lines. 9721 * 7. Use T_SR (scroll reverse) if it exists and inserting at row 0 and 9722 * the 'da' flag is not set or we have clear line capability. 9723 * 8. redraw the characters from ScreenLines[]. 9724 * 9725 * Careful: In a hpterm scroll reverse doesn't work as expected, it moves 9726 * the scrollbar for the window. It does have insert line, use that if it 9727 * exists. 9728 */ 9729 result_empty = (row + line_count >= end); 9730 if (wp != NULL && wp->w_width != Columns && *T_CSV == NUL) 9731 type = USE_REDRAW; 9732 else if (can_clear(T_CD) && result_empty) 9733 type = USE_T_CD; 9734 else if (*T_CAL != NUL && (line_count > 1 || *T_AL == NUL)) 9735 type = USE_T_CAL; 9736 else if (*T_CDL != NUL && result_empty && (line_count > 1 || !can_ce)) 9737 type = USE_T_CDL; 9738 else if (*T_AL != NUL) 9739 type = USE_T_AL; 9740 else if (can_ce && result_empty) 9741 type = USE_T_CE; 9742 else if (*T_DL != NUL && result_empty) 9743 type = USE_T_DL; 9744 else if (*T_SR != NUL && row == 0 && (*T_DA == NUL || can_ce)) 9745 type = USE_T_SR; 9746 else 9747 return FAIL; 9748 9749 /* 9750 * For clearing the lines screen_del_lines() is used. This will also take 9751 * care of t_db if necessary. 9752 */ 9753 if (type == USE_T_CD || type == USE_T_CDL || 9754 type == USE_T_CE || type == USE_T_DL) 9755 return screen_del_lines(off, row, line_count, end, FALSE, 0, wp); 9756 9757 /* 9758 * If text is retained below the screen, first clear or delete as many 9759 * lines at the bottom of the window as are about to be inserted so that 9760 * the deleted lines won't later surface during a screen_del_lines. 9761 */ 9762 if (*T_DB) 9763 screen_del_lines(off, end - line_count, line_count, end, FALSE, 0, wp); 9764 9765 #ifdef FEAT_CLIPBOARD 9766 /* Remove a modeless selection when inserting lines halfway the screen 9767 * or not the full width of the screen. */ 9768 if (off + row > 0 || (wp != NULL && wp->w_width != Columns)) 9769 clip_clear_selection(&clip_star); 9770 else 9771 clip_scroll_selection(-line_count); 9772 #endif 9773 9774 #ifdef FEAT_GUI 9775 /* Don't update the GUI cursor here, ScreenLines[] is invalid until the 9776 * scrolling is actually carried out. */ 9777 gui_dont_update_cursor(row + off <= gui.cursor_row); 9778 #endif 9779 9780 if (wp != NULL && wp->w_wincol != 0 && *T_CSV != NUL && *T_CCS == NUL) 9781 cursor_col = wp->w_wincol; 9782 9783 if (*T_CCS != NUL) /* cursor relative to region */ 9784 cursor_row = row; 9785 else 9786 cursor_row = row + off; 9787 9788 /* 9789 * Shift LineOffset[] line_count down to reflect the inserted lines. 9790 * Clear the inserted lines in ScreenLines[]. 9791 */ 9792 row += off; 9793 end += off; 9794 for (i = 0; i < line_count; ++i) 9795 { 9796 if (wp != NULL && wp->w_width != Columns) 9797 { 9798 /* need to copy part of a line */ 9799 j = end - 1 - i; 9800 while ((j -= line_count) >= row) 9801 linecopy(j + line_count, j, wp); 9802 j += line_count; 9803 if (can_clear((char_u *)" ")) 9804 lineclear(LineOffset[j] + wp->w_wincol, wp->w_width, 9805 clear_attr); 9806 else 9807 lineinvalid(LineOffset[j] + wp->w_wincol, wp->w_width); 9808 LineWraps[j] = FALSE; 9809 } 9810 else 9811 { 9812 j = end - 1 - i; 9813 temp = LineOffset[j]; 9814 while ((j -= line_count) >= row) 9815 { 9816 LineOffset[j + line_count] = LineOffset[j]; 9817 LineWraps[j + line_count] = LineWraps[j]; 9818 } 9819 LineOffset[j + line_count] = temp; 9820 LineWraps[j + line_count] = FALSE; 9821 if (can_clear((char_u *)" ")) 9822 lineclear(temp, (int)Columns, clear_attr); 9823 else 9824 lineinvalid(temp, (int)Columns); 9825 } 9826 } 9827 9828 screen_stop_highlight(); 9829 windgoto(cursor_row, cursor_col); 9830 if (clear_attr != 0) 9831 screen_start_highlight(clear_attr); 9832 9833 /* redraw the characters */ 9834 if (type == USE_REDRAW) 9835 redraw_block(row, end, wp); 9836 else if (type == USE_T_CAL) 9837 { 9838 term_append_lines(line_count); 9839 screen_start(); /* don't know where cursor is now */ 9840 } 9841 else 9842 { 9843 for (i = 0; i < line_count; i++) 9844 { 9845 if (type == USE_T_AL) 9846 { 9847 if (i && cursor_row != 0) 9848 windgoto(cursor_row, cursor_col); 9849 out_str(T_AL); 9850 } 9851 else /* type == USE_T_SR */ 9852 out_str(T_SR); 9853 screen_start(); /* don't know where cursor is now */ 9854 } 9855 } 9856 9857 /* 9858 * With scroll-reverse and 'da' flag set we need to clear the lines that 9859 * have been scrolled down into the region. 9860 */ 9861 if (type == USE_T_SR && *T_DA) 9862 { 9863 for (i = 0; i < line_count; ++i) 9864 { 9865 windgoto(off + i, cursor_col); 9866 out_str(T_CE); 9867 screen_start(); /* don't know where cursor is now */ 9868 } 9869 } 9870 9871 #ifdef FEAT_GUI 9872 gui_can_update_cursor(); 9873 if (gui.in_use) 9874 out_flush(); /* always flush after a scroll */ 9875 #endif 9876 return OK; 9877 } 9878 9879 /* 9880 * Delete lines on the screen and update ScreenLines[]. 9881 * "end" is the line after the scrolled part. Normally it is Rows. 9882 * When scrolling region used "off" is the offset from the top for the region. 9883 * "row" and "end" are relative to the start of the region. 9884 * 9885 * Return OK for success, FAIL if the lines are not deleted. 9886 */ 9887 int 9888 screen_del_lines( 9889 int off, 9890 int row, 9891 int line_count, 9892 int end, 9893 int force, /* even when line_count > p_ttyscroll */ 9894 int clear_attr, /* used for clearing lines */ 9895 win_T *wp UNUSED) /* NULL or window to use width from */ 9896 { 9897 int j; 9898 int i; 9899 unsigned temp; 9900 int cursor_row; 9901 int cursor_col = 0; 9902 int cursor_end; 9903 int result_empty; /* result is empty until end of region */ 9904 int can_delete; /* deleting line codes can be used */ 9905 int type; 9906 9907 /* 9908 * FAIL if 9909 * - there is no valid screen 9910 * - the screen has to be redrawn completely 9911 * - the line count is less than one 9912 * - the line count is more than 'ttyscroll' 9913 * - redrawing for a callback and there is a modeless selection 9914 */ 9915 if (!screen_valid(TRUE) || line_count <= 0 9916 || (!force && line_count > p_ttyscroll) 9917 #ifdef FEAT_CLIPBOARD 9918 || (clip_star.state != SELECT_CLEARED 9919 && redrawing_for_callback > 0) 9920 #endif 9921 ) 9922 return FAIL; 9923 9924 /* 9925 * Check if the rest of the current region will become empty. 9926 */ 9927 result_empty = row + line_count >= end; 9928 9929 /* 9930 * We can delete lines only when 'db' flag not set or when 'ce' option 9931 * available. 9932 */ 9933 can_delete = (*T_DB == NUL || can_clear(T_CE)); 9934 9935 /* 9936 * There are six ways to delete lines: 9937 * 0. When in a vertically split window and t_CV isn't set, redraw the 9938 * characters from ScreenLines[]. 9939 * 1. Use T_CD if it exists and the result is empty. 9940 * 2. Use newlines if row == 0 and count == 1 or T_CDL does not exist. 9941 * 3. Use T_CDL (delete multiple lines) if it exists and line_count > 1 or 9942 * none of the other ways work. 9943 * 4. Use T_CE (erase line) if the result is empty. 9944 * 5. Use T_DL (delete line) if it exists. 9945 * 6. redraw the characters from ScreenLines[]. 9946 */ 9947 if (wp != NULL && wp->w_width != Columns && *T_CSV == NUL) 9948 type = USE_REDRAW; 9949 else if (can_clear(T_CD) && result_empty) 9950 type = USE_T_CD; 9951 #if defined(__BEOS__) && defined(BEOS_DR8) 9952 /* 9953 * USE_NL does not seem to work in Terminal of DR8 so we set T_DB="" in 9954 * its internal termcap... this works okay for tests which test *T_DB != 9955 * NUL. It has the disadvantage that the user cannot use any :set t_* 9956 * command to get T_DB (back) to empty_option, only :set term=... will do 9957 * the trick... 9958 * Anyway, this hack will hopefully go away with the next OS release. 9959 * (Olaf Seibert) 9960 */ 9961 else if (row == 0 && T_DB == empty_option 9962 && (line_count == 1 || *T_CDL == NUL)) 9963 #else 9964 else if (row == 0 && ( 9965 #ifndef AMIGA 9966 /* On the Amiga, somehow '\n' on the last line doesn't always scroll 9967 * up, so use delete-line command */ 9968 line_count == 1 || 9969 #endif 9970 *T_CDL == NUL)) 9971 #endif 9972 type = USE_NL; 9973 else if (*T_CDL != NUL && line_count > 1 && can_delete) 9974 type = USE_T_CDL; 9975 else if (can_clear(T_CE) && result_empty 9976 && (wp == NULL || wp->w_width == Columns)) 9977 type = USE_T_CE; 9978 else if (*T_DL != NUL && can_delete) 9979 type = USE_T_DL; 9980 else if (*T_CDL != NUL && can_delete) 9981 type = USE_T_CDL; 9982 else 9983 return FAIL; 9984 9985 #ifdef FEAT_CLIPBOARD 9986 /* Remove a modeless selection when deleting lines halfway the screen or 9987 * not the full width of the screen. */ 9988 if (off + row > 0 || (wp != NULL && wp->w_width != Columns)) 9989 clip_clear_selection(&clip_star); 9990 else 9991 clip_scroll_selection(line_count); 9992 #endif 9993 9994 #ifdef FEAT_GUI 9995 /* Don't update the GUI cursor here, ScreenLines[] is invalid until the 9996 * scrolling is actually carried out. */ 9997 gui_dont_update_cursor(gui.cursor_row >= row + off 9998 && gui.cursor_row < end + off); 9999 #endif 10000 10001 if (wp != NULL && wp->w_wincol != 0 && *T_CSV != NUL && *T_CCS == NUL) 10002 cursor_col = wp->w_wincol; 10003 10004 if (*T_CCS != NUL) /* cursor relative to region */ 10005 { 10006 cursor_row = row; 10007 cursor_end = end; 10008 } 10009 else 10010 { 10011 cursor_row = row + off; 10012 cursor_end = end + off; 10013 } 10014 10015 /* 10016 * Now shift LineOffset[] line_count up to reflect the deleted lines. 10017 * Clear the inserted lines in ScreenLines[]. 10018 */ 10019 row += off; 10020 end += off; 10021 for (i = 0; i < line_count; ++i) 10022 { 10023 if (wp != NULL && wp->w_width != Columns) 10024 { 10025 /* need to copy part of a line */ 10026 j = row + i; 10027 while ((j += line_count) <= end - 1) 10028 linecopy(j - line_count, j, wp); 10029 j -= line_count; 10030 if (can_clear((char_u *)" ")) 10031 lineclear(LineOffset[j] + wp->w_wincol, wp->w_width, 10032 clear_attr); 10033 else 10034 lineinvalid(LineOffset[j] + wp->w_wincol, wp->w_width); 10035 LineWraps[j] = FALSE; 10036 } 10037 else 10038 { 10039 /* whole width, moving the line pointers is faster */ 10040 j = row + i; 10041 temp = LineOffset[j]; 10042 while ((j += line_count) <= end - 1) 10043 { 10044 LineOffset[j - line_count] = LineOffset[j]; 10045 LineWraps[j - line_count] = LineWraps[j]; 10046 } 10047 LineOffset[j - line_count] = temp; 10048 LineWraps[j - line_count] = FALSE; 10049 if (can_clear((char_u *)" ")) 10050 lineclear(temp, (int)Columns, clear_attr); 10051 else 10052 lineinvalid(temp, (int)Columns); 10053 } 10054 } 10055 10056 if (screen_attr != clear_attr) 10057 screen_stop_highlight(); 10058 if (clear_attr != 0) 10059 screen_start_highlight(clear_attr); 10060 10061 /* redraw the characters */ 10062 if (type == USE_REDRAW) 10063 redraw_block(row, end, wp); 10064 else if (type == USE_T_CD) /* delete the lines */ 10065 { 10066 windgoto(cursor_row, cursor_col); 10067 out_str(T_CD); 10068 screen_start(); /* don't know where cursor is now */ 10069 } 10070 else if (type == USE_T_CDL) 10071 { 10072 windgoto(cursor_row, cursor_col); 10073 term_delete_lines(line_count); 10074 screen_start(); /* don't know where cursor is now */ 10075 } 10076 /* 10077 * Deleting lines at top of the screen or scroll region: Just scroll 10078 * the whole screen (scroll region) up by outputting newlines on the 10079 * last line. 10080 */ 10081 else if (type == USE_NL) 10082 { 10083 windgoto(cursor_end - 1, cursor_col); 10084 for (i = line_count; --i >= 0; ) 10085 out_char('\n'); /* cursor will remain on same line */ 10086 } 10087 else 10088 { 10089 for (i = line_count; --i >= 0; ) 10090 { 10091 if (type == USE_T_DL) 10092 { 10093 windgoto(cursor_row, cursor_col); 10094 out_str(T_DL); /* delete a line */ 10095 } 10096 else /* type == USE_T_CE */ 10097 { 10098 windgoto(cursor_row + i, cursor_col); 10099 out_str(T_CE); /* erase a line */ 10100 } 10101 screen_start(); /* don't know where cursor is now */ 10102 } 10103 } 10104 10105 /* 10106 * If the 'db' flag is set, we need to clear the lines that have been 10107 * scrolled up at the bottom of the region. 10108 */ 10109 if (*T_DB && (type == USE_T_DL || type == USE_T_CDL)) 10110 { 10111 for (i = line_count; i > 0; --i) 10112 { 10113 windgoto(cursor_end - i, cursor_col); 10114 out_str(T_CE); /* erase a line */ 10115 screen_start(); /* don't know where cursor is now */ 10116 } 10117 } 10118 10119 #ifdef FEAT_GUI 10120 gui_can_update_cursor(); 10121 if (gui.in_use) 10122 out_flush(); /* always flush after a scroll */ 10123 #endif 10124 10125 return OK; 10126 } 10127 10128 /* 10129 * Return TRUE when postponing displaying the mode message: when not redrawing 10130 * or inside a mapping. 10131 */ 10132 int 10133 skip_showmode() 10134 { 10135 // Call char_avail() only when we are going to show something, because it 10136 // takes a bit of time. redrawing() may also call char_avail_avail(). 10137 if (global_busy 10138 || msg_silent != 0 10139 || !redrawing() 10140 || (char_avail() && !KeyTyped)) 10141 { 10142 redraw_mode = TRUE; // show mode later 10143 return TRUE; 10144 } 10145 return FALSE; 10146 } 10147 10148 /* 10149 * Show the current mode and ruler. 10150 * 10151 * If clear_cmdline is TRUE, clear the rest of the cmdline. 10152 * If clear_cmdline is FALSE there may be a message there that needs to be 10153 * cleared only if a mode is shown. 10154 * If redraw_mode is TRUE show or clear the mode. 10155 * Return the length of the message (0 if no message). 10156 */ 10157 int 10158 showmode(void) 10159 { 10160 int need_clear; 10161 int length = 0; 10162 int do_mode; 10163 int attr; 10164 int nwr_save; 10165 #ifdef FEAT_INS_EXPAND 10166 int sub_attr; 10167 #endif 10168 10169 do_mode = ((p_smd && msg_silent == 0) 10170 && ((State & INSERT) 10171 || restart_edit != NUL 10172 || VIsual_active)); 10173 if (do_mode || reg_recording != 0) 10174 { 10175 if (skip_showmode()) 10176 return 0; // show mode later 10177 10178 nwr_save = need_wait_return; 10179 10180 /* wait a bit before overwriting an important message */ 10181 check_for_delay(FALSE); 10182 10183 /* if the cmdline is more than one line high, erase top lines */ 10184 need_clear = clear_cmdline; 10185 if (clear_cmdline && cmdline_row < Rows - 1) 10186 msg_clr_cmdline(); /* will reset clear_cmdline */ 10187 10188 /* Position on the last line in the window, column 0 */ 10189 msg_pos_mode(); 10190 cursor_off(); 10191 attr = HL_ATTR(HLF_CM); /* Highlight mode */ 10192 if (do_mode) 10193 { 10194 msg_puts_attr("--", attr); 10195 #if defined(FEAT_XIM) 10196 if ( 10197 # ifdef FEAT_GUI_GTK 10198 preedit_get_status() 10199 # else 10200 im_get_status() 10201 # endif 10202 ) 10203 # ifdef FEAT_GUI_GTK /* most of the time, it's not XIM being used */ 10204 msg_puts_attr(" IM", attr); 10205 # else 10206 msg_puts_attr(" XIM", attr); 10207 # endif 10208 #endif 10209 #if defined(FEAT_HANGULIN) && defined(FEAT_GUI) 10210 if (gui.in_use) 10211 { 10212 if (hangul_input_state_get()) 10213 { 10214 /* HANGUL */ 10215 if (enc_utf8) 10216 msg_puts_attr(" \355\225\234\352\270\200", attr); 10217 else 10218 msg_puts_attr(" \307\321\261\333", attr); 10219 } 10220 } 10221 #endif 10222 #ifdef FEAT_INS_EXPAND 10223 /* CTRL-X in Insert mode */ 10224 if (edit_submode != NULL && !shortmess(SHM_COMPLETIONMENU)) 10225 { 10226 /* These messages can get long, avoid a wrap in a narrow 10227 * window. Prefer showing edit_submode_extra. */ 10228 length = (Rows - msg_row) * Columns - 3; 10229 if (edit_submode_extra != NULL) 10230 length -= vim_strsize(edit_submode_extra); 10231 if (length > 0) 10232 { 10233 if (edit_submode_pre != NULL) 10234 length -= vim_strsize(edit_submode_pre); 10235 if (length - vim_strsize(edit_submode) > 0) 10236 { 10237 if (edit_submode_pre != NULL) 10238 msg_puts_attr((char *)edit_submode_pre, attr); 10239 msg_puts_attr((char *)edit_submode, attr); 10240 } 10241 if (edit_submode_extra != NULL) 10242 { 10243 msg_puts_attr(" ", attr); /* add a space in between */ 10244 if ((int)edit_submode_highl < (int)HLF_COUNT) 10245 sub_attr = HL_ATTR(edit_submode_highl); 10246 else 10247 sub_attr = attr; 10248 msg_puts_attr((char *)edit_submode_extra, sub_attr); 10249 } 10250 } 10251 } 10252 else 10253 #endif 10254 { 10255 if (State & VREPLACE_FLAG) 10256 msg_puts_attr(_(" VREPLACE"), attr); 10257 else if (State & REPLACE_FLAG) 10258 msg_puts_attr(_(" REPLACE"), attr); 10259 else if (State & INSERT) 10260 { 10261 #ifdef FEAT_RIGHTLEFT 10262 if (p_ri) 10263 msg_puts_attr(_(" REVERSE"), attr); 10264 #endif 10265 msg_puts_attr(_(" INSERT"), attr); 10266 } 10267 else if (restart_edit == 'I' || restart_edit == 'A') 10268 msg_puts_attr(_(" (insert)"), attr); 10269 else if (restart_edit == 'R') 10270 msg_puts_attr(_(" (replace)"), attr); 10271 else if (restart_edit == 'V') 10272 msg_puts_attr(_(" (vreplace)"), attr); 10273 #ifdef FEAT_RIGHTLEFT 10274 if (p_hkmap) 10275 msg_puts_attr(_(" Hebrew"), attr); 10276 #endif 10277 #ifdef FEAT_KEYMAP 10278 if (State & LANGMAP) 10279 { 10280 # ifdef FEAT_ARABIC 10281 if (curwin->w_p_arab) 10282 msg_puts_attr(_(" Arabic"), attr); 10283 else 10284 # endif 10285 if (get_keymap_str(curwin, (char_u *)" (%s)", 10286 NameBuff, MAXPATHL)) 10287 msg_puts_attr((char *)NameBuff, attr); 10288 } 10289 #endif 10290 if ((State & INSERT) && p_paste) 10291 msg_puts_attr(_(" (paste)"), attr); 10292 10293 if (VIsual_active) 10294 { 10295 char *p; 10296 10297 /* Don't concatenate separate words to avoid translation 10298 * problems. */ 10299 switch ((VIsual_select ? 4 : 0) 10300 + (VIsual_mode == Ctrl_V) * 2 10301 + (VIsual_mode == 'V')) 10302 { 10303 case 0: p = N_(" VISUAL"); break; 10304 case 1: p = N_(" VISUAL LINE"); break; 10305 case 2: p = N_(" VISUAL BLOCK"); break; 10306 case 4: p = N_(" SELECT"); break; 10307 case 5: p = N_(" SELECT LINE"); break; 10308 default: p = N_(" SELECT BLOCK"); break; 10309 } 10310 msg_puts_attr(_(p), attr); 10311 } 10312 msg_puts_attr(" --", attr); 10313 } 10314 10315 need_clear = TRUE; 10316 } 10317 if (reg_recording != 0 10318 #ifdef FEAT_INS_EXPAND 10319 && edit_submode == NULL /* otherwise it gets too long */ 10320 #endif 10321 ) 10322 { 10323 recording_mode(attr); 10324 need_clear = TRUE; 10325 } 10326 10327 mode_displayed = TRUE; 10328 if (need_clear || clear_cmdline || redraw_mode) 10329 msg_clr_eos(); 10330 msg_didout = FALSE; /* overwrite this message */ 10331 length = msg_col; 10332 msg_col = 0; 10333 need_wait_return = nwr_save; /* never ask for hit-return for this */ 10334 } 10335 else if (clear_cmdline && msg_silent == 0) 10336 /* Clear the whole command line. Will reset "clear_cmdline". */ 10337 msg_clr_cmdline(); 10338 else if (redraw_mode) 10339 { 10340 msg_pos_mode(); 10341 msg_clr_eos(); 10342 } 10343 10344 #ifdef FEAT_CMDL_INFO 10345 /* In Visual mode the size of the selected area must be redrawn. */ 10346 if (VIsual_active) 10347 clear_showcmd(); 10348 10349 /* If the last window has no status line, the ruler is after the mode 10350 * message and must be redrawn */ 10351 if (redrawing() && lastwin->w_status_height == 0) 10352 win_redr_ruler(lastwin, TRUE, FALSE); 10353 #endif 10354 redraw_cmdline = FALSE; 10355 redraw_mode = FALSE; 10356 clear_cmdline = FALSE; 10357 10358 return length; 10359 } 10360 10361 /* 10362 * Position for a mode message. 10363 */ 10364 static void 10365 msg_pos_mode(void) 10366 { 10367 msg_col = 0; 10368 msg_row = Rows - 1; 10369 } 10370 10371 /* 10372 * Delete mode message. Used when ESC is typed which is expected to end 10373 * Insert mode (but Insert mode didn't end yet!). 10374 * Caller should check "mode_displayed". 10375 */ 10376 void 10377 unshowmode(int force) 10378 { 10379 /* 10380 * Don't delete it right now, when not redrawing or inside a mapping. 10381 */ 10382 if (!redrawing() || (!force && char_avail() && !KeyTyped)) 10383 redraw_cmdline = TRUE; /* delete mode later */ 10384 else 10385 clearmode(); 10386 } 10387 10388 /* 10389 * Clear the mode message. 10390 */ 10391 void 10392 clearmode(void) 10393 { 10394 int save_msg_row = msg_row; 10395 int save_msg_col = msg_col; 10396 10397 msg_pos_mode(); 10398 if (reg_recording != 0) 10399 recording_mode(HL_ATTR(HLF_CM)); 10400 msg_clr_eos(); 10401 10402 msg_col = save_msg_col; 10403 msg_row = save_msg_row; 10404 } 10405 10406 static void 10407 recording_mode(int attr) 10408 { 10409 msg_puts_attr(_("recording"), attr); 10410 if (!shortmess(SHM_RECORDING)) 10411 { 10412 char s[4]; 10413 10414 sprintf(s, " @%c", reg_recording); 10415 msg_puts_attr(s, attr); 10416 } 10417 } 10418 10419 /* 10420 * Draw the tab pages line at the top of the Vim window. 10421 */ 10422 void 10423 draw_tabline(void) 10424 { 10425 int tabcount = 0; 10426 tabpage_T *tp; 10427 int tabwidth; 10428 int col = 0; 10429 int scol = 0; 10430 int attr; 10431 win_T *wp; 10432 win_T *cwp; 10433 int wincount; 10434 int modified; 10435 int c; 10436 int len; 10437 int attr_sel = HL_ATTR(HLF_TPS); 10438 int attr_nosel = HL_ATTR(HLF_TP); 10439 int attr_fill = HL_ATTR(HLF_TPF); 10440 char_u *p; 10441 int room; 10442 int use_sep_chars = (t_colors < 8 10443 #ifdef FEAT_GUI 10444 && !gui.in_use 10445 #endif 10446 #ifdef FEAT_TERMGUICOLORS 10447 && !p_tgc 10448 #endif 10449 ); 10450 10451 if (ScreenLines == NULL) 10452 return; 10453 redraw_tabline = FALSE; 10454 10455 #ifdef FEAT_GUI_TABLINE 10456 /* Take care of a GUI tabline. */ 10457 if (gui_use_tabline()) 10458 { 10459 gui_update_tabline(); 10460 return; 10461 } 10462 #endif 10463 10464 if (tabline_height() < 1) 10465 return; 10466 10467 #if defined(FEAT_STL_OPT) 10468 clear_TabPageIdxs(); 10469 10470 /* Use the 'tabline' option if it's set. */ 10471 if (*p_tal != NUL) 10472 { 10473 int saved_did_emsg = did_emsg; 10474 10475 /* Check for an error. If there is one we would loop in redrawing the 10476 * screen. Avoid that by making 'tabline' empty. */ 10477 did_emsg = FALSE; 10478 win_redr_custom(NULL, FALSE); 10479 if (did_emsg) 10480 set_string_option_direct((char_u *)"tabline", -1, 10481 (char_u *)"", OPT_FREE, SID_ERROR); 10482 did_emsg |= saved_did_emsg; 10483 } 10484 else 10485 #endif 10486 { 10487 FOR_ALL_TABPAGES(tp) 10488 ++tabcount; 10489 10490 tabwidth = (Columns - 1 + tabcount / 2) / tabcount; 10491 if (tabwidth < 6) 10492 tabwidth = 6; 10493 10494 attr = attr_nosel; 10495 tabcount = 0; 10496 for (tp = first_tabpage; tp != NULL && col < Columns - 4; 10497 tp = tp->tp_next) 10498 { 10499 scol = col; 10500 10501 if (tp->tp_topframe == topframe) 10502 attr = attr_sel; 10503 if (use_sep_chars && col > 0) 10504 screen_putchar('|', 0, col++, attr); 10505 10506 if (tp->tp_topframe != topframe) 10507 attr = attr_nosel; 10508 10509 screen_putchar(' ', 0, col++, attr); 10510 10511 if (tp == curtab) 10512 { 10513 cwp = curwin; 10514 wp = firstwin; 10515 } 10516 else 10517 { 10518 cwp = tp->tp_curwin; 10519 wp = tp->tp_firstwin; 10520 } 10521 10522 modified = FALSE; 10523 for (wincount = 0; wp != NULL; wp = wp->w_next, ++wincount) 10524 if (bufIsChanged(wp->w_buffer)) 10525 modified = TRUE; 10526 if (modified || wincount > 1) 10527 { 10528 if (wincount > 1) 10529 { 10530 vim_snprintf((char *)NameBuff, MAXPATHL, "%d", wincount); 10531 len = (int)STRLEN(NameBuff); 10532 if (col + len >= Columns - 3) 10533 break; 10534 screen_puts_len(NameBuff, len, 0, col, 10535 #if defined(FEAT_SYN_HL) 10536 hl_combine_attr(attr, HL_ATTR(HLF_T)) 10537 #else 10538 attr 10539 #endif 10540 ); 10541 col += len; 10542 } 10543 if (modified) 10544 screen_puts_len((char_u *)"+", 1, 0, col++, attr); 10545 screen_putchar(' ', 0, col++, attr); 10546 } 10547 10548 room = scol - col + tabwidth - 1; 10549 if (room > 0) 10550 { 10551 /* Get buffer name in NameBuff[] */ 10552 get_trans_bufname(cwp->w_buffer); 10553 shorten_dir(NameBuff); 10554 len = vim_strsize(NameBuff); 10555 p = NameBuff; 10556 if (has_mbyte) 10557 while (len > room) 10558 { 10559 len -= ptr2cells(p); 10560 MB_PTR_ADV(p); 10561 } 10562 else if (len > room) 10563 { 10564 p += len - room; 10565 len = room; 10566 } 10567 if (len > Columns - col - 1) 10568 len = Columns - col - 1; 10569 10570 screen_puts_len(p, (int)STRLEN(p), 0, col, attr); 10571 col += len; 10572 } 10573 screen_putchar(' ', 0, col++, attr); 10574 10575 /* Store the tab page number in TabPageIdxs[], so that 10576 * jump_to_mouse() knows where each one is. */ 10577 ++tabcount; 10578 while (scol < col) 10579 TabPageIdxs[scol++] = tabcount; 10580 } 10581 10582 if (use_sep_chars) 10583 c = '_'; 10584 else 10585 c = ' '; 10586 screen_fill(0, 1, col, (int)Columns, c, c, attr_fill); 10587 10588 /* Put an "X" for closing the current tab if there are several. */ 10589 if (first_tabpage->tp_next != NULL) 10590 { 10591 screen_putchar('X', 0, (int)Columns - 1, attr_nosel); 10592 TabPageIdxs[Columns - 1] = -999; 10593 } 10594 } 10595 10596 /* Reset the flag here again, in case evaluating 'tabline' causes it to be 10597 * set. */ 10598 redraw_tabline = FALSE; 10599 } 10600 10601 /* 10602 * Get buffer name for "buf" into NameBuff[]. 10603 * Takes care of special buffer names and translates special characters. 10604 */ 10605 void 10606 get_trans_bufname(buf_T *buf) 10607 { 10608 if (buf_spname(buf) != NULL) 10609 vim_strncpy(NameBuff, buf_spname(buf), MAXPATHL - 1); 10610 else 10611 home_replace(buf, buf->b_fname, NameBuff, MAXPATHL, TRUE); 10612 trans_characters(NameBuff, MAXPATHL); 10613 } 10614 10615 /* 10616 * Get the character to use in a status line. Get its attributes in "*attr". 10617 */ 10618 static int 10619 fillchar_status(int *attr, win_T *wp) 10620 { 10621 int fill; 10622 10623 #ifdef FEAT_TERMINAL 10624 if (bt_terminal(wp->w_buffer)) 10625 { 10626 if (wp == curwin) 10627 { 10628 *attr = HL_ATTR(HLF_ST); 10629 fill = fill_stl; 10630 } 10631 else 10632 { 10633 *attr = HL_ATTR(HLF_STNC); 10634 fill = fill_stlnc; 10635 } 10636 } 10637 else 10638 #endif 10639 if (wp == curwin) 10640 { 10641 *attr = HL_ATTR(HLF_S); 10642 fill = fill_stl; 10643 } 10644 else 10645 { 10646 *attr = HL_ATTR(HLF_SNC); 10647 fill = fill_stlnc; 10648 } 10649 /* Use fill when there is highlighting, and highlighting of current 10650 * window differs, or the fillchars differ, or this is not the 10651 * current window */ 10652 if (*attr != 0 && ((HL_ATTR(HLF_S) != HL_ATTR(HLF_SNC) 10653 || wp != curwin || ONE_WINDOW) 10654 || (fill_stl != fill_stlnc))) 10655 return fill; 10656 if (wp == curwin) 10657 return '^'; 10658 return '='; 10659 } 10660 10661 /* 10662 * Get the character to use in a separator between vertically split windows. 10663 * Get its attributes in "*attr". 10664 */ 10665 static int 10666 fillchar_vsep(int *attr) 10667 { 10668 *attr = HL_ATTR(HLF_C); 10669 if (*attr == 0 && fill_vert == ' ') 10670 return '|'; 10671 else 10672 return fill_vert; 10673 } 10674 10675 /* 10676 * Return TRUE if redrawing should currently be done. 10677 */ 10678 int 10679 redrawing(void) 10680 { 10681 #ifdef FEAT_EVAL 10682 if (disable_redraw_for_testing) 10683 return 0; 10684 else 10685 #endif 10686 return ((!RedrawingDisabled 10687 #ifdef FEAT_EVAL 10688 || ignore_redraw_flag_for_testing 10689 #endif 10690 ) && !(p_lz && char_avail() && !KeyTyped && !do_redraw)); 10691 } 10692 10693 /* 10694 * Return TRUE if printing messages should currently be done. 10695 */ 10696 int 10697 messaging(void) 10698 { 10699 return (!(p_lz && char_avail() && !KeyTyped)); 10700 } 10701 10702 #ifdef FEAT_MENU 10703 /* 10704 * Draw the window toolbar. 10705 */ 10706 static void 10707 redraw_win_toolbar(win_T *wp) 10708 { 10709 vimmenu_T *menu; 10710 int item_idx = 0; 10711 int item_count = 0; 10712 int col = 0; 10713 int next_col; 10714 int off = (int)(current_ScreenLine - ScreenLines); 10715 int fill_attr = syn_name2attr((char_u *)"ToolbarLine"); 10716 int button_attr = syn_name2attr((char_u *)"ToolbarButton"); 10717 10718 vim_free(wp->w_winbar_items); 10719 for (menu = wp->w_winbar->children; menu != NULL; menu = menu->next) 10720 ++item_count; 10721 wp->w_winbar_items = (winbar_item_T *)alloc_clear( 10722 (unsigned)sizeof(winbar_item_T) * (item_count + 1)); 10723 10724 /* TODO: use fewer spaces if there is not enough room */ 10725 for (menu = wp->w_winbar->children; 10726 menu != NULL && col < wp->w_width; menu = menu->next) 10727 { 10728 space_to_screenline(off + col, fill_attr); 10729 if (++col >= wp->w_width) 10730 break; 10731 if (col > 1) 10732 { 10733 space_to_screenline(off + col, fill_attr); 10734 if (++col >= wp->w_width) 10735 break; 10736 } 10737 10738 wp->w_winbar_items[item_idx].wb_startcol = col; 10739 space_to_screenline(off + col, button_attr); 10740 if (++col >= wp->w_width) 10741 break; 10742 10743 next_col = text_to_screenline(wp, menu->name, col); 10744 while (col < next_col) 10745 { 10746 ScreenAttrs[off + col] = button_attr; 10747 ++col; 10748 } 10749 wp->w_winbar_items[item_idx].wb_endcol = col; 10750 wp->w_winbar_items[item_idx].wb_menu = menu; 10751 ++item_idx; 10752 10753 if (col >= wp->w_width) 10754 break; 10755 space_to_screenline(off + col, button_attr); 10756 ++col; 10757 } 10758 while (col < wp->w_width) 10759 { 10760 space_to_screenline(off + col, fill_attr); 10761 ++col; 10762 } 10763 wp->w_winbar_items[item_idx].wb_menu = NULL; /* end marker */ 10764 10765 screen_line(wp->w_winrow, wp->w_wincol, (int)wp->w_width, 10766 (int)wp->w_width, FALSE); 10767 } 10768 #endif 10769 10770 /* 10771 * Show current status info in ruler and various other places 10772 * If always is FALSE, only show ruler if position has changed. 10773 */ 10774 void 10775 showruler(int always) 10776 { 10777 if (!always && !redrawing()) 10778 return; 10779 #ifdef FEAT_INS_EXPAND 10780 if (pum_visible()) 10781 { 10782 /* Don't redraw right now, do it later. */ 10783 curwin->w_redr_status = TRUE; 10784 return; 10785 } 10786 #endif 10787 #if defined(FEAT_STL_OPT) 10788 if ((*p_stl != NUL || *curwin->w_p_stl != NUL) && curwin->w_status_height) 10789 redraw_custom_statusline(curwin); 10790 else 10791 #endif 10792 #ifdef FEAT_CMDL_INFO 10793 win_redr_ruler(curwin, always, FALSE); 10794 #endif 10795 10796 #ifdef FEAT_TITLE 10797 if (need_maketitle 10798 # ifdef FEAT_STL_OPT 10799 || (p_icon && (stl_syntax & STL_IN_ICON)) 10800 || (p_title && (stl_syntax & STL_IN_TITLE)) 10801 # endif 10802 ) 10803 maketitle(); 10804 #endif 10805 /* Redraw the tab pages line if needed. */ 10806 if (redraw_tabline) 10807 draw_tabline(); 10808 } 10809 10810 #ifdef FEAT_CMDL_INFO 10811 static void 10812 win_redr_ruler(win_T *wp, int always, int ignore_pum) 10813 { 10814 #define RULER_BUF_LEN 70 10815 char_u buffer[RULER_BUF_LEN]; 10816 int row; 10817 int fillchar; 10818 int attr; 10819 int empty_line = FALSE; 10820 colnr_T virtcol; 10821 int i; 10822 size_t len; 10823 int o; 10824 int this_ru_col; 10825 int off = 0; 10826 int width; 10827 10828 /* If 'ruler' off or redrawing disabled, don't do anything */ 10829 if (!p_ru) 10830 return; 10831 10832 /* 10833 * Check if cursor.lnum is valid, since win_redr_ruler() may be called 10834 * after deleting lines, before cursor.lnum is corrected. 10835 */ 10836 if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count) 10837 return; 10838 10839 #ifdef FEAT_INS_EXPAND 10840 /* Don't draw the ruler while doing insert-completion, it might overwrite 10841 * the (long) mode message. */ 10842 if (wp == lastwin && lastwin->w_status_height == 0) 10843 if (edit_submode != NULL) 10844 return; 10845 // Don't draw the ruler when the popup menu is visible, it may overlap. 10846 // Except when the popup menu will be redrawn anyway. 10847 if (!ignore_pum && pum_visible()) 10848 return; 10849 #endif 10850 10851 #ifdef FEAT_STL_OPT 10852 if (*p_ruf) 10853 { 10854 int save_called_emsg = called_emsg; 10855 10856 called_emsg = FALSE; 10857 win_redr_custom(wp, TRUE); 10858 if (called_emsg) 10859 set_string_option_direct((char_u *)"rulerformat", -1, 10860 (char_u *)"", OPT_FREE, SID_ERROR); 10861 called_emsg |= save_called_emsg; 10862 return; 10863 } 10864 #endif 10865 10866 /* 10867 * Check if not in Insert mode and the line is empty (will show "0-1"). 10868 */ 10869 if (!(State & INSERT) 10870 && *ml_get_buf(wp->w_buffer, wp->w_cursor.lnum, FALSE) == NUL) 10871 empty_line = TRUE; 10872 10873 /* 10874 * Only draw the ruler when something changed. 10875 */ 10876 validate_virtcol_win(wp); 10877 if ( redraw_cmdline 10878 || always 10879 || wp->w_cursor.lnum != wp->w_ru_cursor.lnum 10880 || wp->w_cursor.col != wp->w_ru_cursor.col 10881 || wp->w_virtcol != wp->w_ru_virtcol 10882 || wp->w_cursor.coladd != wp->w_ru_cursor.coladd 10883 || wp->w_topline != wp->w_ru_topline 10884 || wp->w_buffer->b_ml.ml_line_count != wp->w_ru_line_count 10885 #ifdef FEAT_DIFF 10886 || wp->w_topfill != wp->w_ru_topfill 10887 #endif 10888 || empty_line != wp->w_ru_empty) 10889 { 10890 cursor_off(); 10891 if (wp->w_status_height) 10892 { 10893 row = W_WINROW(wp) + wp->w_height; 10894 fillchar = fillchar_status(&attr, wp); 10895 off = wp->w_wincol; 10896 width = wp->w_width; 10897 } 10898 else 10899 { 10900 row = Rows - 1; 10901 fillchar = ' '; 10902 attr = 0; 10903 width = Columns; 10904 off = 0; 10905 } 10906 10907 /* In list mode virtcol needs to be recomputed */ 10908 virtcol = wp->w_virtcol; 10909 if (wp->w_p_list && lcs_tab1 == NUL) 10910 { 10911 wp->w_p_list = FALSE; 10912 getvvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL); 10913 wp->w_p_list = TRUE; 10914 } 10915 10916 /* 10917 * Some sprintfs return the length, some return a pointer. 10918 * To avoid portability problems we use strlen() here. 10919 */ 10920 vim_snprintf((char *)buffer, RULER_BUF_LEN, "%ld,", 10921 (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) 10922 ? 0L 10923 : (long)(wp->w_cursor.lnum)); 10924 len = STRLEN(buffer); 10925 col_print(buffer + len, RULER_BUF_LEN - len, 10926 empty_line ? 0 : (int)wp->w_cursor.col + 1, 10927 (int)virtcol + 1); 10928 10929 /* 10930 * Add a "50%" if there is room for it. 10931 * On the last line, don't print in the last column (scrolls the 10932 * screen up on some terminals). 10933 */ 10934 i = (int)STRLEN(buffer); 10935 get_rel_pos(wp, buffer + i + 1, RULER_BUF_LEN - i - 1); 10936 o = i + vim_strsize(buffer + i + 1); 10937 if (wp->w_status_height == 0) /* can't use last char of screen */ 10938 ++o; 10939 this_ru_col = ru_col - (Columns - width); 10940 if (this_ru_col < 0) 10941 this_ru_col = 0; 10942 /* Never use more than half the window/screen width, leave the other 10943 * half for the filename. */ 10944 if (this_ru_col < (width + 1) / 2) 10945 this_ru_col = (width + 1) / 2; 10946 if (this_ru_col + o < width) 10947 { 10948 /* need at least 3 chars left for get_rel_pos() + NUL */ 10949 while (this_ru_col + o < width && RULER_BUF_LEN > i + 4) 10950 { 10951 if (has_mbyte) 10952 i += (*mb_char2bytes)(fillchar, buffer + i); 10953 else 10954 buffer[i++] = fillchar; 10955 ++o; 10956 } 10957 get_rel_pos(wp, buffer + i, RULER_BUF_LEN - i); 10958 } 10959 /* Truncate at window boundary. */ 10960 if (has_mbyte) 10961 { 10962 o = 0; 10963 for (i = 0; buffer[i] != NUL; i += (*mb_ptr2len)(buffer + i)) 10964 { 10965 o += (*mb_ptr2cells)(buffer + i); 10966 if (this_ru_col + o > width) 10967 { 10968 buffer[i] = NUL; 10969 break; 10970 } 10971 } 10972 } 10973 else if (this_ru_col + (int)STRLEN(buffer) > width) 10974 buffer[width - this_ru_col] = NUL; 10975 10976 screen_puts(buffer, row, this_ru_col + off, attr); 10977 i = redraw_cmdline; 10978 screen_fill(row, row + 1, 10979 this_ru_col + off + (int)STRLEN(buffer), 10980 (int)(off + width), 10981 fillchar, fillchar, attr); 10982 /* don't redraw the cmdline because of showing the ruler */ 10983 redraw_cmdline = i; 10984 wp->w_ru_cursor = wp->w_cursor; 10985 wp->w_ru_virtcol = wp->w_virtcol; 10986 wp->w_ru_empty = empty_line; 10987 wp->w_ru_topline = wp->w_topline; 10988 wp->w_ru_line_count = wp->w_buffer->b_ml.ml_line_count; 10989 #ifdef FEAT_DIFF 10990 wp->w_ru_topfill = wp->w_topfill; 10991 #endif 10992 } 10993 } 10994 #endif 10995 10996 #if defined(FEAT_LINEBREAK) || defined(PROTO) 10997 /* 10998 * Return the width of the 'number' and 'relativenumber' column. 10999 * Caller may need to check if 'number' or 'relativenumber' is set. 11000 * Otherwise it depends on 'numberwidth' and the line count. 11001 */ 11002 int 11003 number_width(win_T *wp) 11004 { 11005 int n; 11006 linenr_T lnum; 11007 11008 if (wp->w_p_rnu && !wp->w_p_nu) 11009 /* cursor line shows "0" */ 11010 lnum = wp->w_height; 11011 else 11012 /* cursor line shows absolute line number */ 11013 lnum = wp->w_buffer->b_ml.ml_line_count; 11014 11015 if (lnum == wp->w_nrwidth_line_count && wp->w_nuw_cached == wp->w_p_nuw) 11016 return wp->w_nrwidth_width; 11017 wp->w_nrwidth_line_count = lnum; 11018 11019 n = 0; 11020 do 11021 { 11022 lnum /= 10; 11023 ++n; 11024 } while (lnum > 0); 11025 11026 /* 'numberwidth' gives the minimal width plus one */ 11027 if (n < wp->w_p_nuw - 1) 11028 n = wp->w_p_nuw - 1; 11029 11030 wp->w_nrwidth_width = n; 11031 wp->w_nuw_cached = wp->w_p_nuw; 11032 return n; 11033 } 11034 #endif 11035 11036 #if defined(FEAT_EVAL) || defined(PROTO) 11037 /* 11038 * Return the current cursor column. This is the actual position on the 11039 * screen. First column is 0. 11040 */ 11041 int 11042 screen_screencol(void) 11043 { 11044 return screen_cur_col; 11045 } 11046 11047 /* 11048 * Return the current cursor row. This is the actual position on the screen. 11049 * First row is 0. 11050 */ 11051 int 11052 screen_screenrow(void) 11053 { 11054 return screen_cur_row; 11055 } 11056 #endif 11057