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