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: Lower level 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 40 #include "vim.h" 41 42 /* 43 * The attributes that are actually active for writing to the screen. 44 */ 45 static int screen_attr = 0; 46 47 static void screen_char_2(unsigned off, int row, int col); 48 static void screenclear2(void); 49 static void lineclear(unsigned off, int width, int attr); 50 static void lineinvalid(unsigned off, int width); 51 static int win_do_lines(win_T *wp, int row, int line_count, int mayclear, int del, int clear_attr); 52 static void win_rest_invalid(win_T *wp); 53 static void msg_pos_mode(void); 54 static void recording_mode(int attr); 55 56 // Ugly global: overrule attribute used by screen_char() 57 static int screen_char_attr = 0; 58 59 #if defined(FEAT_CONCEAL) || defined(PROTO) 60 /* 61 * Return TRUE if the cursor line in window "wp" may be concealed, according 62 * to the 'concealcursor' option. 63 */ 64 int 65 conceal_cursor_line(win_T *wp) 66 { 67 int c; 68 69 if (*wp->w_p_cocu == NUL) 70 return FALSE; 71 if (get_real_state() & VISUAL) 72 c = 'v'; 73 else if (State & INSERT) 74 c = 'i'; 75 else if (State & NORMAL) 76 c = 'n'; 77 else if (State & CMDLINE) 78 c = 'c'; 79 else 80 return FALSE; 81 return vim_strchr(wp->w_p_cocu, c) != NULL; 82 } 83 84 /* 85 * Check if the cursor line needs to be redrawn because of 'concealcursor'. 86 * To be called after changing the state, "was_concealed" is the value of 87 * "conceal_cursor_line()" before the change. 88 * " 89 */ 90 void 91 conceal_check_cursor_line(int was_concealed) 92 { 93 if (curwin->w_p_cole > 0 && conceal_cursor_line(curwin) != was_concealed) 94 { 95 int wcol = curwin->w_wcol; 96 97 need_cursor_line_redraw = TRUE; 98 // Need to recompute cursor column, e.g., when starting Visual mode 99 // without concealing. 100 curs_columns(TRUE); 101 102 // When concealing now w_wcol will be computed wrong, keep the previous 103 // value, it will be updated in win_line(). 104 if (!was_concealed) 105 curwin->w_wcol = wcol; 106 } 107 } 108 #endif 109 110 /* 111 * Get 'wincolor' attribute for window "wp". If not set and "wp" is a popup 112 * window then get the "Pmenu" highlight attribute. 113 */ 114 int 115 get_wcr_attr(win_T *wp) 116 { 117 int wcr_attr = 0; 118 119 if (*wp->w_p_wcr != NUL) 120 wcr_attr = syn_name2attr(wp->w_p_wcr); 121 #ifdef FEAT_PROP_POPUP 122 else if (WIN_IS_POPUP(wp)) 123 { 124 if (wp->w_popup_flags & POPF_INFO) 125 wcr_attr = HL_ATTR(HLF_PSI); // PmenuSel 126 else 127 wcr_attr = HL_ATTR(HLF_PNI); // Pmenu 128 } 129 #endif 130 return wcr_attr; 131 } 132 133 /* 134 * Call screen_fill() with the columns adjusted for 'rightleft' if needed. 135 * Return the new offset. 136 */ 137 static int 138 screen_fill_end( 139 win_T *wp, 140 int c1, 141 int c2, 142 int off, 143 int width, 144 int row, 145 int endrow, 146 int attr) 147 { 148 int nn = off + width; 149 150 if (nn > wp->w_width) 151 nn = wp->w_width; 152 #ifdef FEAT_RIGHTLEFT 153 if (wp->w_p_rl) 154 { 155 screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow, 156 W_ENDCOL(wp) - nn, (int)W_ENDCOL(wp) - off, 157 c1, c2, attr); 158 } 159 else 160 #endif 161 screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow, 162 wp->w_wincol + off, (int)wp->w_wincol + nn, 163 c1, c2, attr); 164 return nn; 165 } 166 167 /* 168 * Clear lines near the end the window and mark the unused lines with "c1". 169 * use "c2" as the filler character. 170 * When "draw_margin" is TRUE then draw the sign, fold and number columns. 171 */ 172 void 173 win_draw_end( 174 win_T *wp, 175 int c1, 176 int c2, 177 int draw_margin, 178 int row, 179 int endrow, 180 hlf_T hl) 181 { 182 int n = 0; 183 int attr = HL_ATTR(hl); 184 int wcr_attr = get_wcr_attr(wp); 185 186 attr = hl_combine_attr(wcr_attr, attr); 187 188 if (draw_margin) 189 { 190 #ifdef FEAT_FOLDING 191 int fdc = compute_foldcolumn(wp, 0); 192 193 if (fdc > 0) 194 // draw the fold column 195 n = screen_fill_end(wp, ' ', ' ', n, fdc, 196 row, endrow, hl_combine_attr(wcr_attr, HL_ATTR(HLF_FC))); 197 #endif 198 #ifdef FEAT_SIGNS 199 if (signcolumn_on(wp)) 200 // draw the sign column 201 n = screen_fill_end(wp, ' ', ' ', n, 2, 202 row, endrow, hl_combine_attr(wcr_attr, HL_ATTR(HLF_SC))); 203 #endif 204 if ((wp->w_p_nu || wp->w_p_rnu) 205 && vim_strchr(p_cpo, CPO_NUMCOL) == NULL) 206 // draw the number column 207 n = screen_fill_end(wp, ' ', ' ', n, number_width(wp) + 1, 208 row, endrow, hl_combine_attr(wcr_attr, HL_ATTR(HLF_N))); 209 } 210 211 #ifdef FEAT_RIGHTLEFT 212 if (wp->w_p_rl) 213 { 214 screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow, 215 wp->w_wincol, W_ENDCOL(wp) - 1 - n, 216 c2, c2, attr); 217 screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow, 218 W_ENDCOL(wp) - 1 - n, W_ENDCOL(wp) - n, 219 c1, c2, attr); 220 } 221 else 222 #endif 223 { 224 screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow, 225 wp->w_wincol + n, (int)W_ENDCOL(wp), 226 c1, c2, attr); 227 } 228 229 set_empty_rows(wp, row); 230 } 231 232 #if defined(FEAT_FOLDING) || defined(PROTO) 233 /* 234 * Compute the width of the foldcolumn. Based on 'foldcolumn' and how much 235 * space is available for window "wp", minus "col". 236 */ 237 int 238 compute_foldcolumn(win_T *wp, int col) 239 { 240 int fdc = wp->w_p_fdc; 241 int wmw = wp == curwin && p_wmw == 0 ? 1 : p_wmw; 242 int wwidth = wp->w_width; 243 244 if (fdc > wwidth - (col + wmw)) 245 fdc = wwidth - (col + wmw); 246 return fdc; 247 } 248 249 /* 250 * Fill the foldcolumn at "p" for window "wp". 251 * Only to be called when 'foldcolumn' > 0. 252 * Returns the number of bytes stored in 'p'. When non-multibyte characters are 253 * used for the fold column markers, this is equal to 'fdc' setting. Otherwise, 254 * this will be greater than 'fdc'. 255 */ 256 size_t 257 fill_foldcolumn( 258 char_u *p, 259 win_T *wp, 260 int closed, // TRUE of FALSE 261 linenr_T lnum) // current line number 262 { 263 int i = 0; 264 int level; 265 int first_level; 266 int empty; 267 int fdc = compute_foldcolumn(wp, 0); 268 size_t byte_counter = 0; 269 int symbol = 0; 270 int len = 0; 271 272 // Init to all spaces. 273 vim_memset(p, ' ', MAX_MCO * fdc + 1); 274 275 level = win_foldinfo.fi_level; 276 empty = (fdc == 1) ? 0 : 1; 277 278 // If the column is too narrow, we start at the lowest level that 279 // fits and use numbers to indicate the depth. 280 first_level = level - fdc - closed + 1 + empty; 281 if (first_level < 1) 282 first_level = 1; 283 284 for (i = 0; i < MIN(fdc, level); i++) 285 { 286 if (win_foldinfo.fi_lnum == lnum 287 && first_level + i >= win_foldinfo.fi_low_level) 288 symbol = fill_foldopen; 289 else if (first_level == 1) 290 symbol = fill_foldsep; 291 else if (first_level + i <= 9) 292 symbol = '0' + first_level + i; 293 else 294 symbol = '>'; 295 296 len = utf_char2bytes(symbol, &p[byte_counter]); 297 byte_counter += len; 298 if (first_level + i >= level) 299 { 300 i++; 301 break; 302 } 303 } 304 305 if (closed) 306 { 307 if (symbol != 0) 308 { 309 // rollback length and the character 310 byte_counter -= len; 311 if (len > 1) 312 // for a multibyte character, erase all the bytes 313 vim_memset(p + byte_counter, ' ', len); 314 } 315 symbol = fill_foldclosed; 316 len = utf_char2bytes(symbol, &p[byte_counter]); 317 byte_counter += len; 318 } 319 320 return MAX(byte_counter + (fdc - i), (size_t)fdc); 321 } 322 #endif // FEAT_FOLDING 323 324 /* 325 * Return if the composing characters at "off_from" and "off_to" differ. 326 * Only to be used when ScreenLinesUC[off_from] != 0. 327 */ 328 static int 329 comp_char_differs(int off_from, int off_to) 330 { 331 int i; 332 333 for (i = 0; i < Screen_mco; ++i) 334 { 335 if (ScreenLinesC[i][off_from] != ScreenLinesC[i][off_to]) 336 return TRUE; 337 if (ScreenLinesC[i][off_from] == 0) 338 break; 339 } 340 return FALSE; 341 } 342 343 /* 344 * Check whether the given character needs redrawing: 345 * - the (first byte of the) character is different 346 * - the attributes are different 347 * - the character is multi-byte and the next byte is different 348 * - the character is two cells wide and the second cell differs. 349 */ 350 static int 351 char_needs_redraw(int off_from, int off_to, int cols) 352 { 353 if (cols > 0 354 && ((ScreenLines[off_from] != ScreenLines[off_to] 355 || ScreenAttrs[off_from] != ScreenAttrs[off_to]) 356 || (enc_dbcs != 0 357 && MB_BYTE2LEN(ScreenLines[off_from]) > 1 358 && (enc_dbcs == DBCS_JPNU && ScreenLines[off_from] == 0x8e 359 ? ScreenLines2[off_from] != ScreenLines2[off_to] 360 : (cols > 1 && ScreenLines[off_from + 1] 361 != ScreenLines[off_to + 1]))) 362 || (enc_utf8 363 && (ScreenLinesUC[off_from] != ScreenLinesUC[off_to] 364 || (ScreenLinesUC[off_from] != 0 365 && comp_char_differs(off_from, off_to)) 366 || ((*mb_off2cells)(off_from, off_from + cols) > 1 367 && ScreenLines[off_from + 1] 368 != ScreenLines[off_to + 1]))))) 369 return TRUE; 370 return FALSE; 371 } 372 373 #if defined(FEAT_TERMINAL) || defined(PROTO) 374 /* 375 * Return the index in ScreenLines[] for the current screen line. 376 */ 377 int 378 screen_get_current_line_off() 379 { 380 return (int)(current_ScreenLine - ScreenLines); 381 } 382 #endif 383 384 #ifdef FEAT_PROP_POPUP 385 /* 386 * Return TRUE if this position has a higher level popup or this cell is 387 * transparent in the current popup. 388 */ 389 static int 390 blocked_by_popup(int row, int col) 391 { 392 int off; 393 394 if (!popup_visible) 395 return FALSE; 396 off = row * screen_Columns + col; 397 return popup_mask[off] > screen_zindex || popup_transparent[off]; 398 } 399 #endif 400 401 /* 402 * Reset the highlighting. Used before clearing the screen. 403 */ 404 void 405 reset_screen_attr(void) 406 { 407 #ifdef FEAT_GUI 408 if (gui.in_use) 409 // Use a code that will reset gui.highlight_mask in 410 // gui_stop_highlight(). 411 screen_attr = HL_ALL + 1; 412 else 413 #endif 414 // Use attributes that is very unlikely to appear in text. 415 screen_attr = HL_BOLD | HL_UNDERLINE | HL_INVERSE | HL_STRIKETHROUGH; 416 } 417 418 /* 419 * Move one "cooked" screen line to the screen, but only the characters that 420 * have actually changed. Handle insert/delete character. 421 * "coloff" gives the first column on the screen for this line. 422 * "endcol" gives the columns where valid characters are. 423 * "clear_width" is the width of the window. It's > 0 if the rest of the line 424 * needs to be cleared, negative otherwise. 425 * "flags" can have bits: 426 * SLF_POPUP popup window 427 * SLF_RIGHTLEFT rightleft window: 428 * When TRUE and "clear_width" > 0, clear columns 0 to "endcol" 429 * When FALSE and "clear_width" > 0, clear columns "endcol" to "clear_width" 430 */ 431 void 432 screen_line( 433 int row, 434 int coloff, 435 int endcol, 436 int clear_width, 437 int flags UNUSED) 438 { 439 unsigned off_from; 440 unsigned off_to; 441 unsigned max_off_from; 442 unsigned max_off_to; 443 int col = 0; 444 int hl; 445 int force = FALSE; // force update rest of the line 446 int redraw_this // bool: does character need redraw? 447 #ifdef FEAT_GUI 448 = TRUE // For GUI when while-loop empty 449 #endif 450 ; 451 int redraw_next; // redraw_this for next character 452 int clear_next = FALSE; 453 int char_cells; // 1: normal char 454 // 2: occupies two display cells 455 # define CHAR_CELLS char_cells 456 457 // Check for illegal row and col, just in case. 458 if (row >= Rows) 459 row = Rows - 1; 460 if (endcol > Columns) 461 endcol = Columns; 462 463 # ifdef FEAT_CLIPBOARD 464 clip_may_clear_selection(row, row); 465 # endif 466 467 off_from = (unsigned)(current_ScreenLine - ScreenLines); 468 off_to = LineOffset[row] + coloff; 469 max_off_from = off_from + screen_Columns; 470 max_off_to = LineOffset[row] + screen_Columns; 471 472 #ifdef FEAT_RIGHTLEFT 473 if (flags & SLF_RIGHTLEFT) 474 { 475 // Clear rest first, because it's left of the text. 476 if (clear_width > 0) 477 { 478 while (col <= endcol && ScreenLines[off_to] == ' ' 479 && ScreenAttrs[off_to] == 0 480 && (!enc_utf8 || ScreenLinesUC[off_to] == 0)) 481 { 482 ++off_to; 483 ++col; 484 } 485 if (col <= endcol) 486 screen_fill(row, row + 1, col + coloff, 487 endcol + coloff + 1, ' ', ' ', 0); 488 } 489 col = endcol + 1; 490 off_to = LineOffset[row] + col + coloff; 491 off_from += col; 492 endcol = (clear_width > 0 ? clear_width : -clear_width); 493 } 494 #endif // FEAT_RIGHTLEFT 495 496 #ifdef FEAT_PROP_POPUP 497 // First char of a popup window may go on top of the right half of a 498 // double-wide character. Clear the left half to avoid it getting the popup 499 // window background color. 500 if (coloff > 0 && enc_utf8 501 && ScreenLines[off_to] == 0 502 && ScreenLinesUC[off_to - 1] != 0 503 && (*mb_char2cells)(ScreenLinesUC[off_to - 1]) > 1) 504 { 505 ScreenLines[off_to - 1] = ' '; 506 ScreenLinesUC[off_to - 1] = 0; 507 screen_char(off_to - 1, row, col + coloff - 1); 508 } 509 #endif 510 511 redraw_next = char_needs_redraw(off_from, off_to, endcol - col); 512 513 while (col < endcol) 514 { 515 if (has_mbyte && (col + 1 < endcol)) 516 char_cells = (*mb_off2cells)(off_from, max_off_from); 517 else 518 char_cells = 1; 519 520 redraw_this = redraw_next; 521 redraw_next = force || char_needs_redraw(off_from + CHAR_CELLS, 522 off_to + CHAR_CELLS, endcol - col - CHAR_CELLS); 523 524 #ifdef FEAT_GUI 525 // If the next character was bold, then redraw the current character to 526 // remove any pixels that might have spilt over into us. This only 527 // happens in the GUI. 528 if (redraw_next && gui.in_use) 529 { 530 hl = ScreenAttrs[off_to + CHAR_CELLS]; 531 if (hl > HL_ALL) 532 hl = syn_attr2attr(hl); 533 if (hl & HL_BOLD) 534 redraw_this = TRUE; 535 } 536 #endif 537 #ifdef FEAT_PROP_POPUP 538 if (blocked_by_popup(row, col + coloff)) 539 redraw_this = FALSE; 540 #endif 541 if (redraw_this) 542 { 543 /* 544 * Special handling when 'xs' termcap flag set (hpterm): 545 * Attributes for characters are stored at the position where the 546 * cursor is when writing the highlighting code. The 547 * start-highlighting code must be written with the cursor on the 548 * first highlighted character. The stop-highlighting code must 549 * be written with the cursor just after the last highlighted 550 * character. 551 * Overwriting a character doesn't remove its highlighting. Need 552 * to clear the rest of the line, and force redrawing it 553 * completely. 554 */ 555 if ( p_wiv 556 && !force 557 #ifdef FEAT_GUI 558 && !gui.in_use 559 #endif 560 && ScreenAttrs[off_to] != 0 561 && ScreenAttrs[off_from] != ScreenAttrs[off_to]) 562 { 563 /* 564 * Need to remove highlighting attributes here. 565 */ 566 windgoto(row, col + coloff); 567 out_str(T_CE); // clear rest of this screen line 568 screen_start(); // don't know where cursor is now 569 force = TRUE; // force redraw of rest of the line 570 redraw_next = TRUE; // or else next char would miss out 571 572 /* 573 * If the previous character was highlighted, need to stop 574 * highlighting at this character. 575 */ 576 if (col + coloff > 0 && ScreenAttrs[off_to - 1] != 0) 577 { 578 screen_attr = ScreenAttrs[off_to - 1]; 579 term_windgoto(row, col + coloff); 580 screen_stop_highlight(); 581 } 582 else 583 screen_attr = 0; // highlighting has stopped 584 } 585 if (enc_dbcs != 0) 586 { 587 // Check if overwriting a double-byte with a single-byte or 588 // the other way around requires another character to be 589 // redrawn. For UTF-8 this isn't needed, because comparing 590 // ScreenLinesUC[] is sufficient. 591 if (char_cells == 1 592 && col + 1 < endcol 593 && (*mb_off2cells)(off_to, max_off_to) > 1) 594 { 595 // Writing a single-cell character over a double-cell 596 // character: need to redraw the next cell. 597 ScreenLines[off_to + 1] = 0; 598 redraw_next = TRUE; 599 } 600 else if (char_cells == 2 601 && col + 2 < endcol 602 && (*mb_off2cells)(off_to, max_off_to) == 1 603 && (*mb_off2cells)(off_to + 1, max_off_to) > 1) 604 { 605 // Writing the second half of a double-cell character over 606 // a double-cell character: need to redraw the second 607 // cell. 608 ScreenLines[off_to + 2] = 0; 609 redraw_next = TRUE; 610 } 611 612 if (enc_dbcs == DBCS_JPNU) 613 ScreenLines2[off_to] = ScreenLines2[off_from]; 614 } 615 // When writing a single-width character over a double-width 616 // character and at the end of the redrawn text, need to clear out 617 // the right halve of the old character. 618 // Also required when writing the right halve of a double-width 619 // char over the left halve of an existing one. 620 if (has_mbyte && col + char_cells == endcol 621 && ((char_cells == 1 622 && (*mb_off2cells)(off_to, max_off_to) > 1) 623 || (char_cells == 2 624 && (*mb_off2cells)(off_to, max_off_to) == 1 625 && (*mb_off2cells)(off_to + 1, max_off_to) > 1))) 626 clear_next = TRUE; 627 628 ScreenLines[off_to] = ScreenLines[off_from]; 629 if (enc_utf8) 630 { 631 ScreenLinesUC[off_to] = ScreenLinesUC[off_from]; 632 if (ScreenLinesUC[off_from] != 0) 633 { 634 int i; 635 636 for (i = 0; i < Screen_mco; ++i) 637 ScreenLinesC[i][off_to] = ScreenLinesC[i][off_from]; 638 } 639 } 640 if (char_cells == 2) 641 ScreenLines[off_to + 1] = ScreenLines[off_from + 1]; 642 643 #if defined(FEAT_GUI) || defined(UNIX) 644 // The bold trick makes a single column of pixels appear in the 645 // next character. When a bold character is removed, the next 646 // character should be redrawn too. This happens for our own GUI 647 // and for some xterms. 648 if ( 649 # ifdef FEAT_GUI 650 gui.in_use 651 # endif 652 # if defined(FEAT_GUI) && defined(UNIX) 653 || 654 # endif 655 # ifdef UNIX 656 term_is_xterm 657 # endif 658 ) 659 { 660 hl = ScreenAttrs[off_to]; 661 if (hl > HL_ALL) 662 hl = syn_attr2attr(hl); 663 if (hl & HL_BOLD) 664 redraw_next = TRUE; 665 } 666 #endif 667 ScreenAttrs[off_to] = ScreenAttrs[off_from]; 668 669 // For simplicity set the attributes of second half of a 670 // double-wide character equal to the first half. 671 if (char_cells == 2) 672 ScreenAttrs[off_to + 1] = ScreenAttrs[off_from]; 673 674 if (enc_dbcs != 0 && char_cells == 2) 675 screen_char_2(off_to, row, col + coloff); 676 else 677 screen_char(off_to, row, col + coloff); 678 } 679 else if ( p_wiv 680 #ifdef FEAT_GUI 681 && !gui.in_use 682 #endif 683 && col + coloff > 0) 684 { 685 if (ScreenAttrs[off_to] == ScreenAttrs[off_to - 1]) 686 { 687 /* 688 * Don't output stop-highlight when moving the cursor, it will 689 * stop the highlighting when it should continue. 690 */ 691 screen_attr = 0; 692 } 693 else if (screen_attr != 0) 694 screen_stop_highlight(); 695 } 696 697 off_to += CHAR_CELLS; 698 off_from += CHAR_CELLS; 699 col += CHAR_CELLS; 700 } 701 702 if (clear_next) 703 { 704 // Clear the second half of a double-wide character of which the left 705 // half was overwritten with a single-wide character. 706 ScreenLines[off_to] = ' '; 707 if (enc_utf8) 708 ScreenLinesUC[off_to] = 0; 709 screen_char(off_to, row, col + coloff); 710 } 711 712 if (clear_width > 0 713 #ifdef FEAT_RIGHTLEFT 714 && !(flags & SLF_RIGHTLEFT) 715 #endif 716 ) 717 { 718 #ifdef FEAT_GUI 719 int startCol = col; 720 #endif 721 722 // blank out the rest of the line 723 while (col < clear_width && ScreenLines[off_to] == ' ' 724 && ScreenAttrs[off_to] == 0 725 && (!enc_utf8 || ScreenLinesUC[off_to] == 0)) 726 { 727 ++off_to; 728 ++col; 729 } 730 if (col < clear_width) 731 { 732 #ifdef FEAT_GUI 733 /* 734 * In the GUI, clearing the rest of the line may leave pixels 735 * behind if the first character cleared was bold. Some bold 736 * fonts spill over the left. In this case we redraw the previous 737 * character too. If we didn't skip any blanks above, then we 738 * only redraw if the character wasn't already redrawn anyway. 739 */ 740 if (gui.in_use && (col > startCol || !redraw_this)) 741 { 742 hl = ScreenAttrs[off_to]; 743 if (hl > HL_ALL || (hl & HL_BOLD)) 744 { 745 int prev_cells = 1; 746 747 if (enc_utf8) 748 // for utf-8, ScreenLines[char_offset + 1] == 0 means 749 // that its width is 2. 750 prev_cells = ScreenLines[off_to - 1] == 0 ? 2 : 1; 751 else if (enc_dbcs != 0) 752 { 753 // find previous character by counting from first 754 // column and get its width. 755 unsigned off = LineOffset[row]; 756 unsigned max_off = LineOffset[row] + screen_Columns; 757 758 while (off < off_to) 759 { 760 prev_cells = (*mb_off2cells)(off, max_off); 761 off += prev_cells; 762 } 763 } 764 765 if (enc_dbcs != 0 && prev_cells > 1) 766 screen_char_2(off_to - prev_cells, row, 767 col + coloff - prev_cells); 768 else 769 screen_char(off_to - prev_cells, row, 770 col + coloff - prev_cells); 771 } 772 } 773 #endif 774 screen_fill(row, row + 1, col + coloff, clear_width + coloff, 775 ' ', ' ', 0); 776 off_to += clear_width - col; 777 col = clear_width; 778 } 779 } 780 781 if (clear_width > 0 782 #ifdef FEAT_PROP_POPUP 783 && !(flags & SLF_POPUP) // no separator for popup window 784 #endif 785 ) 786 { 787 // For a window that has a right neighbor, draw the separator char 788 // right of the window contents. But not on top of a popup window. 789 if (coloff + col < Columns) 790 { 791 #ifdef FEAT_PROP_POPUP 792 if (!blocked_by_popup(row, col + coloff)) 793 #endif 794 { 795 int c; 796 797 c = fillchar_vsep(&hl); 798 if (ScreenLines[off_to] != (schar_T)c 799 || (enc_utf8 && (int)ScreenLinesUC[off_to] 800 != (c >= 0x80 ? c : 0)) 801 || ScreenAttrs[off_to] != hl) 802 { 803 ScreenLines[off_to] = c; 804 ScreenAttrs[off_to] = hl; 805 if (enc_utf8) 806 { 807 if (c >= 0x80) 808 { 809 ScreenLinesUC[off_to] = c; 810 ScreenLinesC[0][off_to] = 0; 811 } 812 else 813 ScreenLinesUC[off_to] = 0; 814 } 815 screen_char(off_to, row, col + coloff); 816 } 817 } 818 } 819 else 820 LineWraps[row] = FALSE; 821 } 822 } 823 824 #if defined(FEAT_RIGHTLEFT) || defined(PROTO) 825 /* 826 * Mirror text "str" for right-left displaying. 827 * Only works for single-byte characters (e.g., numbers). 828 */ 829 void 830 rl_mirror(char_u *str) 831 { 832 char_u *p1, *p2; 833 int t; 834 835 for (p1 = str, p2 = str + STRLEN(str) - 1; p1 < p2; ++p1, --p2) 836 { 837 t = *p1; 838 *p1 = *p2; 839 *p2 = t; 840 } 841 } 842 #endif 843 844 /* 845 * Draw the verticap separator right of window "wp" starting with line "row". 846 */ 847 void 848 draw_vsep_win(win_T *wp, int row) 849 { 850 int hl; 851 int c; 852 853 if (wp->w_vsep_width) 854 { 855 // draw the vertical separator right of this window 856 c = fillchar_vsep(&hl); 857 screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + wp->w_height, 858 W_ENDCOL(wp), W_ENDCOL(wp) + 1, 859 c, ' ', hl); 860 } 861 } 862 863 #ifdef FEAT_WILDMENU 864 static int skip_status_match_char(expand_T *xp, char_u *s); 865 866 /* 867 * Get the length of an item as it will be shown in the status line. 868 */ 869 static int 870 status_match_len(expand_T *xp, char_u *s) 871 { 872 int len = 0; 873 874 #ifdef FEAT_MENU 875 int emenu = (xp->xp_context == EXPAND_MENUS 876 || xp->xp_context == EXPAND_MENUNAMES); 877 878 // Check for menu separators - replace with '|'. 879 if (emenu && menu_is_separator(s)) 880 return 1; 881 #endif 882 883 while (*s != NUL) 884 { 885 s += skip_status_match_char(xp, s); 886 len += ptr2cells(s); 887 MB_PTR_ADV(s); 888 } 889 890 return len; 891 } 892 893 /* 894 * Return the number of characters that should be skipped in a status match. 895 * These are backslashes used for escaping. Do show backslashes in help tags. 896 */ 897 static int 898 skip_status_match_char(expand_T *xp, char_u *s) 899 { 900 if ((rem_backslash(s) && xp->xp_context != EXPAND_HELP) 901 #ifdef FEAT_MENU 902 || ((xp->xp_context == EXPAND_MENUS 903 || xp->xp_context == EXPAND_MENUNAMES) 904 && (s[0] == '\t' || (s[0] == '\\' && s[1] != NUL))) 905 #endif 906 ) 907 { 908 #ifndef BACKSLASH_IN_FILENAME 909 if (xp->xp_shell && csh_like_shell() && s[1] == '\\' && s[2] == '!') 910 return 2; 911 #endif 912 return 1; 913 } 914 return 0; 915 } 916 917 /* 918 * Show wildchar matches in the status line. 919 * Show at least the "match" item. 920 * We start at item 'first_match' in the list and show all matches that fit. 921 * 922 * If inversion is possible we use it. Else '=' characters are used. 923 */ 924 void 925 win_redr_status_matches( 926 expand_T *xp, 927 int num_matches, 928 char_u **matches, // list of matches 929 int match, 930 int showtail) 931 { 932 #define L_MATCH(m) (showtail ? sm_gettail(matches[m]) : matches[m]) 933 int row; 934 char_u *buf; 935 int len; 936 int clen; // length in screen cells 937 int fillchar; 938 int attr; 939 int i; 940 int highlight = TRUE; 941 char_u *selstart = NULL; 942 int selstart_col = 0; 943 char_u *selend = NULL; 944 static int first_match = 0; 945 int add_left = FALSE; 946 char_u *s; 947 #ifdef FEAT_MENU 948 int emenu; 949 #endif 950 int l; 951 952 if (matches == NULL) // interrupted completion? 953 return; 954 955 if (has_mbyte) 956 buf = alloc(Columns * MB_MAXBYTES + 1); 957 else 958 buf = alloc(Columns + 1); 959 if (buf == NULL) 960 return; 961 962 if (match == -1) // don't show match but original text 963 { 964 match = 0; 965 highlight = FALSE; 966 } 967 // count 1 for the ending ">" 968 clen = status_match_len(xp, L_MATCH(match)) + 3; 969 if (match == 0) 970 first_match = 0; 971 else if (match < first_match) 972 { 973 // jumping left, as far as we can go 974 first_match = match; 975 add_left = TRUE; 976 } 977 else 978 { 979 // check if match fits on the screen 980 for (i = first_match; i < match; ++i) 981 clen += status_match_len(xp, L_MATCH(i)) + 2; 982 if (first_match > 0) 983 clen += 2; 984 // jumping right, put match at the left 985 if ((long)clen > Columns) 986 { 987 first_match = match; 988 // if showing the last match, we can add some on the left 989 clen = 2; 990 for (i = match; i < num_matches; ++i) 991 { 992 clen += status_match_len(xp, L_MATCH(i)) + 2; 993 if ((long)clen >= Columns) 994 break; 995 } 996 if (i == num_matches) 997 add_left = TRUE; 998 } 999 } 1000 if (add_left) 1001 while (first_match > 0) 1002 { 1003 clen += status_match_len(xp, L_MATCH(first_match - 1)) + 2; 1004 if ((long)clen >= Columns) 1005 break; 1006 --first_match; 1007 } 1008 1009 fillchar = fillchar_status(&attr, curwin); 1010 1011 if (first_match == 0) 1012 { 1013 *buf = NUL; 1014 len = 0; 1015 } 1016 else 1017 { 1018 STRCPY(buf, "< "); 1019 len = 2; 1020 } 1021 clen = len; 1022 1023 i = first_match; 1024 while ((long)(clen + status_match_len(xp, L_MATCH(i)) + 2) < Columns) 1025 { 1026 if (i == match) 1027 { 1028 selstart = buf + len; 1029 selstart_col = clen; 1030 } 1031 1032 s = L_MATCH(i); 1033 // Check for menu separators - replace with '|' 1034 #ifdef FEAT_MENU 1035 emenu = (xp->xp_context == EXPAND_MENUS 1036 || xp->xp_context == EXPAND_MENUNAMES); 1037 if (emenu && menu_is_separator(s)) 1038 { 1039 STRCPY(buf + len, transchar('|')); 1040 l = (int)STRLEN(buf + len); 1041 len += l; 1042 clen += l; 1043 } 1044 else 1045 #endif 1046 for ( ; *s != NUL; ++s) 1047 { 1048 s += skip_status_match_char(xp, s); 1049 clen += ptr2cells(s); 1050 if (has_mbyte && (l = (*mb_ptr2len)(s)) > 1) 1051 { 1052 STRNCPY(buf + len, s, l); 1053 s += l - 1; 1054 len += l; 1055 } 1056 else 1057 { 1058 STRCPY(buf + len, transchar_byte(*s)); 1059 len += (int)STRLEN(buf + len); 1060 } 1061 } 1062 if (i == match) 1063 selend = buf + len; 1064 1065 *(buf + len++) = ' '; 1066 *(buf + len++) = ' '; 1067 clen += 2; 1068 if (++i == num_matches) 1069 break; 1070 } 1071 1072 if (i != num_matches) 1073 { 1074 *(buf + len++) = '>'; 1075 ++clen; 1076 } 1077 1078 buf[len] = NUL; 1079 1080 row = cmdline_row - 1; 1081 if (row >= 0) 1082 { 1083 if (wild_menu_showing == 0) 1084 { 1085 if (msg_scrolled > 0) 1086 { 1087 // Put the wildmenu just above the command line. If there is 1088 // no room, scroll the screen one line up. 1089 if (cmdline_row == Rows - 1) 1090 { 1091 screen_del_lines(0, 0, 1, (int)Rows, TRUE, 0, NULL); 1092 ++msg_scrolled; 1093 } 1094 else 1095 { 1096 ++cmdline_row; 1097 ++row; 1098 } 1099 wild_menu_showing = WM_SCROLLED; 1100 } 1101 else 1102 { 1103 // Create status line if needed by setting 'laststatus' to 2. 1104 // Set 'winminheight' to zero to avoid that the window is 1105 // resized. 1106 if (lastwin->w_status_height == 0) 1107 { 1108 save_p_ls = p_ls; 1109 save_p_wmh = p_wmh; 1110 p_ls = 2; 1111 p_wmh = 0; 1112 last_status(FALSE); 1113 } 1114 wild_menu_showing = WM_SHOWN; 1115 } 1116 } 1117 1118 screen_puts(buf, row, 0, attr); 1119 if (selstart != NULL && highlight) 1120 { 1121 *selend = NUL; 1122 screen_puts(selstart, row, selstart_col, HL_ATTR(HLF_WM)); 1123 } 1124 1125 screen_fill(row, row + 1, clen, (int)Columns, fillchar, fillchar, attr); 1126 } 1127 1128 win_redraw_last_status(topframe); 1129 vim_free(buf); 1130 } 1131 #endif 1132 1133 /* 1134 * Return TRUE if the status line of window "wp" is connected to the status 1135 * line of the window right of it. If not, then it's a vertical separator. 1136 * Only call if (wp->w_vsep_width != 0). 1137 */ 1138 int 1139 stl_connected(win_T *wp) 1140 { 1141 frame_T *fr; 1142 1143 fr = wp->w_frame; 1144 while (fr->fr_parent != NULL) 1145 { 1146 if (fr->fr_parent->fr_layout == FR_COL) 1147 { 1148 if (fr->fr_next != NULL) 1149 break; 1150 } 1151 else 1152 { 1153 if (fr->fr_next != NULL) 1154 return TRUE; 1155 } 1156 fr = fr->fr_parent; 1157 } 1158 return FALSE; 1159 } 1160 1161 1162 /* 1163 * Get the value to show for the language mappings, active 'keymap'. 1164 */ 1165 int 1166 get_keymap_str( 1167 win_T *wp, 1168 char_u *fmt, // format string containing one %s item 1169 char_u *buf, // buffer for the result 1170 int len) // length of buffer 1171 { 1172 char_u *p; 1173 1174 if (wp->w_buffer->b_p_iminsert != B_IMODE_LMAP) 1175 return FALSE; 1176 1177 { 1178 #ifdef FEAT_EVAL 1179 buf_T *old_curbuf = curbuf; 1180 win_T *old_curwin = curwin; 1181 char_u *s; 1182 1183 curbuf = wp->w_buffer; 1184 curwin = wp; 1185 STRCPY(buf, "b:keymap_name"); // must be writable 1186 ++emsg_skip; 1187 s = p = eval_to_string(buf, FALSE); 1188 --emsg_skip; 1189 curbuf = old_curbuf; 1190 curwin = old_curwin; 1191 if (p == NULL || *p == NUL) 1192 #endif 1193 { 1194 #ifdef FEAT_KEYMAP 1195 if (wp->w_buffer->b_kmap_state & KEYMAP_LOADED) 1196 p = wp->w_buffer->b_p_keymap; 1197 else 1198 #endif 1199 p = (char_u *)"lang"; 1200 } 1201 if (vim_snprintf((char *)buf, len, (char *)fmt, p) > len - 1) 1202 buf[0] = NUL; 1203 #ifdef FEAT_EVAL 1204 vim_free(s); 1205 #endif 1206 } 1207 return buf[0] != NUL; 1208 } 1209 1210 #if defined(FEAT_STL_OPT) || defined(PROTO) 1211 /* 1212 * Redraw the status line or ruler of window "wp". 1213 * When "wp" is NULL redraw the tab pages line from 'tabline'. 1214 */ 1215 void 1216 win_redr_custom( 1217 win_T *wp, 1218 int draw_ruler) // TRUE or FALSE 1219 { 1220 static int entered = FALSE; 1221 int attr; 1222 int curattr; 1223 int row; 1224 int col = 0; 1225 int maxwidth; 1226 int width; 1227 int n; 1228 int len; 1229 int fillchar; 1230 char_u buf[MAXPATHL]; 1231 char_u *stl; 1232 char_u *p; 1233 stl_hlrec_T *hltab; 1234 stl_hlrec_T *tabtab; 1235 int use_sandbox = FALSE; 1236 win_T *ewp; 1237 int p_crb_save; 1238 1239 // There is a tiny chance that this gets called recursively: When 1240 // redrawing a status line triggers redrawing the ruler or tabline. 1241 // Avoid trouble by not allowing recursion. 1242 if (entered) 1243 return; 1244 entered = TRUE; 1245 1246 // setup environment for the task at hand 1247 if (wp == NULL) 1248 { 1249 // Use 'tabline'. Always at the first line of the screen. 1250 stl = p_tal; 1251 row = 0; 1252 fillchar = ' '; 1253 attr = HL_ATTR(HLF_TPF); 1254 maxwidth = Columns; 1255 # ifdef FEAT_EVAL 1256 use_sandbox = was_set_insecurely((char_u *)"tabline", 0); 1257 # endif 1258 } 1259 else 1260 { 1261 row = statusline_row(wp); 1262 fillchar = fillchar_status(&attr, wp); 1263 maxwidth = wp->w_width; 1264 1265 if (draw_ruler) 1266 { 1267 stl = p_ruf; 1268 // advance past any leading group spec - implicit in ru_col 1269 if (*stl == '%') 1270 { 1271 if (*++stl == '-') 1272 stl++; 1273 if (atoi((char *)stl)) 1274 while (VIM_ISDIGIT(*stl)) 1275 stl++; 1276 if (*stl++ != '(') 1277 stl = p_ruf; 1278 } 1279 col = ru_col - (Columns - wp->w_width); 1280 if (col < (wp->w_width + 1) / 2) 1281 col = (wp->w_width + 1) / 2; 1282 maxwidth = wp->w_width - col; 1283 if (!wp->w_status_height) 1284 { 1285 row = Rows - 1; 1286 --maxwidth; // writing in last column may cause scrolling 1287 fillchar = ' '; 1288 attr = 0; 1289 } 1290 1291 # ifdef FEAT_EVAL 1292 use_sandbox = was_set_insecurely((char_u *)"rulerformat", 0); 1293 # endif 1294 } 1295 else 1296 { 1297 if (*wp->w_p_stl != NUL) 1298 stl = wp->w_p_stl; 1299 else 1300 stl = p_stl; 1301 # ifdef FEAT_EVAL 1302 use_sandbox = was_set_insecurely((char_u *)"statusline", 1303 *wp->w_p_stl == NUL ? 0 : OPT_LOCAL); 1304 # endif 1305 } 1306 1307 col += wp->w_wincol; 1308 } 1309 1310 if (maxwidth <= 0) 1311 goto theend; 1312 1313 // Temporarily reset 'cursorbind', we don't want a side effect from moving 1314 // the cursor away and back. 1315 ewp = wp == NULL ? curwin : wp; 1316 p_crb_save = ewp->w_p_crb; 1317 ewp->w_p_crb = FALSE; 1318 1319 // Make a copy, because the statusline may include a function call that 1320 // might change the option value and free the memory. 1321 stl = vim_strsave(stl); 1322 width = build_stl_str_hl(ewp, buf, sizeof(buf), 1323 stl, use_sandbox, 1324 fillchar, maxwidth, &hltab, &tabtab); 1325 vim_free(stl); 1326 ewp->w_p_crb = p_crb_save; 1327 1328 // Make all characters printable. 1329 p = transstr(buf); 1330 if (p != NULL) 1331 { 1332 vim_strncpy(buf, p, sizeof(buf) - 1); 1333 vim_free(p); 1334 } 1335 1336 // fill up with "fillchar" 1337 len = (int)STRLEN(buf); 1338 while (width < maxwidth && len < (int)sizeof(buf) - 1) 1339 { 1340 len += (*mb_char2bytes)(fillchar, buf + len); 1341 ++width; 1342 } 1343 buf[len] = NUL; 1344 1345 /* 1346 * Draw each snippet with the specified highlighting. 1347 */ 1348 curattr = attr; 1349 p = buf; 1350 for (n = 0; hltab[n].start != NULL; n++) 1351 { 1352 len = (int)(hltab[n].start - p); 1353 screen_puts_len(p, len, row, col, curattr); 1354 col += vim_strnsize(p, len); 1355 p = hltab[n].start; 1356 1357 if (hltab[n].userhl == 0) 1358 curattr = attr; 1359 else if (hltab[n].userhl < 0) 1360 curattr = syn_id2attr(-hltab[n].userhl); 1361 #ifdef FEAT_TERMINAL 1362 else if (wp != NULL && wp != curwin && bt_terminal(wp->w_buffer) 1363 && wp->w_status_height != 0) 1364 curattr = highlight_stltermnc[hltab[n].userhl - 1]; 1365 else if (wp != NULL && bt_terminal(wp->w_buffer) 1366 && wp->w_status_height != 0) 1367 curattr = highlight_stlterm[hltab[n].userhl - 1]; 1368 #endif 1369 else if (wp != NULL && wp != curwin && wp->w_status_height != 0) 1370 curattr = highlight_stlnc[hltab[n].userhl - 1]; 1371 else 1372 curattr = highlight_user[hltab[n].userhl - 1]; 1373 } 1374 screen_puts(p, row, col, curattr); 1375 1376 if (wp == NULL) 1377 { 1378 // Fill the TabPageIdxs[] array for clicking in the tab pagesline. 1379 col = 0; 1380 len = 0; 1381 p = buf; 1382 fillchar = 0; 1383 for (n = 0; tabtab[n].start != NULL; n++) 1384 { 1385 len += vim_strnsize(p, (int)(tabtab[n].start - p)); 1386 while (col < len) 1387 TabPageIdxs[col++] = fillchar; 1388 p = tabtab[n].start; 1389 fillchar = tabtab[n].userhl; 1390 } 1391 while (col < Columns) 1392 TabPageIdxs[col++] = fillchar; 1393 } 1394 1395 theend: 1396 entered = FALSE; 1397 } 1398 1399 #endif // FEAT_STL_OPT 1400 1401 /* 1402 * Output a single character directly to the screen and update ScreenLines. 1403 */ 1404 void 1405 screen_putchar(int c, int row, int col, int attr) 1406 { 1407 char_u buf[MB_MAXBYTES + 1]; 1408 1409 if (has_mbyte) 1410 buf[(*mb_char2bytes)(c, buf)] = NUL; 1411 else 1412 { 1413 buf[0] = c; 1414 buf[1] = NUL; 1415 } 1416 screen_puts(buf, row, col, attr); 1417 } 1418 1419 /* 1420 * Get a single character directly from ScreenLines into "bytes[]". 1421 * Also return its attribute in *attrp; 1422 */ 1423 void 1424 screen_getbytes(int row, int col, char_u *bytes, int *attrp) 1425 { 1426 unsigned off; 1427 1428 // safety check 1429 if (ScreenLines != NULL && row < screen_Rows && col < screen_Columns) 1430 { 1431 off = LineOffset[row] + col; 1432 *attrp = ScreenAttrs[off]; 1433 bytes[0] = ScreenLines[off]; 1434 bytes[1] = NUL; 1435 1436 if (enc_utf8 && ScreenLinesUC[off] != 0) 1437 bytes[utfc_char2bytes(off, bytes)] = NUL; 1438 else if (enc_dbcs == DBCS_JPNU && ScreenLines[off] == 0x8e) 1439 { 1440 bytes[0] = ScreenLines[off]; 1441 bytes[1] = ScreenLines2[off]; 1442 bytes[2] = NUL; 1443 } 1444 else if (enc_dbcs && MB_BYTE2LEN(bytes[0]) > 1) 1445 { 1446 bytes[1] = ScreenLines[off + 1]; 1447 bytes[2] = NUL; 1448 } 1449 } 1450 } 1451 1452 /* 1453 * Return TRUE if composing characters for screen posn "off" differs from 1454 * composing characters in "u8cc". 1455 * Only to be used when ScreenLinesUC[off] != 0. 1456 */ 1457 static int 1458 screen_comp_differs(int off, int *u8cc) 1459 { 1460 int i; 1461 1462 for (i = 0; i < Screen_mco; ++i) 1463 { 1464 if (ScreenLinesC[i][off] != (u8char_T)u8cc[i]) 1465 return TRUE; 1466 if (u8cc[i] == 0) 1467 break; 1468 } 1469 return FALSE; 1470 } 1471 1472 /* 1473 * Put string '*text' on the screen at position 'row' and 'col', with 1474 * attributes 'attr', and update ScreenLines[] and ScreenAttrs[]. 1475 * Note: only outputs within one row, message is truncated at screen boundary! 1476 * Note: if ScreenLines[], row and/or col is invalid, nothing is done. 1477 */ 1478 void 1479 screen_puts( 1480 char_u *text, 1481 int row, 1482 int col, 1483 int attr) 1484 { 1485 screen_puts_len(text, -1, row, col, attr); 1486 } 1487 1488 /* 1489 * Like screen_puts(), but output "text[len]". When "len" is -1 output up to 1490 * a NUL. 1491 */ 1492 void 1493 screen_puts_len( 1494 char_u *text, 1495 int textlen, 1496 int row, 1497 int col, 1498 int attr) 1499 { 1500 unsigned off; 1501 char_u *ptr = text; 1502 int len = textlen; 1503 int c; 1504 unsigned max_off; 1505 int mbyte_blen = 1; 1506 int mbyte_cells = 1; 1507 int u8c = 0; 1508 int u8cc[MAX_MCO]; 1509 int clear_next_cell = FALSE; 1510 #ifdef FEAT_ARABIC 1511 int prev_c = 0; // previous Arabic character 1512 int pc, nc, nc1; 1513 int pcc[MAX_MCO]; 1514 #endif 1515 int force_redraw_this; 1516 int force_redraw_next = FALSE; 1517 int need_redraw; 1518 1519 // Safety check. The check for negative row and column is to fix issue 1520 // #4102. TODO: find out why row/col could be negative. 1521 if (ScreenLines == NULL 1522 || row >= screen_Rows || row < 0 1523 || col >= screen_Columns || col < 0) 1524 return; 1525 off = LineOffset[row] + col; 1526 1527 // When drawing over the right halve of a double-wide char clear out the 1528 // left halve. Only needed in a terminal. 1529 if (has_mbyte && col > 0 && col < screen_Columns 1530 #ifdef FEAT_GUI 1531 && !gui.in_use 1532 #endif 1533 && mb_fix_col(col, row) != col) 1534 { 1535 ScreenLines[off - 1] = ' '; 1536 ScreenAttrs[off - 1] = 0; 1537 if (enc_utf8) 1538 { 1539 ScreenLinesUC[off - 1] = 0; 1540 ScreenLinesC[0][off - 1] = 0; 1541 } 1542 // redraw the previous cell, make it empty 1543 screen_char(off - 1, row, col - 1); 1544 // force the cell at "col" to be redrawn 1545 force_redraw_next = TRUE; 1546 } 1547 1548 max_off = LineOffset[row] + screen_Columns; 1549 while (col < screen_Columns 1550 && (len < 0 || (int)(ptr - text) < len) 1551 && *ptr != NUL) 1552 { 1553 c = *ptr; 1554 // check if this is the first byte of a multibyte 1555 if (has_mbyte) 1556 { 1557 if (enc_utf8 && len > 0) 1558 mbyte_blen = utfc_ptr2len_len(ptr, (int)((text + len) - ptr)); 1559 else 1560 mbyte_blen = (*mb_ptr2len)(ptr); 1561 if (enc_dbcs == DBCS_JPNU && c == 0x8e) 1562 mbyte_cells = 1; 1563 else if (enc_dbcs != 0) 1564 mbyte_cells = mbyte_blen; 1565 else // enc_utf8 1566 { 1567 if (len >= 0) 1568 u8c = utfc_ptr2char_len(ptr, u8cc, 1569 (int)((text + len) - ptr)); 1570 else 1571 u8c = utfc_ptr2char(ptr, u8cc); 1572 mbyte_cells = utf_char2cells(u8c); 1573 #ifdef FEAT_ARABIC 1574 if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) 1575 { 1576 // Do Arabic shaping. 1577 if (len >= 0 && (int)(ptr - text) + mbyte_blen >= len) 1578 { 1579 // Past end of string to be displayed. 1580 nc = NUL; 1581 nc1 = NUL; 1582 } 1583 else 1584 { 1585 nc = utfc_ptr2char_len(ptr + mbyte_blen, pcc, 1586 (int)((text + len) - ptr - mbyte_blen)); 1587 nc1 = pcc[0]; 1588 } 1589 pc = prev_c; 1590 prev_c = u8c; 1591 u8c = arabic_shape(u8c, &c, &u8cc[0], nc, nc1, pc); 1592 } 1593 else 1594 prev_c = u8c; 1595 #endif 1596 if (col + mbyte_cells > screen_Columns) 1597 { 1598 // Only 1 cell left, but character requires 2 cells: 1599 // display a '>' in the last column to avoid wrapping. 1600 c = '>'; 1601 mbyte_cells = 1; 1602 } 1603 } 1604 } 1605 1606 force_redraw_this = force_redraw_next; 1607 force_redraw_next = FALSE; 1608 1609 need_redraw = ScreenLines[off] != c 1610 || (mbyte_cells == 2 1611 && ScreenLines[off + 1] != (enc_dbcs ? ptr[1] : 0)) 1612 || (enc_dbcs == DBCS_JPNU 1613 && c == 0x8e 1614 && ScreenLines2[off] != ptr[1]) 1615 || (enc_utf8 1616 && (ScreenLinesUC[off] != 1617 (u8char_T)(c < 0x80 && u8cc[0] == 0 ? 0 : u8c) 1618 || (ScreenLinesUC[off] != 0 1619 && screen_comp_differs(off, u8cc)))) 1620 || ScreenAttrs[off] != attr 1621 || exmode_active; 1622 1623 if ((need_redraw || force_redraw_this) 1624 #ifdef FEAT_PROP_POPUP 1625 && !blocked_by_popup(row, col) 1626 #endif 1627 ) 1628 { 1629 #if defined(FEAT_GUI) || defined(UNIX) 1630 // The bold trick makes a single row of pixels appear in the next 1631 // character. When a bold character is removed, the next 1632 // character should be redrawn too. This happens for our own GUI 1633 // and for some xterms. 1634 if (need_redraw && ScreenLines[off] != ' ' && ( 1635 # ifdef FEAT_GUI 1636 gui.in_use 1637 # endif 1638 # if defined(FEAT_GUI) && defined(UNIX) 1639 || 1640 # endif 1641 # ifdef UNIX 1642 term_is_xterm 1643 # endif 1644 )) 1645 { 1646 int n = ScreenAttrs[off]; 1647 1648 if (n > HL_ALL) 1649 n = syn_attr2attr(n); 1650 if (n & HL_BOLD) 1651 force_redraw_next = TRUE; 1652 } 1653 #endif 1654 // When at the end of the text and overwriting a two-cell 1655 // character with a one-cell character, need to clear the next 1656 // cell. Also when overwriting the left halve of a two-cell char 1657 // with the right halve of a two-cell char. Do this only once 1658 // (mb_off2cells() may return 2 on the right halve). 1659 if (clear_next_cell) 1660 clear_next_cell = FALSE; 1661 else if (has_mbyte 1662 && (len < 0 ? ptr[mbyte_blen] == NUL 1663 : ptr + mbyte_blen >= text + len) 1664 && ((mbyte_cells == 1 && (*mb_off2cells)(off, max_off) > 1) 1665 || (mbyte_cells == 2 1666 && (*mb_off2cells)(off, max_off) == 1 1667 && (*mb_off2cells)(off + 1, max_off) > 1))) 1668 clear_next_cell = TRUE; 1669 1670 // Make sure we never leave a second byte of a double-byte behind, 1671 // it confuses mb_off2cells(). 1672 if (enc_dbcs 1673 && ((mbyte_cells == 1 && (*mb_off2cells)(off, max_off) > 1) 1674 || (mbyte_cells == 2 1675 && (*mb_off2cells)(off, max_off) == 1 1676 && (*mb_off2cells)(off + 1, max_off) > 1))) 1677 ScreenLines[off + mbyte_blen] = 0; 1678 ScreenLines[off] = c; 1679 ScreenAttrs[off] = attr; 1680 if (enc_utf8) 1681 { 1682 if (c < 0x80 && u8cc[0] == 0) 1683 ScreenLinesUC[off] = 0; 1684 else 1685 { 1686 int i; 1687 1688 ScreenLinesUC[off] = u8c; 1689 for (i = 0; i < Screen_mco; ++i) 1690 { 1691 ScreenLinesC[i][off] = u8cc[i]; 1692 if (u8cc[i] == 0) 1693 break; 1694 } 1695 } 1696 if (mbyte_cells == 2) 1697 { 1698 ScreenLines[off + 1] = 0; 1699 ScreenAttrs[off + 1] = attr; 1700 } 1701 screen_char(off, row, col); 1702 } 1703 else if (mbyte_cells == 2) 1704 { 1705 ScreenLines[off + 1] = ptr[1]; 1706 ScreenAttrs[off + 1] = attr; 1707 screen_char_2(off, row, col); 1708 } 1709 else if (enc_dbcs == DBCS_JPNU && c == 0x8e) 1710 { 1711 ScreenLines2[off] = ptr[1]; 1712 screen_char(off, row, col); 1713 } 1714 else 1715 screen_char(off, row, col); 1716 } 1717 if (has_mbyte) 1718 { 1719 off += mbyte_cells; 1720 col += mbyte_cells; 1721 ptr += mbyte_blen; 1722 if (clear_next_cell) 1723 { 1724 // This only happens at the end, display one space next. 1725 ptr = (char_u *)" "; 1726 len = -1; 1727 } 1728 } 1729 else 1730 { 1731 ++off; 1732 ++col; 1733 ++ptr; 1734 } 1735 } 1736 1737 // If we detected the next character needs to be redrawn, but the text 1738 // doesn't extend up to there, update the character here. 1739 if (force_redraw_next && col < screen_Columns) 1740 { 1741 if (enc_dbcs != 0 && dbcs_off2cells(off, max_off) > 1) 1742 screen_char_2(off, row, col); 1743 else 1744 screen_char(off, row, col); 1745 } 1746 } 1747 1748 #if defined(FEAT_SEARCH_EXTRA) || defined(PROTO) 1749 /* 1750 * Prepare for 'hlsearch' highlighting. 1751 */ 1752 void 1753 start_search_hl(void) 1754 { 1755 if (p_hls && !no_hlsearch) 1756 { 1757 end_search_hl(); // just in case it wasn't called before 1758 last_pat_prog(&screen_search_hl.rm); 1759 screen_search_hl.attr = HL_ATTR(HLF_L); 1760 # ifdef FEAT_RELTIME 1761 // Set the time limit to 'redrawtime'. 1762 profile_setlimit(p_rdt, &screen_search_hl.tm); 1763 # endif 1764 } 1765 } 1766 1767 /* 1768 * Clean up for 'hlsearch' highlighting. 1769 */ 1770 void 1771 end_search_hl(void) 1772 { 1773 if (screen_search_hl.rm.regprog != NULL) 1774 { 1775 vim_regfree(screen_search_hl.rm.regprog); 1776 screen_search_hl.rm.regprog = NULL; 1777 } 1778 } 1779 #endif 1780 1781 static void 1782 screen_start_highlight(int attr) 1783 { 1784 attrentry_T *aep = NULL; 1785 1786 screen_attr = attr; 1787 if (full_screen 1788 #ifdef MSWIN 1789 && termcap_active 1790 #endif 1791 ) 1792 { 1793 #ifdef FEAT_GUI 1794 if (gui.in_use) 1795 { 1796 char buf[20]; 1797 1798 // The GUI handles this internally. 1799 sprintf(buf, IF_EB("\033|%dh", ESC_STR "|%dh"), attr); 1800 OUT_STR(buf); 1801 } 1802 else 1803 #endif 1804 { 1805 if (attr > HL_ALL) // special HL attr. 1806 { 1807 if (IS_CTERM) 1808 aep = syn_cterm_attr2entry(attr); 1809 else 1810 aep = syn_term_attr2entry(attr); 1811 if (aep == NULL) // did ":syntax clear" 1812 attr = 0; 1813 else 1814 attr = aep->ae_attr; 1815 } 1816 #if defined(FEAT_VTP) && defined(FEAT_TERMGUICOLORS) 1817 if (use_vtp()) 1818 { 1819 guicolor_T defguifg, defguibg; 1820 int defctermfg, defctermbg; 1821 1822 // If FG and BG are unset, the color is undefined when 1823 // BOLD+INVERSE. Use Normal as the default value. 1824 get_default_console_color(&defctermfg, &defctermbg, &defguifg, 1825 &defguibg); 1826 1827 if (p_tgc) 1828 { 1829 if (aep == NULL || COLOR_INVALID(aep->ae_u.cterm.fg_rgb)) 1830 term_fg_rgb_color(defguifg); 1831 if (aep == NULL || COLOR_INVALID(aep->ae_u.cterm.bg_rgb)) 1832 term_bg_rgb_color(defguibg); 1833 } 1834 else if (t_colors >= 256) 1835 { 1836 if (aep == NULL || aep->ae_u.cterm.fg_color == 0) 1837 term_fg_color(defctermfg); 1838 if (aep == NULL || aep->ae_u.cterm.bg_color == 0) 1839 term_bg_color(defctermbg); 1840 } 1841 } 1842 #endif 1843 if ((attr & HL_BOLD) && *T_MD != NUL) // bold 1844 out_str(T_MD); 1845 else if (aep != NULL && cterm_normal_fg_bold && ( 1846 #ifdef FEAT_TERMGUICOLORS 1847 p_tgc && aep->ae_u.cterm.fg_rgb != CTERMCOLOR 1848 ? aep->ae_u.cterm.fg_rgb != INVALCOLOR 1849 : 1850 #endif 1851 t_colors > 1 && aep->ae_u.cterm.fg_color)) 1852 // If the Normal FG color has BOLD attribute and the new HL 1853 // has a FG color defined, clear BOLD. 1854 out_str(T_ME); 1855 if ((attr & HL_STANDOUT) && *T_SO != NUL) // standout 1856 out_str(T_SO); 1857 if ((attr & HL_UNDERCURL) && *T_UCS != NUL) // undercurl 1858 out_str(T_UCS); 1859 if (((attr & HL_UNDERLINE) // underline or undercurl 1860 || ((attr & HL_UNDERCURL) && *T_UCS == NUL)) 1861 && *T_US != NUL) 1862 out_str(T_US); 1863 if ((attr & HL_ITALIC) && *T_CZH != NUL) // italic 1864 out_str(T_CZH); 1865 if ((attr & HL_INVERSE) && *T_MR != NUL) // inverse (reverse) 1866 out_str(T_MR); 1867 if ((attr & HL_STRIKETHROUGH) && *T_STS != NUL) // strike 1868 out_str(T_STS); 1869 1870 /* 1871 * Output the color or start string after bold etc., in case the 1872 * bold etc. override the color setting. 1873 */ 1874 if (aep != NULL) 1875 { 1876 #ifdef FEAT_TERMGUICOLORS 1877 // When 'termguicolors' is set but fg or bg is unset, 1878 // fall back to the cterm colors. This helps for SpellBad, 1879 // where the GUI uses a red undercurl. 1880 if (p_tgc && aep->ae_u.cterm.fg_rgb != CTERMCOLOR) 1881 { 1882 if (aep->ae_u.cterm.fg_rgb != INVALCOLOR) 1883 term_fg_rgb_color(aep->ae_u.cterm.fg_rgb); 1884 } 1885 else 1886 #endif 1887 if (t_colors > 1) 1888 { 1889 if (aep->ae_u.cterm.fg_color) 1890 term_fg_color(aep->ae_u.cterm.fg_color - 1); 1891 } 1892 #ifdef FEAT_TERMGUICOLORS 1893 if (p_tgc && aep->ae_u.cterm.bg_rgb != CTERMCOLOR) 1894 { 1895 if (aep->ae_u.cterm.bg_rgb != INVALCOLOR) 1896 term_bg_rgb_color(aep->ae_u.cterm.bg_rgb); 1897 } 1898 else 1899 #endif 1900 if (t_colors > 1) 1901 { 1902 if (aep->ae_u.cterm.bg_color) 1903 term_bg_color(aep->ae_u.cterm.bg_color - 1); 1904 } 1905 #ifdef FEAT_TERMGUICOLORS 1906 if (p_tgc && aep->ae_u.cterm.ul_rgb != CTERMCOLOR) 1907 { 1908 if (aep->ae_u.cterm.ul_rgb != INVALCOLOR) 1909 term_ul_rgb_color(aep->ae_u.cterm.ul_rgb); 1910 } 1911 else 1912 #endif 1913 if (t_colors > 1) 1914 { 1915 if (aep->ae_u.cterm.ul_color) 1916 term_ul_color(aep->ae_u.cterm.ul_color - 1); 1917 } 1918 1919 if (!IS_CTERM) 1920 { 1921 if (aep->ae_u.term.start != NULL) 1922 out_str(aep->ae_u.term.start); 1923 } 1924 } 1925 } 1926 } 1927 } 1928 1929 void 1930 screen_stop_highlight(void) 1931 { 1932 int do_ME = FALSE; // output T_ME code 1933 #if defined(FEAT_VTP) && defined(FEAT_TERMGUICOLORS) 1934 int do_ME_fg = FALSE, do_ME_bg = FALSE; 1935 #endif 1936 1937 if (screen_attr != 0 1938 #ifdef MSWIN 1939 && termcap_active 1940 #endif 1941 ) 1942 { 1943 #ifdef FEAT_GUI 1944 if (gui.in_use) 1945 { 1946 char buf[20]; 1947 1948 // use internal GUI code 1949 sprintf(buf, IF_EB("\033|%dH", ESC_STR "|%dH"), screen_attr); 1950 OUT_STR(buf); 1951 } 1952 else 1953 #endif 1954 { 1955 if (screen_attr > HL_ALL) // special HL attr. 1956 { 1957 attrentry_T *aep; 1958 1959 if (IS_CTERM) 1960 { 1961 /* 1962 * Assume that t_me restores the original colors! 1963 */ 1964 aep = syn_cterm_attr2entry(screen_attr); 1965 if (aep != NULL && (( 1966 #ifdef FEAT_TERMGUICOLORS 1967 p_tgc && aep->ae_u.cterm.fg_rgb != CTERMCOLOR 1968 ? aep->ae_u.cterm.fg_rgb != INVALCOLOR 1969 # ifdef FEAT_VTP 1970 ? !(do_ME_fg = TRUE) : (do_ME_fg = FALSE) 1971 # endif 1972 : 1973 #endif 1974 aep->ae_u.cterm.fg_color) || ( 1975 #ifdef FEAT_TERMGUICOLORS 1976 p_tgc && aep->ae_u.cterm.bg_rgb != CTERMCOLOR 1977 ? aep->ae_u.cterm.bg_rgb != INVALCOLOR 1978 # ifdef FEAT_VTP 1979 ? !(do_ME_bg = TRUE) : (do_ME_bg = FALSE) 1980 # endif 1981 : 1982 #endif 1983 aep->ae_u.cterm.bg_color))) 1984 do_ME = TRUE; 1985 #if defined(FEAT_VTP) && defined(FEAT_TERMGUICOLORS) 1986 if (use_vtp()) 1987 { 1988 if (do_ME_fg && do_ME_bg) 1989 do_ME = TRUE; 1990 1991 // FG and BG cannot be separated in T_ME, which is not 1992 // efficient. 1993 if (!do_ME && do_ME_fg) 1994 out_str((char_u *)"\033|39m"); // restore FG 1995 if (!do_ME && do_ME_bg) 1996 out_str((char_u *)"\033|49m"); // restore BG 1997 } 1998 else 1999 { 2000 // Process FG and BG at once. 2001 if (!do_ME) 2002 do_ME = do_ME_fg | do_ME_bg; 2003 } 2004 #endif 2005 } 2006 else 2007 { 2008 aep = syn_term_attr2entry(screen_attr); 2009 if (aep != NULL && aep->ae_u.term.stop != NULL) 2010 { 2011 if (STRCMP(aep->ae_u.term.stop, T_ME) == 0) 2012 do_ME = TRUE; 2013 else 2014 out_str(aep->ae_u.term.stop); 2015 } 2016 } 2017 if (aep == NULL) // did ":syntax clear" 2018 screen_attr = 0; 2019 else 2020 screen_attr = aep->ae_attr; 2021 } 2022 2023 /* 2024 * Often all ending-codes are equal to T_ME. Avoid outputting the 2025 * same sequence several times. 2026 */ 2027 if (screen_attr & HL_STANDOUT) 2028 { 2029 if (STRCMP(T_SE, T_ME) == 0) 2030 do_ME = TRUE; 2031 else 2032 out_str(T_SE); 2033 } 2034 if ((screen_attr & HL_UNDERCURL) && *T_UCE != NUL) 2035 { 2036 if (STRCMP(T_UCE, T_ME) == 0) 2037 do_ME = TRUE; 2038 else 2039 out_str(T_UCE); 2040 } 2041 if ((screen_attr & HL_UNDERLINE) 2042 || ((screen_attr & HL_UNDERCURL) && *T_UCE == NUL)) 2043 { 2044 if (STRCMP(T_UE, T_ME) == 0) 2045 do_ME = TRUE; 2046 else 2047 out_str(T_UE); 2048 } 2049 if (screen_attr & HL_ITALIC) 2050 { 2051 if (STRCMP(T_CZR, T_ME) == 0) 2052 do_ME = TRUE; 2053 else 2054 out_str(T_CZR); 2055 } 2056 if (screen_attr & HL_STRIKETHROUGH) 2057 { 2058 if (STRCMP(T_STE, T_ME) == 0) 2059 do_ME = TRUE; 2060 else 2061 out_str(T_STE); 2062 } 2063 if (do_ME || (screen_attr & (HL_BOLD | HL_INVERSE))) 2064 out_str(T_ME); 2065 2066 #ifdef FEAT_TERMGUICOLORS 2067 if (p_tgc) 2068 { 2069 if (cterm_normal_fg_gui_color != INVALCOLOR) 2070 term_fg_rgb_color(cterm_normal_fg_gui_color); 2071 if (cterm_normal_bg_gui_color != INVALCOLOR) 2072 term_bg_rgb_color(cterm_normal_bg_gui_color); 2073 if (cterm_normal_ul_gui_color != INVALCOLOR) 2074 term_ul_rgb_color(cterm_normal_ul_gui_color); 2075 } 2076 else 2077 #endif 2078 { 2079 if (t_colors > 1) 2080 { 2081 // set Normal cterm colors 2082 if (cterm_normal_fg_color != 0) 2083 term_fg_color(cterm_normal_fg_color - 1); 2084 if (cterm_normal_bg_color != 0) 2085 term_bg_color(cterm_normal_bg_color - 1); 2086 if (cterm_normal_ul_color != 0) 2087 term_ul_color(cterm_normal_ul_color - 1); 2088 if (cterm_normal_fg_bold) 2089 out_str(T_MD); 2090 } 2091 } 2092 } 2093 } 2094 screen_attr = 0; 2095 } 2096 2097 /* 2098 * Reset the colors for a cterm. Used when leaving Vim. 2099 * The machine specific code may override this again. 2100 */ 2101 void 2102 reset_cterm_colors(void) 2103 { 2104 if (IS_CTERM) 2105 { 2106 // set Normal cterm colors 2107 #ifdef FEAT_TERMGUICOLORS 2108 if (p_tgc ? (cterm_normal_fg_gui_color != INVALCOLOR 2109 || cterm_normal_bg_gui_color != INVALCOLOR) 2110 : (cterm_normal_fg_color > 0 || cterm_normal_bg_color > 0)) 2111 #else 2112 if (cterm_normal_fg_color > 0 || cterm_normal_bg_color > 0) 2113 #endif 2114 { 2115 out_str(T_OP); 2116 screen_attr = -1; 2117 } 2118 if (cterm_normal_fg_bold) 2119 { 2120 out_str(T_ME); 2121 screen_attr = -1; 2122 } 2123 } 2124 } 2125 2126 /* 2127 * Put character ScreenLines["off"] on the screen at position "row" and "col", 2128 * using the attributes from ScreenAttrs["off"]. 2129 */ 2130 void 2131 screen_char(unsigned off, int row, int col) 2132 { 2133 int attr; 2134 2135 // Check for illegal values, just in case (could happen just after 2136 // resizing). 2137 if (row >= screen_Rows || col >= screen_Columns) 2138 return; 2139 2140 // Skip if under the popup menu. 2141 // Popup windows with zindex higher than POPUPMENU_ZINDEX go on top. 2142 if (pum_under_menu(row, col) 2143 #ifdef FEAT_PROP_POPUP 2144 && screen_zindex <= POPUPMENU_ZINDEX 2145 #endif 2146 ) 2147 return; 2148 #ifdef FEAT_PROP_POPUP 2149 if (blocked_by_popup(row, col)) 2150 return; 2151 #endif 2152 2153 // Outputting a character in the last cell on the screen may scroll the 2154 // screen up. Only do it when the "xn" termcap property is set, otherwise 2155 // mark the character invalid (update it when scrolled up). 2156 if (*T_XN == NUL 2157 && row == screen_Rows - 1 && col == screen_Columns - 1 2158 #ifdef FEAT_RIGHTLEFT 2159 // account for first command-line character in rightleft mode 2160 && !cmdmsg_rl 2161 #endif 2162 ) 2163 { 2164 ScreenAttrs[off] = (sattr_T)-1; 2165 return; 2166 } 2167 2168 /* 2169 * Stop highlighting first, so it's easier to move the cursor. 2170 */ 2171 if (screen_char_attr != 0) 2172 attr = screen_char_attr; 2173 else 2174 attr = ScreenAttrs[off]; 2175 if (screen_attr != attr) 2176 screen_stop_highlight(); 2177 2178 windgoto(row, col); 2179 2180 if (screen_attr != attr) 2181 screen_start_highlight(attr); 2182 2183 if (enc_utf8 && ScreenLinesUC[off] != 0) 2184 { 2185 char_u buf[MB_MAXBYTES + 1]; 2186 2187 if (utf_ambiguous_width(ScreenLinesUC[off])) 2188 { 2189 if (*p_ambw == 'd' 2190 #ifdef FEAT_GUI 2191 && !gui.in_use 2192 #endif 2193 ) 2194 { 2195 // Clear the two screen cells. If the character is actually 2196 // single width it won't change the second cell. 2197 out_str((char_u *)" "); 2198 term_windgoto(row, col); 2199 } 2200 // not sure where the cursor is after drawing the ambiguous width 2201 // character 2202 screen_cur_col = 9999; 2203 } 2204 else if (utf_char2cells(ScreenLinesUC[off]) > 1) 2205 ++screen_cur_col; 2206 2207 // Convert the UTF-8 character to bytes and write it. 2208 buf[utfc_char2bytes(off, buf)] = NUL; 2209 out_str(buf); 2210 } 2211 else 2212 { 2213 out_flush_check(); 2214 out_char(ScreenLines[off]); 2215 // double-byte character in single-width cell 2216 if (enc_dbcs == DBCS_JPNU && ScreenLines[off] == 0x8e) 2217 out_char(ScreenLines2[off]); 2218 } 2219 2220 screen_cur_col++; 2221 } 2222 2223 /* 2224 * Used for enc_dbcs only: Put one double-wide character at ScreenLines["off"] 2225 * on the screen at position 'row' and 'col'. 2226 * The attributes of the first byte is used for all. This is required to 2227 * output the two bytes of a double-byte character with nothing in between. 2228 */ 2229 static void 2230 screen_char_2(unsigned off, int row, int col) 2231 { 2232 // Check for illegal values (could be wrong when screen was resized). 2233 if (off + 1 >= (unsigned)(screen_Rows * screen_Columns)) 2234 return; 2235 2236 // Outputting the last character on the screen may scrollup the screen. 2237 // Don't to it! Mark the character invalid (update it when scrolled up) 2238 if (row == screen_Rows - 1 && col >= screen_Columns - 2) 2239 { 2240 ScreenAttrs[off] = (sattr_T)-1; 2241 return; 2242 } 2243 2244 // Output the first byte normally (positions the cursor), then write the 2245 // second byte directly. 2246 screen_char(off, row, col); 2247 out_char(ScreenLines[off + 1]); 2248 ++screen_cur_col; 2249 } 2250 2251 /* 2252 * Draw a rectangle of the screen, inverted when "invert" is TRUE. 2253 * This uses the contents of ScreenLines[] and doesn't change it. 2254 */ 2255 void 2256 screen_draw_rectangle( 2257 int row, 2258 int col, 2259 int height, 2260 int width, 2261 int invert) 2262 { 2263 int r, c; 2264 int off; 2265 int max_off; 2266 2267 // Can't use ScreenLines unless initialized 2268 if (ScreenLines == NULL) 2269 return; 2270 2271 if (invert) 2272 screen_char_attr = HL_INVERSE; 2273 for (r = row; r < row + height; ++r) 2274 { 2275 off = LineOffset[r]; 2276 max_off = off + screen_Columns; 2277 for (c = col; c < col + width; ++c) 2278 { 2279 if (enc_dbcs != 0 && dbcs_off2cells(off + c, max_off) > 1) 2280 { 2281 screen_char_2(off + c, r, c); 2282 ++c; 2283 } 2284 else 2285 { 2286 screen_char(off + c, r, c); 2287 if (utf_off2cells(off + c, max_off) > 1) 2288 ++c; 2289 } 2290 } 2291 } 2292 screen_char_attr = 0; 2293 } 2294 2295 /* 2296 * Redraw the characters for a vertically split window. 2297 */ 2298 static void 2299 redraw_block(int row, int end, win_T *wp) 2300 { 2301 int col; 2302 int width; 2303 2304 # ifdef FEAT_CLIPBOARD 2305 clip_may_clear_selection(row, end - 1); 2306 # endif 2307 2308 if (wp == NULL) 2309 { 2310 col = 0; 2311 width = Columns; 2312 } 2313 else 2314 { 2315 col = wp->w_wincol; 2316 width = wp->w_width; 2317 } 2318 screen_draw_rectangle(row, col, end - row, width, FALSE); 2319 } 2320 2321 void 2322 space_to_screenline(int off, int attr) 2323 { 2324 ScreenLines[off] = ' '; 2325 ScreenAttrs[off] = attr; 2326 if (enc_utf8) 2327 ScreenLinesUC[off] = 0; 2328 } 2329 2330 /* 2331 * Fill the screen from 'start_row' to 'end_row', from 'start_col' to 'end_col' 2332 * with character 'c1' in first column followed by 'c2' in the other columns. 2333 * Use attributes 'attr'. 2334 */ 2335 void 2336 screen_fill( 2337 int start_row, 2338 int end_row, 2339 int start_col, 2340 int end_col, 2341 int c1, 2342 int c2, 2343 int attr) 2344 { 2345 int row; 2346 int col; 2347 int off; 2348 int end_off; 2349 int did_delete; 2350 int c; 2351 int norm_term; 2352 #if defined(FEAT_GUI) || defined(UNIX) 2353 int force_next = FALSE; 2354 #endif 2355 2356 if (end_row > screen_Rows) // safety check 2357 end_row = screen_Rows; 2358 if (end_col > screen_Columns) // safety check 2359 end_col = screen_Columns; 2360 if (ScreenLines == NULL 2361 || start_row >= end_row 2362 || start_col >= end_col) // nothing to do 2363 return; 2364 2365 // it's a "normal" terminal when not in a GUI or cterm 2366 norm_term = ( 2367 #ifdef FEAT_GUI 2368 !gui.in_use && 2369 #endif 2370 !IS_CTERM); 2371 for (row = start_row; row < end_row; ++row) 2372 { 2373 if (has_mbyte 2374 #ifdef FEAT_GUI 2375 && !gui.in_use 2376 #endif 2377 ) 2378 { 2379 // When drawing over the right halve of a double-wide char clear 2380 // out the left halve. When drawing over the left halve of a 2381 // double wide-char clear out the right halve. Only needed in a 2382 // terminal. 2383 if (start_col > 0 && mb_fix_col(start_col, row) != start_col) 2384 screen_puts_len((char_u *)" ", 1, row, start_col - 1, 0); 2385 if (end_col < screen_Columns && mb_fix_col(end_col, row) != end_col) 2386 screen_puts_len((char_u *)" ", 1, row, end_col, 0); 2387 } 2388 /* 2389 * Try to use delete-line termcap code, when no attributes or in a 2390 * "normal" terminal, where a bold/italic space is just a 2391 * space. 2392 */ 2393 did_delete = FALSE; 2394 if (c2 == ' ' 2395 && end_col == Columns 2396 && can_clear(T_CE) 2397 && (attr == 0 2398 || (norm_term 2399 && attr <= HL_ALL 2400 && ((attr & ~(HL_BOLD | HL_ITALIC)) == 0)))) 2401 { 2402 /* 2403 * check if we really need to clear something 2404 */ 2405 col = start_col; 2406 if (c1 != ' ') // don't clear first char 2407 ++col; 2408 2409 off = LineOffset[row] + col; 2410 end_off = LineOffset[row] + end_col; 2411 2412 // skip blanks (used often, keep it fast!) 2413 if (enc_utf8) 2414 while (off < end_off && ScreenLines[off] == ' ' 2415 && ScreenAttrs[off] == 0 && ScreenLinesUC[off] == 0) 2416 ++off; 2417 else 2418 while (off < end_off && ScreenLines[off] == ' ' 2419 && ScreenAttrs[off] == 0) 2420 ++off; 2421 if (off < end_off) // something to be cleared 2422 { 2423 col = off - LineOffset[row]; 2424 screen_stop_highlight(); 2425 term_windgoto(row, col);// clear rest of this screen line 2426 out_str(T_CE); 2427 screen_start(); // don't know where cursor is now 2428 col = end_col - col; 2429 while (col--) // clear chars in ScreenLines 2430 { 2431 space_to_screenline(off, 0); 2432 ++off; 2433 } 2434 } 2435 did_delete = TRUE; // the chars are cleared now 2436 } 2437 2438 off = LineOffset[row] + start_col; 2439 c = c1; 2440 for (col = start_col; col < end_col; ++col) 2441 { 2442 if ((ScreenLines[off] != c 2443 || (enc_utf8 && (int)ScreenLinesUC[off] 2444 != (c >= 0x80 ? c : 0)) 2445 || ScreenAttrs[off] != attr 2446 #if defined(FEAT_GUI) || defined(UNIX) 2447 || force_next 2448 #endif 2449 ) 2450 #ifdef FEAT_PROP_POPUP 2451 // Skip if under a(nother) popup. 2452 && !blocked_by_popup(row, col) 2453 #endif 2454 ) 2455 { 2456 #if defined(FEAT_GUI) || defined(UNIX) 2457 // The bold trick may make a single row of pixels appear in 2458 // the next character. When a bold character is removed, the 2459 // next character should be redrawn too. This happens for our 2460 // own GUI and for some xterms. 2461 if ( 2462 # ifdef FEAT_GUI 2463 gui.in_use 2464 # endif 2465 # if defined(FEAT_GUI) && defined(UNIX) 2466 || 2467 # endif 2468 # ifdef UNIX 2469 term_is_xterm 2470 # endif 2471 ) 2472 { 2473 if (ScreenLines[off] != ' ' 2474 && (ScreenAttrs[off] > HL_ALL 2475 || ScreenAttrs[off] & HL_BOLD)) 2476 force_next = TRUE; 2477 else 2478 force_next = FALSE; 2479 } 2480 #endif 2481 ScreenLines[off] = c; 2482 if (enc_utf8) 2483 { 2484 if (c >= 0x80) 2485 { 2486 ScreenLinesUC[off] = c; 2487 ScreenLinesC[0][off] = 0; 2488 } 2489 else 2490 ScreenLinesUC[off] = 0; 2491 } 2492 ScreenAttrs[off] = attr; 2493 if (!did_delete || c != ' ') 2494 screen_char(off, row, col); 2495 } 2496 ++off; 2497 if (col == start_col) 2498 { 2499 if (did_delete) 2500 break; 2501 c = c2; 2502 } 2503 } 2504 if (end_col == Columns) 2505 LineWraps[row] = FALSE; 2506 if (row == Rows - 1) // overwritten the command line 2507 { 2508 redraw_cmdline = TRUE; 2509 if (start_col == 0 && end_col == Columns 2510 && c1 == ' ' && c2 == ' ' && attr == 0) 2511 clear_cmdline = FALSE; // command line has been cleared 2512 if (start_col == 0) 2513 mode_displayed = FALSE; // mode cleared or overwritten 2514 } 2515 } 2516 } 2517 2518 /* 2519 * Check if there should be a delay. Used before clearing or redrawing the 2520 * screen or the command line. 2521 */ 2522 void 2523 check_for_delay(int check_msg_scroll) 2524 { 2525 if ((emsg_on_display || (check_msg_scroll && msg_scroll)) 2526 && !did_wait_return 2527 && emsg_silent == 0 2528 && !in_assert_fails) 2529 { 2530 out_flush(); 2531 ui_delay(1006L, TRUE); 2532 emsg_on_display = FALSE; 2533 if (check_msg_scroll) 2534 msg_scroll = FALSE; 2535 } 2536 } 2537 2538 /* 2539 * Init TabPageIdxs[] to zero: Clicking outside of tabs has no effect. 2540 */ 2541 static void 2542 clear_TabPageIdxs(void) 2543 { 2544 int scol; 2545 2546 for (scol = 0; scol < Columns; ++scol) 2547 TabPageIdxs[scol] = 0; 2548 } 2549 2550 /* 2551 * screen_valid - allocate screen buffers if size changed 2552 * If "doclear" is TRUE: clear screen if it has been resized. 2553 * Returns TRUE if there is a valid screen to write to. 2554 * Returns FALSE when starting up and screen not initialized yet. 2555 */ 2556 int 2557 screen_valid(int doclear) 2558 { 2559 screenalloc(doclear); // allocate screen buffers if size changed 2560 return (ScreenLines != NULL); 2561 } 2562 2563 /* 2564 * Resize the shell to Rows and Columns. 2565 * Allocate ScreenLines[] and associated items. 2566 * 2567 * There may be some time between setting Rows and Columns and (re)allocating 2568 * ScreenLines[]. This happens when starting up and when (manually) changing 2569 * the shell size. Always use screen_Rows and screen_Columns to access items 2570 * in ScreenLines[]. Use Rows and Columns for positioning text etc. where the 2571 * final size of the shell is needed. 2572 */ 2573 void 2574 screenalloc(int doclear) 2575 { 2576 int new_row, old_row; 2577 #ifdef FEAT_GUI 2578 int old_Rows; 2579 #endif 2580 win_T *wp; 2581 int outofmem = FALSE; 2582 int len; 2583 schar_T *new_ScreenLines; 2584 u8char_T *new_ScreenLinesUC = NULL; 2585 u8char_T *new_ScreenLinesC[MAX_MCO]; 2586 schar_T *new_ScreenLines2 = NULL; 2587 int i; 2588 sattr_T *new_ScreenAttrs; 2589 unsigned *new_LineOffset; 2590 char_u *new_LineWraps; 2591 short *new_TabPageIdxs; 2592 #ifdef FEAT_PROP_POPUP 2593 short *new_popup_mask; 2594 short *new_popup_mask_next; 2595 char *new_popup_transparent; 2596 #endif 2597 tabpage_T *tp; 2598 static int entered = FALSE; // avoid recursiveness 2599 static int done_outofmem_msg = FALSE; // did outofmem message 2600 int retry_count = 0; 2601 2602 retry: 2603 /* 2604 * Allocation of the screen buffers is done only when the size changes and 2605 * when Rows and Columns have been set and we have started doing full 2606 * screen stuff. 2607 */ 2608 if ((ScreenLines != NULL 2609 && Rows == screen_Rows 2610 && Columns == screen_Columns 2611 && enc_utf8 == (ScreenLinesUC != NULL) 2612 && (enc_dbcs == DBCS_JPNU) == (ScreenLines2 != NULL) 2613 && p_mco == Screen_mco) 2614 || Rows == 0 2615 || Columns == 0 2616 || (!full_screen && ScreenLines == NULL)) 2617 return; 2618 2619 /* 2620 * It's possible that we produce an out-of-memory message below, which 2621 * will cause this function to be called again. To break the loop, just 2622 * return here. 2623 */ 2624 if (entered) 2625 return; 2626 entered = TRUE; 2627 2628 /* 2629 * Note that the window sizes are updated before reallocating the arrays, 2630 * thus we must not redraw here! 2631 */ 2632 ++RedrawingDisabled; 2633 2634 win_new_shellsize(); // fit the windows in the new sized shell 2635 2636 #ifdef FEAT_GUI_HAIKU 2637 vim_lock_screen(); // be safe, put it here 2638 #endif 2639 2640 comp_col(); // recompute columns for shown command and ruler 2641 2642 /* 2643 * We're changing the size of the screen. 2644 * - Allocate new arrays for ScreenLines and ScreenAttrs. 2645 * - Move lines from the old arrays into the new arrays, clear extra 2646 * lines (unless the screen is going to be cleared). 2647 * - Free the old arrays. 2648 * 2649 * If anything fails, make ScreenLines NULL, so we don't do anything! 2650 * Continuing with the old ScreenLines may result in a crash, because the 2651 * size is wrong. 2652 */ 2653 FOR_ALL_TAB_WINDOWS(tp, wp) 2654 win_free_lsize(wp); 2655 if (aucmd_win != NULL) 2656 win_free_lsize(aucmd_win); 2657 #ifdef FEAT_PROP_POPUP 2658 // global popup windows 2659 FOR_ALL_POPUPWINS(wp) 2660 win_free_lsize(wp); 2661 // tab-local popup windows 2662 FOR_ALL_TABPAGES(tp) 2663 FOR_ALL_POPUPWINS_IN_TAB(tp, wp) 2664 win_free_lsize(wp); 2665 #endif 2666 2667 new_ScreenLines = LALLOC_MULT(schar_T, (Rows + 1) * Columns); 2668 vim_memset(new_ScreenLinesC, 0, sizeof(u8char_T *) * MAX_MCO); 2669 if (enc_utf8) 2670 { 2671 new_ScreenLinesUC = LALLOC_MULT(u8char_T, (Rows + 1) * Columns); 2672 for (i = 0; i < p_mco; ++i) 2673 new_ScreenLinesC[i] = LALLOC_CLEAR_MULT(u8char_T, 2674 (Rows + 1) * Columns); 2675 } 2676 if (enc_dbcs == DBCS_JPNU) 2677 new_ScreenLines2 = LALLOC_MULT(schar_T, (Rows + 1) * Columns); 2678 new_ScreenAttrs = LALLOC_MULT(sattr_T, (Rows + 1) * Columns); 2679 new_LineOffset = LALLOC_MULT(unsigned, Rows); 2680 new_LineWraps = LALLOC_MULT(char_u, Rows); 2681 new_TabPageIdxs = LALLOC_MULT(short, Columns); 2682 #ifdef FEAT_PROP_POPUP 2683 new_popup_mask = LALLOC_MULT(short, Rows * Columns); 2684 new_popup_mask_next = LALLOC_MULT(short, Rows * Columns); 2685 new_popup_transparent = LALLOC_MULT(char, Rows * Columns); 2686 #endif 2687 2688 FOR_ALL_TAB_WINDOWS(tp, wp) 2689 { 2690 if (win_alloc_lines(wp) == FAIL) 2691 { 2692 outofmem = TRUE; 2693 goto give_up; 2694 } 2695 } 2696 if (aucmd_win != NULL && aucmd_win->w_lines == NULL 2697 && win_alloc_lines(aucmd_win) == FAIL) 2698 outofmem = TRUE; 2699 #ifdef FEAT_PROP_POPUP 2700 // global popup windows 2701 FOR_ALL_POPUPWINS(wp) 2702 if (win_alloc_lines(wp) == FAIL) 2703 { 2704 outofmem = TRUE; 2705 goto give_up; 2706 } 2707 // tab-local popup windows 2708 FOR_ALL_TABPAGES(tp) 2709 FOR_ALL_POPUPWINS_IN_TAB(tp, wp) 2710 if (win_alloc_lines(wp) == FAIL) 2711 { 2712 outofmem = TRUE; 2713 goto give_up; 2714 } 2715 #endif 2716 2717 give_up: 2718 2719 for (i = 0; i < p_mco; ++i) 2720 if (new_ScreenLinesC[i] == NULL) 2721 break; 2722 if (new_ScreenLines == NULL 2723 || (enc_utf8 && (new_ScreenLinesUC == NULL || i != p_mco)) 2724 || (enc_dbcs == DBCS_JPNU && new_ScreenLines2 == NULL) 2725 || new_ScreenAttrs == NULL 2726 || new_LineOffset == NULL 2727 || new_LineWraps == NULL 2728 || new_TabPageIdxs == NULL 2729 #ifdef FEAT_PROP_POPUP 2730 || new_popup_mask == NULL 2731 || new_popup_mask_next == NULL 2732 || new_popup_transparent == NULL 2733 #endif 2734 || outofmem) 2735 { 2736 if (ScreenLines != NULL || !done_outofmem_msg) 2737 { 2738 // guess the size 2739 do_outofmem_msg((long_u)((Rows + 1) * Columns)); 2740 2741 // Remember we did this to avoid getting outofmem messages over 2742 // and over again. 2743 done_outofmem_msg = TRUE; 2744 } 2745 VIM_CLEAR(new_ScreenLines); 2746 VIM_CLEAR(new_ScreenLinesUC); 2747 for (i = 0; i < p_mco; ++i) 2748 VIM_CLEAR(new_ScreenLinesC[i]); 2749 VIM_CLEAR(new_ScreenLines2); 2750 VIM_CLEAR(new_ScreenAttrs); 2751 VIM_CLEAR(new_LineOffset); 2752 VIM_CLEAR(new_LineWraps); 2753 VIM_CLEAR(new_TabPageIdxs); 2754 #ifdef FEAT_PROP_POPUP 2755 VIM_CLEAR(new_popup_mask); 2756 VIM_CLEAR(new_popup_mask_next); 2757 VIM_CLEAR(new_popup_transparent); 2758 #endif 2759 } 2760 else 2761 { 2762 done_outofmem_msg = FALSE; 2763 2764 for (new_row = 0; new_row < Rows; ++new_row) 2765 { 2766 new_LineOffset[new_row] = new_row * Columns; 2767 new_LineWraps[new_row] = FALSE; 2768 2769 /* 2770 * If the screen is not going to be cleared, copy as much as 2771 * possible from the old screen to the new one and clear the rest 2772 * (used when resizing the window at the "--more--" prompt or when 2773 * executing an external command, for the GUI). 2774 */ 2775 if (!doclear) 2776 { 2777 (void)vim_memset(new_ScreenLines + new_row * Columns, 2778 ' ', (size_t)Columns * sizeof(schar_T)); 2779 if (enc_utf8) 2780 { 2781 (void)vim_memset(new_ScreenLinesUC + new_row * Columns, 2782 0, (size_t)Columns * sizeof(u8char_T)); 2783 for (i = 0; i < p_mco; ++i) 2784 (void)vim_memset(new_ScreenLinesC[i] 2785 + new_row * Columns, 2786 0, (size_t)Columns * sizeof(u8char_T)); 2787 } 2788 if (enc_dbcs == DBCS_JPNU) 2789 (void)vim_memset(new_ScreenLines2 + new_row * Columns, 2790 0, (size_t)Columns * sizeof(schar_T)); 2791 (void)vim_memset(new_ScreenAttrs + new_row * Columns, 2792 0, (size_t)Columns * sizeof(sattr_T)); 2793 old_row = new_row + (screen_Rows - Rows); 2794 if (old_row >= 0 && ScreenLines != NULL) 2795 { 2796 if (screen_Columns < Columns) 2797 len = screen_Columns; 2798 else 2799 len = Columns; 2800 // When switching to utf-8 don't copy characters, they 2801 // may be invalid now. Also when p_mco changes. 2802 if (!(enc_utf8 && ScreenLinesUC == NULL) 2803 && p_mco == Screen_mco) 2804 mch_memmove(new_ScreenLines + new_LineOffset[new_row], 2805 ScreenLines + LineOffset[old_row], 2806 (size_t)len * sizeof(schar_T)); 2807 if (enc_utf8 && ScreenLinesUC != NULL 2808 && p_mco == Screen_mco) 2809 { 2810 mch_memmove(new_ScreenLinesUC + new_LineOffset[new_row], 2811 ScreenLinesUC + LineOffset[old_row], 2812 (size_t)len * sizeof(u8char_T)); 2813 for (i = 0; i < p_mco; ++i) 2814 mch_memmove(new_ScreenLinesC[i] 2815 + new_LineOffset[new_row], 2816 ScreenLinesC[i] + LineOffset[old_row], 2817 (size_t)len * sizeof(u8char_T)); 2818 } 2819 if (enc_dbcs == DBCS_JPNU && ScreenLines2 != NULL) 2820 mch_memmove(new_ScreenLines2 + new_LineOffset[new_row], 2821 ScreenLines2 + LineOffset[old_row], 2822 (size_t)len * sizeof(schar_T)); 2823 mch_memmove(new_ScreenAttrs + new_LineOffset[new_row], 2824 ScreenAttrs + LineOffset[old_row], 2825 (size_t)len * sizeof(sattr_T)); 2826 } 2827 } 2828 } 2829 // Use the last line of the screen for the current line. 2830 current_ScreenLine = new_ScreenLines + Rows * Columns; 2831 2832 #ifdef FEAT_PROP_POPUP 2833 vim_memset(new_popup_mask, 0, Rows * Columns * sizeof(short)); 2834 vim_memset(new_popup_transparent, 0, Rows * Columns * sizeof(char)); 2835 #endif 2836 } 2837 2838 free_screenlines(); 2839 2840 // NOTE: this may result in all pointers to become NULL. 2841 ScreenLines = new_ScreenLines; 2842 ScreenLinesUC = new_ScreenLinesUC; 2843 for (i = 0; i < p_mco; ++i) 2844 ScreenLinesC[i] = new_ScreenLinesC[i]; 2845 Screen_mco = p_mco; 2846 ScreenLines2 = new_ScreenLines2; 2847 ScreenAttrs = new_ScreenAttrs; 2848 LineOffset = new_LineOffset; 2849 LineWraps = new_LineWraps; 2850 TabPageIdxs = new_TabPageIdxs; 2851 #ifdef FEAT_PROP_POPUP 2852 popup_mask = new_popup_mask; 2853 popup_mask_next = new_popup_mask_next; 2854 popup_transparent = new_popup_transparent; 2855 popup_mask_refresh = TRUE; 2856 #endif 2857 2858 // It's important that screen_Rows and screen_Columns reflect the actual 2859 // size of ScreenLines[]. Set them before calling anything. 2860 #ifdef FEAT_GUI 2861 old_Rows = screen_Rows; 2862 #endif 2863 screen_Rows = Rows; 2864 screen_Columns = Columns; 2865 2866 must_redraw = CLEAR; // need to clear the screen later 2867 if (doclear) 2868 screenclear2(); 2869 #ifdef FEAT_GUI 2870 else if (gui.in_use 2871 && !gui.starting 2872 && ScreenLines != NULL 2873 && old_Rows != Rows) 2874 { 2875 gui_redraw_block(0, 0, (int)Rows - 1, (int)Columns - 1, 0); 2876 2877 // Adjust the position of the cursor, for when executing an external 2878 // command. 2879 if (msg_row >= Rows) // Rows got smaller 2880 msg_row = Rows - 1; // put cursor at last row 2881 else if (Rows > old_Rows) // Rows got bigger 2882 msg_row += Rows - old_Rows; // put cursor in same place 2883 if (msg_col >= Columns) // Columns got smaller 2884 msg_col = Columns - 1; // put cursor at last column 2885 } 2886 #endif 2887 clear_TabPageIdxs(); 2888 2889 #ifdef FEAT_GUI_HAIKU 2890 vim_unlock_screen(); 2891 #endif 2892 2893 entered = FALSE; 2894 --RedrawingDisabled; 2895 2896 /* 2897 * Do not apply autocommands more than 3 times to avoid an endless loop 2898 * in case applying autocommands always changes Rows or Columns. 2899 */ 2900 if (starting == 0 && ++retry_count <= 3) 2901 { 2902 apply_autocmds(EVENT_VIMRESIZED, NULL, NULL, FALSE, curbuf); 2903 // In rare cases, autocommands may have altered Rows or Columns, 2904 // jump back to check if we need to allocate the screen again. 2905 goto retry; 2906 } 2907 } 2908 2909 void 2910 free_screenlines(void) 2911 { 2912 int i; 2913 2914 VIM_CLEAR(ScreenLinesUC); 2915 for (i = 0; i < Screen_mco; ++i) 2916 VIM_CLEAR(ScreenLinesC[i]); 2917 VIM_CLEAR(ScreenLines2); 2918 VIM_CLEAR(ScreenLines); 2919 VIM_CLEAR(ScreenAttrs); 2920 VIM_CLEAR(LineOffset); 2921 VIM_CLEAR(LineWraps); 2922 VIM_CLEAR(TabPageIdxs); 2923 #ifdef FEAT_PROP_POPUP 2924 VIM_CLEAR(popup_mask); 2925 VIM_CLEAR(popup_mask_next); 2926 VIM_CLEAR(popup_transparent); 2927 #endif 2928 } 2929 2930 void 2931 screenclear(void) 2932 { 2933 check_for_delay(FALSE); 2934 screenalloc(FALSE); // allocate screen buffers if size changed 2935 screenclear2(); // clear the screen 2936 } 2937 2938 static void 2939 screenclear2(void) 2940 { 2941 int i; 2942 2943 if (starting == NO_SCREEN || ScreenLines == NULL 2944 #ifdef FEAT_GUI 2945 || (gui.in_use && gui.starting) 2946 #endif 2947 ) 2948 return; 2949 2950 #ifdef FEAT_GUI 2951 if (!gui.in_use) 2952 #endif 2953 screen_attr = -1; // force setting the Normal colors 2954 screen_stop_highlight(); // don't want highlighting here 2955 2956 #ifdef FEAT_CLIPBOARD 2957 // disable selection without redrawing it 2958 clip_scroll_selection(9999); 2959 #endif 2960 2961 // blank out ScreenLines 2962 for (i = 0; i < Rows; ++i) 2963 { 2964 lineclear(LineOffset[i], (int)Columns, 0); 2965 LineWraps[i] = FALSE; 2966 } 2967 2968 if (can_clear(T_CL)) 2969 { 2970 out_str(T_CL); // clear the display 2971 clear_cmdline = FALSE; 2972 mode_displayed = FALSE; 2973 } 2974 else 2975 { 2976 // can't clear the screen, mark all chars with invalid attributes 2977 for (i = 0; i < Rows; ++i) 2978 lineinvalid(LineOffset[i], (int)Columns); 2979 clear_cmdline = TRUE; 2980 } 2981 2982 screen_cleared = TRUE; // can use contents of ScreenLines now 2983 2984 win_rest_invalid(firstwin); 2985 redraw_cmdline = TRUE; 2986 redraw_tabline = TRUE; 2987 if (must_redraw == CLEAR) // no need to clear again 2988 must_redraw = NOT_VALID; 2989 compute_cmdrow(); 2990 msg_row = cmdline_row; // put cursor on last line for messages 2991 msg_col = 0; 2992 screen_start(); // don't know where cursor is now 2993 msg_scrolled = 0; // can't scroll back 2994 msg_didany = FALSE; 2995 msg_didout = FALSE; 2996 } 2997 2998 /* 2999 * Clear one line in ScreenLines. 3000 */ 3001 static void 3002 lineclear(unsigned off, int width, int attr) 3003 { 3004 (void)vim_memset(ScreenLines + off, ' ', (size_t)width * sizeof(schar_T)); 3005 if (enc_utf8) 3006 (void)vim_memset(ScreenLinesUC + off, 0, 3007 (size_t)width * sizeof(u8char_T)); 3008 (void)vim_memset(ScreenAttrs + off, attr, (size_t)width * sizeof(sattr_T)); 3009 } 3010 3011 /* 3012 * Mark one line in ScreenLines invalid by setting the attributes to an 3013 * invalid value. 3014 */ 3015 static void 3016 lineinvalid(unsigned off, int width) 3017 { 3018 (void)vim_memset(ScreenAttrs + off, -1, (size_t)width * sizeof(sattr_T)); 3019 } 3020 3021 /* 3022 * To be called when characters were sent to the terminal directly, outputting 3023 * test on "screen_lnum". 3024 */ 3025 void 3026 line_was_clobbered(int screen_lnum) 3027 { 3028 lineinvalid(LineOffset[screen_lnum], (int)Columns); 3029 } 3030 3031 /* 3032 * Copy part of a Screenline for vertically split window "wp". 3033 */ 3034 static void 3035 linecopy(int to, int from, win_T *wp) 3036 { 3037 unsigned off_to = LineOffset[to] + wp->w_wincol; 3038 unsigned off_from = LineOffset[from] + wp->w_wincol; 3039 3040 mch_memmove(ScreenLines + off_to, ScreenLines + off_from, 3041 wp->w_width * sizeof(schar_T)); 3042 if (enc_utf8) 3043 { 3044 int i; 3045 3046 mch_memmove(ScreenLinesUC + off_to, ScreenLinesUC + off_from, 3047 wp->w_width * sizeof(u8char_T)); 3048 for (i = 0; i < p_mco; ++i) 3049 mch_memmove(ScreenLinesC[i] + off_to, ScreenLinesC[i] + off_from, 3050 wp->w_width * sizeof(u8char_T)); 3051 } 3052 if (enc_dbcs == DBCS_JPNU) 3053 mch_memmove(ScreenLines2 + off_to, ScreenLines2 + off_from, 3054 wp->w_width * sizeof(schar_T)); 3055 mch_memmove(ScreenAttrs + off_to, ScreenAttrs + off_from, 3056 wp->w_width * sizeof(sattr_T)); 3057 } 3058 3059 /* 3060 * Return TRUE if clearing with term string "p" would work. 3061 * It can't work when the string is empty or it won't set the right background. 3062 * Don't clear to end-of-line when there are popups, it may cause flicker. 3063 */ 3064 int 3065 can_clear(char_u *p) 3066 { 3067 return (*p != NUL && (t_colors <= 1 3068 #ifdef FEAT_GUI 3069 || gui.in_use 3070 #endif 3071 #ifdef FEAT_TERMGUICOLORS 3072 || (p_tgc && cterm_normal_bg_gui_color == INVALCOLOR) 3073 || (!p_tgc && cterm_normal_bg_color == 0) 3074 #else 3075 || cterm_normal_bg_color == 0 3076 #endif 3077 || *T_UT != NUL) 3078 #ifdef FEAT_PROP_POPUP 3079 && !(p == T_CE && popup_visible) 3080 #endif 3081 ); 3082 } 3083 3084 /* 3085 * Reset cursor position. Use whenever cursor was moved because of outputting 3086 * something directly to the screen (shell commands) or a terminal control 3087 * code. 3088 */ 3089 void 3090 screen_start(void) 3091 { 3092 screen_cur_row = screen_cur_col = 9999; 3093 } 3094 3095 /* 3096 * Move the cursor to position "row","col" in the screen. 3097 * This tries to find the most efficient way to move, minimizing the number of 3098 * characters sent to the terminal. 3099 */ 3100 void 3101 windgoto(int row, int col) 3102 { 3103 sattr_T *p; 3104 int i; 3105 int plan; 3106 int cost; 3107 int wouldbe_col; 3108 int noinvcurs; 3109 char_u *bs; 3110 int goto_cost; 3111 int attr; 3112 3113 #define GOTO_COST 7 // assume a term_windgoto() takes about 7 chars 3114 #define HIGHL_COST 5 // assume unhighlight takes 5 chars 3115 3116 #define PLAN_LE 1 3117 #define PLAN_CR 2 3118 #define PLAN_NL 3 3119 #define PLAN_WRITE 4 3120 // Can't use ScreenLines unless initialized 3121 if (ScreenLines == NULL) 3122 return; 3123 if (col != screen_cur_col || row != screen_cur_row) 3124 { 3125 // Check for valid position. 3126 if (row < 0) // window without text lines? 3127 row = 0; 3128 if (row >= screen_Rows) 3129 row = screen_Rows - 1; 3130 if (col >= screen_Columns) 3131 col = screen_Columns - 1; 3132 3133 // check if no cursor movement is allowed in highlight mode 3134 if (screen_attr && *T_MS == NUL) 3135 noinvcurs = HIGHL_COST; 3136 else 3137 noinvcurs = 0; 3138 goto_cost = GOTO_COST + noinvcurs; 3139 3140 /* 3141 * Plan how to do the positioning: 3142 * 1. Use CR to move it to column 0, same row. 3143 * 2. Use T_LE to move it a few columns to the left. 3144 * 3. Use NL to move a few lines down, column 0. 3145 * 4. Move a few columns to the right with T_ND or by writing chars. 3146 * 3147 * Don't do this if the cursor went beyond the last column, the cursor 3148 * position is unknown then (some terminals wrap, some don't ) 3149 * 3150 * First check if the highlighting attributes allow us to write 3151 * characters to move the cursor to the right. 3152 */ 3153 if (row >= screen_cur_row && screen_cur_col < Columns) 3154 { 3155 /* 3156 * If the cursor is in the same row, bigger col, we can use CR 3157 * or T_LE. 3158 */ 3159 bs = NULL; // init for GCC 3160 attr = screen_attr; 3161 if (row == screen_cur_row && col < screen_cur_col) 3162 { 3163 // "le" is preferred over "bc", because "bc" is obsolete 3164 if (*T_LE) 3165 bs = T_LE; // "cursor left" 3166 else 3167 bs = T_BC; // "backspace character (old) 3168 if (*bs) 3169 cost = (screen_cur_col - col) * (int)STRLEN(bs); 3170 else 3171 cost = 999; 3172 if (col + 1 < cost) // using CR is less characters 3173 { 3174 plan = PLAN_CR; 3175 wouldbe_col = 0; 3176 cost = 1; // CR is just one character 3177 } 3178 else 3179 { 3180 plan = PLAN_LE; 3181 wouldbe_col = col; 3182 } 3183 if (noinvcurs) // will stop highlighting 3184 { 3185 cost += noinvcurs; 3186 attr = 0; 3187 } 3188 } 3189 3190 /* 3191 * If the cursor is above where we want to be, we can use CR LF. 3192 */ 3193 else if (row > screen_cur_row) 3194 { 3195 plan = PLAN_NL; 3196 wouldbe_col = 0; 3197 cost = (row - screen_cur_row) * 2; // CR LF 3198 if (noinvcurs) // will stop highlighting 3199 { 3200 cost += noinvcurs; 3201 attr = 0; 3202 } 3203 } 3204 3205 /* 3206 * If the cursor is in the same row, smaller col, just use write. 3207 */ 3208 else 3209 { 3210 plan = PLAN_WRITE; 3211 wouldbe_col = screen_cur_col; 3212 cost = 0; 3213 } 3214 3215 /* 3216 * Check if any characters that need to be written have the 3217 * correct attributes. Also avoid UTF-8 characters. 3218 */ 3219 i = col - wouldbe_col; 3220 if (i > 0) 3221 cost += i; 3222 if (cost < goto_cost && i > 0) 3223 { 3224 /* 3225 * Check if the attributes are correct without additionally 3226 * stopping highlighting. 3227 */ 3228 p = ScreenAttrs + LineOffset[row] + wouldbe_col; 3229 while (i && *p++ == attr) 3230 --i; 3231 if (i != 0) 3232 { 3233 /* 3234 * Try if it works when highlighting is stopped here. 3235 */ 3236 if (*--p == 0) 3237 { 3238 cost += noinvcurs; 3239 while (i && *p++ == 0) 3240 --i; 3241 } 3242 if (i != 0) 3243 cost = 999; // different attributes, don't do it 3244 } 3245 if (enc_utf8) 3246 { 3247 // Don't use an UTF-8 char for positioning, it's slow. 3248 for (i = wouldbe_col; i < col; ++i) 3249 if (ScreenLinesUC[LineOffset[row] + i] != 0) 3250 { 3251 cost = 999; 3252 break; 3253 } 3254 } 3255 } 3256 3257 /* 3258 * We can do it without term_windgoto()! 3259 */ 3260 if (cost < goto_cost) 3261 { 3262 if (plan == PLAN_LE) 3263 { 3264 if (noinvcurs) 3265 screen_stop_highlight(); 3266 while (screen_cur_col > col) 3267 { 3268 out_str(bs); 3269 --screen_cur_col; 3270 } 3271 } 3272 else if (plan == PLAN_CR) 3273 { 3274 if (noinvcurs) 3275 screen_stop_highlight(); 3276 out_char('\r'); 3277 screen_cur_col = 0; 3278 } 3279 else if (plan == PLAN_NL) 3280 { 3281 if (noinvcurs) 3282 screen_stop_highlight(); 3283 while (screen_cur_row < row) 3284 { 3285 out_char('\n'); 3286 ++screen_cur_row; 3287 } 3288 screen_cur_col = 0; 3289 } 3290 3291 i = col - screen_cur_col; 3292 if (i > 0) 3293 { 3294 /* 3295 * Use cursor-right if it's one character only. Avoids 3296 * removing a line of pixels from the last bold char, when 3297 * using the bold trick in the GUI. 3298 */ 3299 if (T_ND[0] != NUL && T_ND[1] == NUL) 3300 { 3301 while (i-- > 0) 3302 out_char(*T_ND); 3303 } 3304 else 3305 { 3306 int off; 3307 3308 off = LineOffset[row] + screen_cur_col; 3309 while (i-- > 0) 3310 { 3311 if (ScreenAttrs[off] != screen_attr) 3312 screen_stop_highlight(); 3313 out_flush_check(); 3314 out_char(ScreenLines[off]); 3315 if (enc_dbcs == DBCS_JPNU 3316 && ScreenLines[off] == 0x8e) 3317 out_char(ScreenLines2[off]); 3318 ++off; 3319 } 3320 } 3321 } 3322 } 3323 } 3324 else 3325 cost = 999; 3326 3327 if (cost >= goto_cost) 3328 { 3329 if (noinvcurs) 3330 screen_stop_highlight(); 3331 if (row == screen_cur_row && (col > screen_cur_col) 3332 && *T_CRI != NUL) 3333 term_cursor_right(col - screen_cur_col); 3334 else 3335 term_windgoto(row, col); 3336 } 3337 screen_cur_row = row; 3338 screen_cur_col = col; 3339 } 3340 } 3341 3342 /* 3343 * Set cursor to its position in the current window. 3344 */ 3345 void 3346 setcursor(void) 3347 { 3348 setcursor_mayforce(FALSE); 3349 } 3350 3351 /* 3352 * Set cursor to its position in the current window. 3353 * When "force" is TRUE also when not redrawing. 3354 */ 3355 void 3356 setcursor_mayforce(int force) 3357 { 3358 if (force || redrawing()) 3359 { 3360 validate_cursor(); 3361 windgoto(W_WINROW(curwin) + curwin->w_wrow, 3362 curwin->w_wincol + ( 3363 #ifdef FEAT_RIGHTLEFT 3364 // With 'rightleft' set and the cursor on a double-wide 3365 // character, position it on the leftmost column. 3366 curwin->w_p_rl ? ((int)curwin->w_width - curwin->w_wcol 3367 - ((has_mbyte 3368 && (*mb_ptr2cells)(ml_get_cursor()) == 2 3369 && vim_isprintc(gchar_cursor())) ? 2 : 1)) : 3370 #endif 3371 curwin->w_wcol)); 3372 } 3373 } 3374 3375 3376 /* 3377 * Insert 'line_count' lines at 'row' in window 'wp'. 3378 * If 'invalid' is TRUE the wp->w_lines[].wl_lnum is invalidated. 3379 * If 'mayclear' is TRUE the screen will be cleared if it is faster than 3380 * scrolling. 3381 * Returns FAIL if the lines are not inserted, OK for success. 3382 */ 3383 int 3384 win_ins_lines( 3385 win_T *wp, 3386 int row, 3387 int line_count, 3388 int invalid, 3389 int mayclear) 3390 { 3391 int did_delete; 3392 int nextrow; 3393 int lastrow; 3394 int retval; 3395 3396 if (invalid) 3397 wp->w_lines_valid = 0; 3398 3399 if (wp->w_height < 5) 3400 return FAIL; 3401 3402 if (line_count > wp->w_height - row) 3403 line_count = wp->w_height - row; 3404 3405 retval = win_do_lines(wp, row, line_count, mayclear, FALSE, 0); 3406 if (retval != MAYBE) 3407 return retval; 3408 3409 /* 3410 * If there is a next window or a status line, we first try to delete the 3411 * lines at the bottom to avoid messing what is after the window. 3412 * If this fails and there are following windows, don't do anything to 3413 * avoid messing up those windows, better just redraw. 3414 */ 3415 did_delete = FALSE; 3416 if (wp->w_next != NULL || wp->w_status_height) 3417 { 3418 if (screen_del_lines(0, W_WINROW(wp) + wp->w_height - line_count, 3419 line_count, (int)Rows, FALSE, 0, NULL) == OK) 3420 did_delete = TRUE; 3421 else if (wp->w_next) 3422 return FAIL; 3423 } 3424 /* 3425 * if no lines deleted, blank the lines that will end up below the window 3426 */ 3427 if (!did_delete) 3428 { 3429 wp->w_redr_status = TRUE; 3430 redraw_cmdline = TRUE; 3431 nextrow = W_WINROW(wp) + wp->w_height + wp->w_status_height; 3432 lastrow = nextrow + line_count; 3433 if (lastrow > Rows) 3434 lastrow = Rows; 3435 screen_fill(nextrow - line_count, lastrow - line_count, 3436 wp->w_wincol, (int)W_ENDCOL(wp), 3437 ' ', ' ', 0); 3438 } 3439 3440 if (screen_ins_lines(0, W_WINROW(wp) + row, line_count, (int)Rows, 0, NULL) 3441 == FAIL) 3442 { 3443 // deletion will have messed up other windows 3444 if (did_delete) 3445 { 3446 wp->w_redr_status = TRUE; 3447 win_rest_invalid(W_NEXT(wp)); 3448 } 3449 return FAIL; 3450 } 3451 3452 return OK; 3453 } 3454 3455 /* 3456 * Delete "line_count" window lines at "row" in window "wp". 3457 * If "invalid" is TRUE curwin->w_lines[] is invalidated. 3458 * If "mayclear" is TRUE the screen will be cleared if it is faster than 3459 * scrolling 3460 * Return OK for success, FAIL if the lines are not deleted. 3461 */ 3462 int 3463 win_del_lines( 3464 win_T *wp, 3465 int row, 3466 int line_count, 3467 int invalid, 3468 int mayclear, 3469 int clear_attr) // for clearing lines 3470 { 3471 int retval; 3472 3473 if (invalid) 3474 wp->w_lines_valid = 0; 3475 3476 if (line_count > wp->w_height - row) 3477 line_count = wp->w_height - row; 3478 3479 retval = win_do_lines(wp, row, line_count, mayclear, TRUE, clear_attr); 3480 if (retval != MAYBE) 3481 return retval; 3482 3483 if (screen_del_lines(0, W_WINROW(wp) + row, line_count, 3484 (int)Rows, FALSE, clear_attr, NULL) == FAIL) 3485 return FAIL; 3486 3487 /* 3488 * If there are windows or status lines below, try to put them at the 3489 * correct place. If we can't do that, they have to be redrawn. 3490 */ 3491 if (wp->w_next || wp->w_status_height || cmdline_row < Rows - 1) 3492 { 3493 if (screen_ins_lines(0, W_WINROW(wp) + wp->w_height - line_count, 3494 line_count, (int)Rows, clear_attr, NULL) == FAIL) 3495 { 3496 wp->w_redr_status = TRUE; 3497 win_rest_invalid(wp->w_next); 3498 } 3499 } 3500 /* 3501 * If this is the last window and there is no status line, redraw the 3502 * command line later. 3503 */ 3504 else 3505 redraw_cmdline = TRUE; 3506 return OK; 3507 } 3508 3509 /* 3510 * Common code for win_ins_lines() and win_del_lines(). 3511 * Returns OK or FAIL when the work has been done. 3512 * Returns MAYBE when not finished yet. 3513 */ 3514 static int 3515 win_do_lines( 3516 win_T *wp, 3517 int row, 3518 int line_count, 3519 int mayclear, 3520 int del, 3521 int clear_attr) 3522 { 3523 int retval; 3524 3525 if (!redrawing() || line_count <= 0) 3526 return FAIL; 3527 3528 // When inserting lines would result in loss of command output, just redraw 3529 // the lines. 3530 if (no_win_do_lines_ins && !del) 3531 return FAIL; 3532 3533 // only a few lines left: redraw is faster 3534 if (mayclear && Rows - line_count < 5 && wp->w_width == Columns) 3535 { 3536 if (!no_win_do_lines_ins) 3537 screenclear(); // will set wp->w_lines_valid to 0 3538 return FAIL; 3539 } 3540 3541 #ifdef FEAT_PROP_POPUP 3542 // this doesn't work when there are popups visible 3543 if (popup_visible) 3544 return FAIL; 3545 #endif 3546 3547 // Delete all remaining lines 3548 if (row + line_count >= wp->w_height) 3549 { 3550 screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + wp->w_height, 3551 wp->w_wincol, (int)W_ENDCOL(wp), 3552 ' ', ' ', 0); 3553 return OK; 3554 } 3555 3556 /* 3557 * When scrolling, the message on the command line should be cleared, 3558 * otherwise it will stay there forever. 3559 * Don't do this when avoiding to insert lines. 3560 */ 3561 if (!no_win_do_lines_ins) 3562 clear_cmdline = TRUE; 3563 3564 /* 3565 * If the terminal can set a scroll region, use that. 3566 * Always do this in a vertically split window. This will redraw from 3567 * ScreenLines[] when t_CV isn't defined. That's faster than using 3568 * win_line(). 3569 * Don't use a scroll region when we are going to redraw the text, writing 3570 * a character in the lower right corner of the scroll region may cause a 3571 * scroll-up . 3572 */ 3573 if (scroll_region || wp->w_width != Columns) 3574 { 3575 if (scroll_region && (wp->w_width == Columns || *T_CSV != NUL)) 3576 scroll_region_set(wp, row); 3577 if (del) 3578 retval = screen_del_lines(W_WINROW(wp) + row, 0, line_count, 3579 wp->w_height - row, FALSE, clear_attr, wp); 3580 else 3581 retval = screen_ins_lines(W_WINROW(wp) + row, 0, line_count, 3582 wp->w_height - row, clear_attr, wp); 3583 if (scroll_region && (wp->w_width == Columns || *T_CSV != NUL)) 3584 scroll_region_reset(); 3585 return retval; 3586 } 3587 3588 if (wp->w_next != NULL && p_tf) // don't delete/insert on fast terminal 3589 return FAIL; 3590 3591 return MAYBE; 3592 } 3593 3594 /* 3595 * window 'wp' and everything after it is messed up, mark it for redraw 3596 */ 3597 static void 3598 win_rest_invalid(win_T *wp) 3599 { 3600 while (wp != NULL) 3601 { 3602 redraw_win_later(wp, NOT_VALID); 3603 wp->w_redr_status = TRUE; 3604 wp = wp->w_next; 3605 } 3606 redraw_cmdline = TRUE; 3607 } 3608 3609 /* 3610 * The rest of the routines in this file perform screen manipulations. The 3611 * given operation is performed physically on the screen. The corresponding 3612 * change is also made to the internal screen image. In this way, the editor 3613 * anticipates the effect of editing changes on the appearance of the screen. 3614 * That way, when we call screenupdate a complete redraw isn't usually 3615 * necessary. Another advantage is that we can keep adding code to anticipate 3616 * screen changes, and in the meantime, everything still works. 3617 */ 3618 3619 /* 3620 * types for inserting or deleting lines 3621 */ 3622 #define USE_T_CAL 1 3623 #define USE_T_CDL 2 3624 #define USE_T_AL 3 3625 #define USE_T_CE 4 3626 #define USE_T_DL 5 3627 #define USE_T_SR 6 3628 #define USE_NL 7 3629 #define USE_T_CD 8 3630 #define USE_REDRAW 9 3631 3632 /* 3633 * insert lines on the screen and update ScreenLines[] 3634 * 'end' is the line after the scrolled part. Normally it is Rows. 3635 * When scrolling region used 'off' is the offset from the top for the region. 3636 * 'row' and 'end' are relative to the start of the region. 3637 * 3638 * return FAIL for failure, OK for success. 3639 */ 3640 int 3641 screen_ins_lines( 3642 int off, 3643 int row, 3644 int line_count, 3645 int end, 3646 int clear_attr, 3647 win_T *wp) // NULL or window to use width from 3648 { 3649 int i; 3650 int j; 3651 unsigned temp; 3652 int cursor_row; 3653 int cursor_col = 0; 3654 int type; 3655 int result_empty; 3656 int can_ce = can_clear(T_CE); 3657 3658 /* 3659 * FAIL if 3660 * - there is no valid screen 3661 * - the screen has to be redrawn completely 3662 * - the line count is less than one 3663 * - the line count is more than 'ttyscroll' 3664 * - redrawing for a callback and there is a modeless selection 3665 * - there is a popup window 3666 */ 3667 if (!screen_valid(TRUE) 3668 || line_count <= 0 || line_count > p_ttyscroll 3669 #ifdef FEAT_CLIPBOARD 3670 || (clip_star.state != SELECT_CLEARED 3671 && redrawing_for_callback > 0) 3672 #endif 3673 #ifdef FEAT_PROP_POPUP 3674 || popup_visible 3675 #endif 3676 ) 3677 return FAIL; 3678 3679 /* 3680 * There are seven ways to insert lines: 3681 * 0. When in a vertically split window and t_CV isn't set, redraw the 3682 * characters from ScreenLines[]. 3683 * 1. Use T_CD (clear to end of display) if it exists and the result of 3684 * the insert is just empty lines 3685 * 2. Use T_CAL (insert multiple lines) if it exists and T_AL is not 3686 * present or line_count > 1. It looks better if we do all the inserts 3687 * at once. 3688 * 3. Use T_CDL (delete multiple lines) if it exists and the result of the 3689 * insert is just empty lines and T_CE is not present or line_count > 3690 * 1. 3691 * 4. Use T_AL (insert line) if it exists. 3692 * 5. Use T_CE (erase line) if it exists and the result of the insert is 3693 * just empty lines. 3694 * 6. Use T_DL (delete line) if it exists and the result of the insert is 3695 * just empty lines. 3696 * 7. Use T_SR (scroll reverse) if it exists and inserting at row 0 and 3697 * the 'da' flag is not set or we have clear line capability. 3698 * 8. redraw the characters from ScreenLines[]. 3699 * 3700 * Careful: In a hpterm scroll reverse doesn't work as expected, it moves 3701 * the scrollbar for the window. It does have insert line, use that if it 3702 * exists. 3703 */ 3704 result_empty = (row + line_count >= end); 3705 if (wp != NULL && wp->w_width != Columns && *T_CSV == NUL) 3706 type = USE_REDRAW; 3707 else if (can_clear(T_CD) && result_empty) 3708 type = USE_T_CD; 3709 else if (*T_CAL != NUL && (line_count > 1 || *T_AL == NUL)) 3710 type = USE_T_CAL; 3711 else if (*T_CDL != NUL && result_empty && (line_count > 1 || !can_ce)) 3712 type = USE_T_CDL; 3713 else if (*T_AL != NUL) 3714 type = USE_T_AL; 3715 else if (can_ce && result_empty) 3716 type = USE_T_CE; 3717 else if (*T_DL != NUL && result_empty) 3718 type = USE_T_DL; 3719 else if (*T_SR != NUL && row == 0 && (*T_DA == NUL || can_ce)) 3720 type = USE_T_SR; 3721 else 3722 return FAIL; 3723 3724 /* 3725 * For clearing the lines screen_del_lines() is used. This will also take 3726 * care of t_db if necessary. 3727 */ 3728 if (type == USE_T_CD || type == USE_T_CDL || 3729 type == USE_T_CE || type == USE_T_DL) 3730 return screen_del_lines(off, row, line_count, end, FALSE, 0, wp); 3731 3732 /* 3733 * If text is retained below the screen, first clear or delete as many 3734 * lines at the bottom of the window as are about to be inserted so that 3735 * the deleted lines won't later surface during a screen_del_lines. 3736 */ 3737 if (*T_DB) 3738 screen_del_lines(off, end - line_count, line_count, end, FALSE, 0, wp); 3739 3740 #ifdef FEAT_CLIPBOARD 3741 // Remove a modeless selection when inserting lines halfway the screen 3742 // or not the full width of the screen. 3743 if (off + row > 0 || (wp != NULL && wp->w_width != Columns)) 3744 clip_clear_selection(&clip_star); 3745 else 3746 clip_scroll_selection(-line_count); 3747 #endif 3748 3749 #ifdef FEAT_GUI_HAIKU 3750 vim_lock_screen(); 3751 #endif 3752 3753 #ifdef FEAT_GUI 3754 // Don't update the GUI cursor here, ScreenLines[] is invalid until the 3755 // scrolling is actually carried out. 3756 gui_dont_update_cursor(row + off <= gui.cursor_row); 3757 #endif 3758 3759 if (wp != NULL && wp->w_wincol != 0 && *T_CSV != NUL && *T_CCS == NUL) 3760 cursor_col = wp->w_wincol; 3761 3762 if (*T_CCS != NUL) // cursor relative to region 3763 cursor_row = row; 3764 else 3765 cursor_row = row + off; 3766 3767 /* 3768 * Shift LineOffset[] line_count down to reflect the inserted lines. 3769 * Clear the inserted lines in ScreenLines[]. 3770 */ 3771 row += off; 3772 end += off; 3773 for (i = 0; i < line_count; ++i) 3774 { 3775 if (wp != NULL && wp->w_width != Columns) 3776 { 3777 // need to copy part of a line 3778 j = end - 1 - i; 3779 while ((j -= line_count) >= row) 3780 linecopy(j + line_count, j, wp); 3781 j += line_count; 3782 if (can_clear((char_u *)" ")) 3783 lineclear(LineOffset[j] + wp->w_wincol, wp->w_width, 3784 clear_attr); 3785 else 3786 lineinvalid(LineOffset[j] + wp->w_wincol, wp->w_width); 3787 LineWraps[j] = FALSE; 3788 } 3789 else 3790 { 3791 j = end - 1 - i; 3792 temp = LineOffset[j]; 3793 while ((j -= line_count) >= row) 3794 { 3795 LineOffset[j + line_count] = LineOffset[j]; 3796 LineWraps[j + line_count] = LineWraps[j]; 3797 } 3798 LineOffset[j + line_count] = temp; 3799 LineWraps[j + line_count] = FALSE; 3800 if (can_clear((char_u *)" ")) 3801 lineclear(temp, (int)Columns, clear_attr); 3802 else 3803 lineinvalid(temp, (int)Columns); 3804 } 3805 } 3806 3807 #ifdef FEAT_GUI_HAIKU 3808 vim_unlock_screen(); 3809 #endif 3810 3811 screen_stop_highlight(); 3812 windgoto(cursor_row, cursor_col); 3813 if (clear_attr != 0) 3814 screen_start_highlight(clear_attr); 3815 3816 // redraw the characters 3817 if (type == USE_REDRAW) 3818 redraw_block(row, end, wp); 3819 else if (type == USE_T_CAL) 3820 { 3821 term_append_lines(line_count); 3822 screen_start(); // don't know where cursor is now 3823 } 3824 else 3825 { 3826 for (i = 0; i < line_count; i++) 3827 { 3828 if (type == USE_T_AL) 3829 { 3830 if (i && cursor_row != 0) 3831 windgoto(cursor_row, cursor_col); 3832 out_str(T_AL); 3833 } 3834 else // type == USE_T_SR 3835 out_str(T_SR); 3836 screen_start(); // don't know where cursor is now 3837 } 3838 } 3839 3840 /* 3841 * With scroll-reverse and 'da' flag set we need to clear the lines that 3842 * have been scrolled down into the region. 3843 */ 3844 if (type == USE_T_SR && *T_DA) 3845 { 3846 for (i = 0; i < line_count; ++i) 3847 { 3848 windgoto(off + i, cursor_col); 3849 out_str(T_CE); 3850 screen_start(); // don't know where cursor is now 3851 } 3852 } 3853 3854 #ifdef FEAT_GUI 3855 gui_can_update_cursor(); 3856 if (gui.in_use) 3857 out_flush(); // always flush after a scroll 3858 #endif 3859 return OK; 3860 } 3861 3862 /* 3863 * Delete lines on the screen and update ScreenLines[]. 3864 * "end" is the line after the scrolled part. Normally it is Rows. 3865 * When scrolling region used "off" is the offset from the top for the region. 3866 * "row" and "end" are relative to the start of the region. 3867 * 3868 * Return OK for success, FAIL if the lines are not deleted. 3869 */ 3870 int 3871 screen_del_lines( 3872 int off, 3873 int row, 3874 int line_count, 3875 int end, 3876 int force, // even when line_count > p_ttyscroll 3877 int clear_attr, // used for clearing lines 3878 win_T *wp UNUSED) // NULL or window to use width from 3879 { 3880 int j; 3881 int i; 3882 unsigned temp; 3883 int cursor_row; 3884 int cursor_col = 0; 3885 int cursor_end; 3886 int result_empty; // result is empty until end of region 3887 int can_delete; // deleting line codes can be used 3888 int type; 3889 3890 /* 3891 * FAIL if 3892 * - there is no valid screen 3893 * - the screen has to be redrawn completely 3894 * - the line count is less than one 3895 * - the line count is more than 'ttyscroll' 3896 * - redrawing for a callback and there is a modeless selection 3897 */ 3898 if (!screen_valid(TRUE) || line_count <= 0 3899 || (!force && line_count > p_ttyscroll) 3900 #ifdef FEAT_CLIPBOARD 3901 || (clip_star.state != SELECT_CLEARED 3902 && redrawing_for_callback > 0) 3903 #endif 3904 ) 3905 return FAIL; 3906 3907 /* 3908 * Check if the rest of the current region will become empty. 3909 */ 3910 result_empty = row + line_count >= end; 3911 3912 /* 3913 * We can delete lines only when 'db' flag not set or when 'ce' option 3914 * available. 3915 */ 3916 can_delete = (*T_DB == NUL || can_clear(T_CE)); 3917 3918 /* 3919 * There are six ways to delete lines: 3920 * 0. When in a vertically split window and t_CV isn't set, redraw the 3921 * characters from ScreenLines[]. 3922 * 1. Use T_CD if it exists and the result is empty. 3923 * 2. Use newlines if row == 0 and count == 1 or T_CDL does not exist. 3924 * 3. Use T_CDL (delete multiple lines) if it exists and line_count > 1 or 3925 * none of the other ways work. 3926 * 4. Use T_CE (erase line) if the result is empty. 3927 * 5. Use T_DL (delete line) if it exists. 3928 * 6. redraw the characters from ScreenLines[]. 3929 */ 3930 if (wp != NULL && wp->w_width != Columns && *T_CSV == NUL) 3931 type = USE_REDRAW; 3932 else if (can_clear(T_CD) && result_empty) 3933 type = USE_T_CD; 3934 else if (row == 0 && ( 3935 #ifndef AMIGA 3936 // On the Amiga, somehow '\n' on the last line doesn't always scroll 3937 // up, so use delete-line command 3938 line_count == 1 || 3939 #endif 3940 *T_CDL == NUL)) 3941 type = USE_NL; 3942 else if (*T_CDL != NUL && line_count > 1 && can_delete) 3943 type = USE_T_CDL; 3944 else if (can_clear(T_CE) && result_empty 3945 && (wp == NULL || wp->w_width == Columns)) 3946 type = USE_T_CE; 3947 else if (*T_DL != NUL && can_delete) 3948 type = USE_T_DL; 3949 else if (*T_CDL != NUL && can_delete) 3950 type = USE_T_CDL; 3951 else 3952 return FAIL; 3953 3954 #ifdef FEAT_CLIPBOARD 3955 // Remove a modeless selection when deleting lines halfway the screen or 3956 // not the full width of the screen. 3957 if (off + row > 0 || (wp != NULL && wp->w_width != Columns)) 3958 clip_clear_selection(&clip_star); 3959 else 3960 clip_scroll_selection(line_count); 3961 #endif 3962 3963 #ifdef FEAT_GUI_HAIKU 3964 vim_lock_screen(); 3965 #endif 3966 3967 #ifdef FEAT_GUI 3968 // Don't update the GUI cursor here, ScreenLines[] is invalid until the 3969 // scrolling is actually carried out. 3970 gui_dont_update_cursor(gui.cursor_row >= row + off 3971 && gui.cursor_row < end + off); 3972 #endif 3973 3974 if (wp != NULL && wp->w_wincol != 0 && *T_CSV != NUL && *T_CCS == NUL) 3975 cursor_col = wp->w_wincol; 3976 3977 if (*T_CCS != NUL) // cursor relative to region 3978 { 3979 cursor_row = row; 3980 cursor_end = end; 3981 } 3982 else 3983 { 3984 cursor_row = row + off; 3985 cursor_end = end + off; 3986 } 3987 3988 /* 3989 * Now shift LineOffset[] line_count up to reflect the deleted lines. 3990 * Clear the inserted lines in ScreenLines[]. 3991 */ 3992 row += off; 3993 end += off; 3994 for (i = 0; i < line_count; ++i) 3995 { 3996 if (wp != NULL && wp->w_width != Columns) 3997 { 3998 // need to copy part of a line 3999 j = row + i; 4000 while ((j += line_count) <= end - 1) 4001 linecopy(j - line_count, j, wp); 4002 j -= line_count; 4003 if (can_clear((char_u *)" ")) 4004 lineclear(LineOffset[j] + wp->w_wincol, wp->w_width, 4005 clear_attr); 4006 else 4007 lineinvalid(LineOffset[j] + wp->w_wincol, wp->w_width); 4008 LineWraps[j] = FALSE; 4009 } 4010 else 4011 { 4012 // whole width, moving the line pointers is faster 4013 j = row + i; 4014 temp = LineOffset[j]; 4015 while ((j += line_count) <= end - 1) 4016 { 4017 LineOffset[j - line_count] = LineOffset[j]; 4018 LineWraps[j - line_count] = LineWraps[j]; 4019 } 4020 LineOffset[j - line_count] = temp; 4021 LineWraps[j - line_count] = FALSE; 4022 if (can_clear((char_u *)" ")) 4023 lineclear(temp, (int)Columns, clear_attr); 4024 else 4025 lineinvalid(temp, (int)Columns); 4026 } 4027 } 4028 4029 #ifdef FEAT_GUI_HAIKU 4030 vim_unlock_screen(); 4031 #endif 4032 4033 if (screen_attr != clear_attr) 4034 screen_stop_highlight(); 4035 if (clear_attr != 0) 4036 screen_start_highlight(clear_attr); 4037 4038 // redraw the characters 4039 if (type == USE_REDRAW) 4040 redraw_block(row, end, wp); 4041 else if (type == USE_T_CD) // delete the lines 4042 { 4043 windgoto(cursor_row, cursor_col); 4044 out_str(T_CD); 4045 screen_start(); // don't know where cursor is now 4046 } 4047 else if (type == USE_T_CDL) 4048 { 4049 windgoto(cursor_row, cursor_col); 4050 term_delete_lines(line_count); 4051 screen_start(); // don't know where cursor is now 4052 } 4053 /* 4054 * Deleting lines at top of the screen or scroll region: Just scroll 4055 * the whole screen (scroll region) up by outputting newlines on the 4056 * last line. 4057 */ 4058 else if (type == USE_NL) 4059 { 4060 windgoto(cursor_end - 1, cursor_col); 4061 for (i = line_count; --i >= 0; ) 4062 out_char('\n'); // cursor will remain on same line 4063 } 4064 else 4065 { 4066 for (i = line_count; --i >= 0; ) 4067 { 4068 if (type == USE_T_DL) 4069 { 4070 windgoto(cursor_row, cursor_col); 4071 out_str(T_DL); // delete a line 4072 } 4073 else // type == USE_T_CE 4074 { 4075 windgoto(cursor_row + i, cursor_col); 4076 out_str(T_CE); // erase a line 4077 } 4078 screen_start(); // don't know where cursor is now 4079 } 4080 } 4081 4082 /* 4083 * If the 'db' flag is set, we need to clear the lines that have been 4084 * scrolled up at the bottom of the region. 4085 */ 4086 if (*T_DB && (type == USE_T_DL || type == USE_T_CDL)) 4087 { 4088 for (i = line_count; i > 0; --i) 4089 { 4090 windgoto(cursor_end - i, cursor_col); 4091 out_str(T_CE); // erase a line 4092 screen_start(); // don't know where cursor is now 4093 } 4094 } 4095 4096 #ifdef FEAT_GUI 4097 gui_can_update_cursor(); 4098 if (gui.in_use) 4099 out_flush(); // always flush after a scroll 4100 #endif 4101 4102 return OK; 4103 } 4104 4105 /* 4106 * Return TRUE when postponing displaying the mode message: when not redrawing 4107 * or inside a mapping. 4108 */ 4109 int 4110 skip_showmode() 4111 { 4112 // Call char_avail() only when we are going to show something, because it 4113 // takes a bit of time. redrawing() may also call char_avail_avail(). 4114 if (global_busy 4115 || msg_silent != 0 4116 || !redrawing() 4117 || (char_avail() && !KeyTyped)) 4118 { 4119 redraw_mode = TRUE; // show mode later 4120 return TRUE; 4121 } 4122 return FALSE; 4123 } 4124 4125 /* 4126 * Show the current mode and ruler. 4127 * 4128 * If clear_cmdline is TRUE, clear the rest of the cmdline. 4129 * If clear_cmdline is FALSE there may be a message there that needs to be 4130 * cleared only if a mode is shown. 4131 * If redraw_mode is TRUE show or clear the mode. 4132 * Return the length of the message (0 if no message). 4133 */ 4134 int 4135 showmode(void) 4136 { 4137 int need_clear; 4138 int length = 0; 4139 int do_mode; 4140 int attr; 4141 int nwr_save; 4142 int sub_attr; 4143 4144 do_mode = ((p_smd && msg_silent == 0) 4145 && ((State & INSERT) 4146 || restart_edit != NUL 4147 || VIsual_active)); 4148 if (do_mode || reg_recording != 0) 4149 { 4150 if (skip_showmode()) 4151 return 0; // show mode later 4152 4153 nwr_save = need_wait_return; 4154 4155 // wait a bit before overwriting an important message 4156 check_for_delay(FALSE); 4157 4158 // if the cmdline is more than one line high, erase top lines 4159 need_clear = clear_cmdline; 4160 if (clear_cmdline && cmdline_row < Rows - 1) 4161 msg_clr_cmdline(); // will reset clear_cmdline 4162 4163 // Position on the last line in the window, column 0 4164 msg_pos_mode(); 4165 cursor_off(); 4166 attr = HL_ATTR(HLF_CM); // Highlight mode 4167 if (do_mode) 4168 { 4169 msg_puts_attr("--", attr); 4170 #if defined(FEAT_XIM) 4171 if ( 4172 # ifdef FEAT_GUI_GTK 4173 preedit_get_status() 4174 # else 4175 im_get_status() 4176 # endif 4177 ) 4178 # ifdef FEAT_GUI_GTK // most of the time, it's not XIM being used 4179 msg_puts_attr(" IM", attr); 4180 # else 4181 msg_puts_attr(" XIM", attr); 4182 # endif 4183 #endif 4184 // CTRL-X in Insert mode 4185 if (edit_submode != NULL && !shortmess(SHM_COMPLETIONMENU)) 4186 { 4187 // These messages can get long, avoid a wrap in a narrow 4188 // window. Prefer showing edit_submode_extra. 4189 length = (Rows - msg_row) * Columns - 3; 4190 if (edit_submode_extra != NULL) 4191 length -= vim_strsize(edit_submode_extra); 4192 if (length > 0) 4193 { 4194 if (edit_submode_pre != NULL) 4195 length -= vim_strsize(edit_submode_pre); 4196 if (length - vim_strsize(edit_submode) > 0) 4197 { 4198 if (edit_submode_pre != NULL) 4199 msg_puts_attr((char *)edit_submode_pre, attr); 4200 msg_puts_attr((char *)edit_submode, attr); 4201 } 4202 if (edit_submode_extra != NULL) 4203 { 4204 msg_puts_attr(" ", attr); // add a space in between 4205 if ((int)edit_submode_highl < (int)HLF_COUNT) 4206 sub_attr = HL_ATTR(edit_submode_highl); 4207 else 4208 sub_attr = attr; 4209 msg_puts_attr((char *)edit_submode_extra, sub_attr); 4210 } 4211 } 4212 } 4213 else 4214 { 4215 if (State & VREPLACE_FLAG) 4216 msg_puts_attr(_(" VREPLACE"), attr); 4217 else if (State & REPLACE_FLAG) 4218 msg_puts_attr(_(" REPLACE"), attr); 4219 else if (State & INSERT) 4220 { 4221 #ifdef FEAT_RIGHTLEFT 4222 if (p_ri) 4223 msg_puts_attr(_(" REVERSE"), attr); 4224 #endif 4225 msg_puts_attr(_(" INSERT"), attr); 4226 } 4227 else if (restart_edit == 'I' || restart_edit == 'i' || 4228 restart_edit == 'a' || restart_edit == 'A') 4229 msg_puts_attr(_(" (insert)"), attr); 4230 else if (restart_edit == 'R') 4231 msg_puts_attr(_(" (replace)"), attr); 4232 else if (restart_edit == 'V') 4233 msg_puts_attr(_(" (vreplace)"), attr); 4234 #ifdef FEAT_RIGHTLEFT 4235 if (p_hkmap) 4236 msg_puts_attr(_(" Hebrew"), attr); 4237 #endif 4238 #ifdef FEAT_KEYMAP 4239 if (State & LANGMAP) 4240 { 4241 # ifdef FEAT_ARABIC 4242 if (curwin->w_p_arab) 4243 msg_puts_attr(_(" Arabic"), attr); 4244 else 4245 # endif 4246 if (get_keymap_str(curwin, (char_u *)" (%s)", 4247 NameBuff, MAXPATHL)) 4248 msg_puts_attr((char *)NameBuff, attr); 4249 } 4250 #endif 4251 if ((State & INSERT) && p_paste) 4252 msg_puts_attr(_(" (paste)"), attr); 4253 4254 if (VIsual_active) 4255 { 4256 char *p; 4257 4258 // Don't concatenate separate words to avoid translation 4259 // problems. 4260 switch ((VIsual_select ? 4 : 0) 4261 + (VIsual_mode == Ctrl_V) * 2 4262 + (VIsual_mode == 'V')) 4263 { 4264 case 0: p = N_(" VISUAL"); break; 4265 case 1: p = N_(" VISUAL LINE"); break; 4266 case 2: p = N_(" VISUAL BLOCK"); break; 4267 case 4: p = N_(" SELECT"); break; 4268 case 5: p = N_(" SELECT LINE"); break; 4269 default: p = N_(" SELECT BLOCK"); break; 4270 } 4271 msg_puts_attr(_(p), attr); 4272 } 4273 msg_puts_attr(" --", attr); 4274 } 4275 4276 need_clear = TRUE; 4277 } 4278 if (reg_recording != 0 4279 && edit_submode == NULL) // otherwise it gets too long 4280 { 4281 recording_mode(attr); 4282 need_clear = TRUE; 4283 } 4284 4285 mode_displayed = TRUE; 4286 if (need_clear || clear_cmdline || redraw_mode) 4287 msg_clr_eos(); 4288 msg_didout = FALSE; // overwrite this message 4289 length = msg_col; 4290 msg_col = 0; 4291 need_wait_return = nwr_save; // never ask for hit-return for this 4292 } 4293 else if (clear_cmdline && msg_silent == 0) 4294 // Clear the whole command line. Will reset "clear_cmdline". 4295 msg_clr_cmdline(); 4296 else if (redraw_mode) 4297 { 4298 msg_pos_mode(); 4299 msg_clr_eos(); 4300 } 4301 4302 #ifdef FEAT_CMDL_INFO 4303 // In Visual mode the size of the selected area must be redrawn. 4304 if (VIsual_active) 4305 clear_showcmd(); 4306 4307 // If the last window has no status line, the ruler is after the mode 4308 // message and must be redrawn 4309 if (redrawing() && lastwin->w_status_height == 0) 4310 win_redr_ruler(lastwin, TRUE, FALSE); 4311 #endif 4312 redraw_cmdline = FALSE; 4313 redraw_mode = FALSE; 4314 clear_cmdline = FALSE; 4315 4316 return length; 4317 } 4318 4319 /* 4320 * Position for a mode message. 4321 */ 4322 static void 4323 msg_pos_mode(void) 4324 { 4325 msg_col = 0; 4326 msg_row = Rows - 1; 4327 } 4328 4329 /* 4330 * Delete mode message. Used when ESC is typed which is expected to end 4331 * Insert mode (but Insert mode didn't end yet!). 4332 * Caller should check "mode_displayed". 4333 */ 4334 void 4335 unshowmode(int force) 4336 { 4337 /* 4338 * Don't delete it right now, when not redrawing or inside a mapping. 4339 */ 4340 if (!redrawing() || (!force && char_avail() && !KeyTyped)) 4341 redraw_cmdline = TRUE; // delete mode later 4342 else 4343 clearmode(); 4344 } 4345 4346 /* 4347 * Clear the mode message. 4348 */ 4349 void 4350 clearmode(void) 4351 { 4352 int save_msg_row = msg_row; 4353 int save_msg_col = msg_col; 4354 4355 msg_pos_mode(); 4356 if (reg_recording != 0) 4357 recording_mode(HL_ATTR(HLF_CM)); 4358 msg_clr_eos(); 4359 4360 msg_col = save_msg_col; 4361 msg_row = save_msg_row; 4362 } 4363 4364 static void 4365 recording_mode(int attr) 4366 { 4367 msg_puts_attr(_("recording"), attr); 4368 if (!shortmess(SHM_RECORDING)) 4369 { 4370 char s[4]; 4371 4372 sprintf(s, " @%c", reg_recording); 4373 msg_puts_attr(s, attr); 4374 } 4375 } 4376 4377 /* 4378 * Draw the tab pages line at the top of the Vim window. 4379 */ 4380 void 4381 draw_tabline(void) 4382 { 4383 int tabcount = 0; 4384 tabpage_T *tp; 4385 int tabwidth; 4386 int col = 0; 4387 int scol = 0; 4388 int attr; 4389 win_T *wp; 4390 win_T *cwp; 4391 int wincount; 4392 int modified; 4393 int c; 4394 int len; 4395 int attr_sel = HL_ATTR(HLF_TPS); 4396 int attr_nosel = HL_ATTR(HLF_TP); 4397 int attr_fill = HL_ATTR(HLF_TPF); 4398 char_u *p; 4399 int room; 4400 int use_sep_chars = (t_colors < 8 4401 #ifdef FEAT_GUI 4402 && !gui.in_use 4403 #endif 4404 #ifdef FEAT_TERMGUICOLORS 4405 && !p_tgc 4406 #endif 4407 ); 4408 4409 if (ScreenLines == NULL) 4410 return; 4411 redraw_tabline = FALSE; 4412 4413 #ifdef FEAT_GUI_TABLINE 4414 // Take care of a GUI tabline. 4415 if (gui_use_tabline()) 4416 { 4417 gui_update_tabline(); 4418 return; 4419 } 4420 #endif 4421 4422 if (tabline_height() < 1) 4423 return; 4424 4425 #if defined(FEAT_STL_OPT) 4426 clear_TabPageIdxs(); 4427 4428 // Use the 'tabline' option if it's set. 4429 if (*p_tal != NUL) 4430 { 4431 int saved_did_emsg = did_emsg; 4432 4433 // Check for an error. If there is one we would loop in redrawing the 4434 // screen. Avoid that by making 'tabline' empty. 4435 did_emsg = FALSE; 4436 win_redr_custom(NULL, FALSE); 4437 if (did_emsg) 4438 set_string_option_direct((char_u *)"tabline", -1, 4439 (char_u *)"", OPT_FREE, SID_ERROR); 4440 did_emsg |= saved_did_emsg; 4441 } 4442 else 4443 #endif 4444 { 4445 FOR_ALL_TABPAGES(tp) 4446 ++tabcount; 4447 4448 tabwidth = (Columns - 1 + tabcount / 2) / tabcount; 4449 if (tabwidth < 6) 4450 tabwidth = 6; 4451 4452 attr = attr_nosel; 4453 tabcount = 0; 4454 for (tp = first_tabpage; tp != NULL && col < Columns - 4; 4455 tp = tp->tp_next) 4456 { 4457 scol = col; 4458 4459 if (tp->tp_topframe == topframe) 4460 attr = attr_sel; 4461 if (use_sep_chars && col > 0) 4462 screen_putchar('|', 0, col++, attr); 4463 4464 if (tp->tp_topframe != topframe) 4465 attr = attr_nosel; 4466 4467 screen_putchar(' ', 0, col++, attr); 4468 4469 if (tp == curtab) 4470 { 4471 cwp = curwin; 4472 wp = firstwin; 4473 } 4474 else 4475 { 4476 cwp = tp->tp_curwin; 4477 wp = tp->tp_firstwin; 4478 } 4479 4480 modified = FALSE; 4481 for (wincount = 0; wp != NULL; wp = wp->w_next, ++wincount) 4482 if (bufIsChanged(wp->w_buffer)) 4483 modified = TRUE; 4484 if (modified || wincount > 1) 4485 { 4486 if (wincount > 1) 4487 { 4488 vim_snprintf((char *)NameBuff, MAXPATHL, "%d", wincount); 4489 len = (int)STRLEN(NameBuff); 4490 if (col + len >= Columns - 3) 4491 break; 4492 screen_puts_len(NameBuff, len, 0, col, 4493 #if defined(FEAT_SYN_HL) 4494 hl_combine_attr(attr, HL_ATTR(HLF_T)) 4495 #else 4496 attr 4497 #endif 4498 ); 4499 col += len; 4500 } 4501 if (modified) 4502 screen_puts_len((char_u *)"+", 1, 0, col++, attr); 4503 screen_putchar(' ', 0, col++, attr); 4504 } 4505 4506 room = scol - col + tabwidth - 1; 4507 if (room > 0) 4508 { 4509 // Get buffer name in NameBuff[] 4510 get_trans_bufname(cwp->w_buffer); 4511 shorten_dir(NameBuff); 4512 len = vim_strsize(NameBuff); 4513 p = NameBuff; 4514 if (has_mbyte) 4515 while (len > room) 4516 { 4517 len -= ptr2cells(p); 4518 MB_PTR_ADV(p); 4519 } 4520 else if (len > room) 4521 { 4522 p += len - room; 4523 len = room; 4524 } 4525 if (len > Columns - col - 1) 4526 len = Columns - col - 1; 4527 4528 screen_puts_len(p, (int)STRLEN(p), 0, col, attr); 4529 col += len; 4530 } 4531 screen_putchar(' ', 0, col++, attr); 4532 4533 // Store the tab page number in TabPageIdxs[], so that 4534 // jump_to_mouse() knows where each one is. 4535 ++tabcount; 4536 while (scol < col) 4537 TabPageIdxs[scol++] = tabcount; 4538 } 4539 4540 if (use_sep_chars) 4541 c = '_'; 4542 else 4543 c = ' '; 4544 screen_fill(0, 1, col, (int)Columns, c, c, attr_fill); 4545 4546 // Put an "X" for closing the current tab if there are several. 4547 if (first_tabpage->tp_next != NULL) 4548 { 4549 screen_putchar('X', 0, (int)Columns - 1, attr_nosel); 4550 TabPageIdxs[Columns - 1] = -999; 4551 } 4552 } 4553 4554 // Reset the flag here again, in case evaluating 'tabline' causes it to be 4555 // set. 4556 redraw_tabline = FALSE; 4557 } 4558 4559 /* 4560 * Get buffer name for "buf" into NameBuff[]. 4561 * Takes care of special buffer names and translates special characters. 4562 */ 4563 void 4564 get_trans_bufname(buf_T *buf) 4565 { 4566 if (buf_spname(buf) != NULL) 4567 vim_strncpy(NameBuff, buf_spname(buf), MAXPATHL - 1); 4568 else 4569 home_replace(buf, buf->b_fname, NameBuff, MAXPATHL, TRUE); 4570 trans_characters(NameBuff, MAXPATHL); 4571 } 4572 4573 /* 4574 * Get the character to use in a status line. Get its attributes in "*attr". 4575 */ 4576 int 4577 fillchar_status(int *attr, win_T *wp) 4578 { 4579 int fill; 4580 4581 #ifdef FEAT_TERMINAL 4582 if (bt_terminal(wp->w_buffer)) 4583 { 4584 if (wp == curwin) 4585 { 4586 *attr = HL_ATTR(HLF_ST); 4587 fill = fill_stl; 4588 } 4589 else 4590 { 4591 *attr = HL_ATTR(HLF_STNC); 4592 fill = fill_stlnc; 4593 } 4594 } 4595 else 4596 #endif 4597 if (wp == curwin) 4598 { 4599 *attr = HL_ATTR(HLF_S); 4600 fill = fill_stl; 4601 } 4602 else 4603 { 4604 *attr = HL_ATTR(HLF_SNC); 4605 fill = fill_stlnc; 4606 } 4607 // Use fill when there is highlighting, and highlighting of current 4608 // window differs, or the fillchars differ, or this is not the 4609 // current window 4610 if (*attr != 0 && ((HL_ATTR(HLF_S) != HL_ATTR(HLF_SNC) 4611 || wp != curwin || ONE_WINDOW) 4612 || (fill_stl != fill_stlnc))) 4613 return fill; 4614 if (wp == curwin) 4615 return '^'; 4616 return '='; 4617 } 4618 4619 /* 4620 * Get the character to use in a separator between vertically split windows. 4621 * Get its attributes in "*attr". 4622 */ 4623 int 4624 fillchar_vsep(int *attr) 4625 { 4626 *attr = HL_ATTR(HLF_C); 4627 if (*attr == 0 && fill_vert == ' ') 4628 return '|'; 4629 else 4630 return fill_vert; 4631 } 4632 4633 /* 4634 * Return TRUE if redrawing should currently be done. 4635 */ 4636 int 4637 redrawing(void) 4638 { 4639 #ifdef FEAT_EVAL 4640 if (disable_redraw_for_testing) 4641 return 0; 4642 else 4643 #endif 4644 return ((!RedrawingDisabled 4645 #ifdef FEAT_EVAL 4646 || ignore_redraw_flag_for_testing 4647 #endif 4648 ) && !(p_lz && char_avail() && !KeyTyped && !do_redraw)); 4649 } 4650 4651 /* 4652 * Return TRUE if printing messages should currently be done. 4653 */ 4654 int 4655 messaging(void) 4656 { 4657 return (!(p_lz && char_avail() && !KeyTyped)); 4658 } 4659 4660 /* 4661 * Compute columns for ruler and shown command. 'sc_col' is also used to 4662 * decide what the maximum length of a message on the status line can be. 4663 * If there is a status line for the last window, 'sc_col' is independent 4664 * of 'ru_col'. 4665 */ 4666 4667 #define COL_RULER 17 // columns needed by standard ruler 4668 4669 void 4670 comp_col(void) 4671 { 4672 #if defined(FEAT_CMDL_INFO) 4673 int last_has_status = (p_ls == 2 || (p_ls == 1 && !ONE_WINDOW)); 4674 4675 sc_col = 0; 4676 ru_col = 0; 4677 if (p_ru) 4678 { 4679 # ifdef FEAT_STL_OPT 4680 ru_col = (ru_wid ? ru_wid : COL_RULER) + 1; 4681 # else 4682 ru_col = COL_RULER + 1; 4683 # endif 4684 // no last status line, adjust sc_col 4685 if (!last_has_status) 4686 sc_col = ru_col; 4687 } 4688 if (p_sc) 4689 { 4690 sc_col += SHOWCMD_COLS; 4691 if (!p_ru || last_has_status) // no need for separating space 4692 ++sc_col; 4693 } 4694 sc_col = Columns - sc_col; 4695 ru_col = Columns - ru_col; 4696 if (sc_col <= 0) // screen too narrow, will become a mess 4697 sc_col = 1; 4698 if (ru_col <= 0) 4699 ru_col = 1; 4700 #else 4701 sc_col = Columns; 4702 ru_col = Columns; 4703 #endif 4704 #ifdef FEAT_EVAL 4705 set_vim_var_nr(VV_ECHOSPACE, sc_col - 1); 4706 #endif 4707 } 4708 4709 #if defined(FEAT_LINEBREAK) || defined(PROTO) 4710 /* 4711 * Return the width of the 'number' and 'relativenumber' column. 4712 * Caller may need to check if 'number' or 'relativenumber' is set. 4713 * Otherwise it depends on 'numberwidth' and the line count. 4714 */ 4715 int 4716 number_width(win_T *wp) 4717 { 4718 int n; 4719 linenr_T lnum; 4720 4721 if (wp->w_p_rnu && !wp->w_p_nu) 4722 // cursor line shows "0" 4723 lnum = wp->w_height; 4724 else 4725 // cursor line shows absolute line number 4726 lnum = wp->w_buffer->b_ml.ml_line_count; 4727 4728 if (lnum == wp->w_nrwidth_line_count && wp->w_nuw_cached == wp->w_p_nuw) 4729 return wp->w_nrwidth_width; 4730 wp->w_nrwidth_line_count = lnum; 4731 4732 n = 0; 4733 do 4734 { 4735 lnum /= 10; 4736 ++n; 4737 } while (lnum > 0); 4738 4739 // 'numberwidth' gives the minimal width plus one 4740 if (n < wp->w_p_nuw - 1) 4741 n = wp->w_p_nuw - 1; 4742 4743 # ifdef FEAT_SIGNS 4744 // If 'signcolumn' is set to 'number' and there is a sign to display, then 4745 // the minimal width for the number column is 2. 4746 if (n < 2 && get_first_valid_sign(wp) != NULL 4747 && (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u')) 4748 n = 2; 4749 # endif 4750 4751 wp->w_nrwidth_width = n; 4752 wp->w_nuw_cached = wp->w_p_nuw; 4753 return n; 4754 } 4755 #endif 4756 4757 #if defined(FEAT_EVAL) || defined(PROTO) 4758 /* 4759 * Return the current cursor column. This is the actual position on the 4760 * screen. First column is 0. 4761 */ 4762 int 4763 screen_screencol(void) 4764 { 4765 return screen_cur_col; 4766 } 4767 4768 /* 4769 * Return the current cursor row. This is the actual position on the screen. 4770 * First row is 0. 4771 */ 4772 int 4773 screen_screenrow(void) 4774 { 4775 return screen_cur_row; 4776 } 4777 #endif 4778 4779 /* 4780 * Handle setting 'listchars' or 'fillchars'. 4781 * Assume monocell characters. 4782 * Returns error message, NULL if it's OK. 4783 */ 4784 char * 4785 set_chars_option(win_T *wp, char_u **varp) 4786 { 4787 int round, i, len, entries; 4788 char_u *p, *s; 4789 int c1 = 0, c2 = 0, c3 = 0; 4790 char_u *last_multispace = NULL; // Last occurrence of "multispace:" 4791 int multispace_len = 0; // Length of lcs-multispace string 4792 struct charstab 4793 { 4794 int *cp; 4795 char *name; 4796 }; 4797 static struct charstab filltab[] = 4798 { 4799 {&fill_stl, "stl"}, 4800 {&fill_stlnc, "stlnc"}, 4801 {&fill_vert, "vert"}, 4802 {&fill_fold, "fold"}, 4803 {&fill_foldopen, "foldopen"}, 4804 {&fill_foldclosed, "foldclose"}, 4805 {&fill_foldsep, "foldsep"}, 4806 {&fill_diff, "diff"}, 4807 {&fill_eob, "eob"}, 4808 }; 4809 static lcs_chars_T lcs_chars; 4810 struct charstab lcstab[] = 4811 { 4812 {&lcs_chars.eol, "eol"}, 4813 {&lcs_chars.ext, "extends"}, 4814 {&lcs_chars.nbsp, "nbsp"}, 4815 {&lcs_chars.prec, "precedes"}, 4816 {&lcs_chars.space, "space"}, 4817 {&lcs_chars.tab2, "tab"}, 4818 {&lcs_chars.trail, "trail"}, 4819 {&lcs_chars.lead, "lead"}, 4820 #ifdef FEAT_CONCEAL 4821 {&lcs_chars.conceal, "conceal"}, 4822 #else 4823 {NULL, "conceal"}, 4824 #endif 4825 }; 4826 struct charstab *tab; 4827 4828 if (varp == &p_lcs || varp == &wp->w_p_lcs) 4829 { 4830 tab = lcstab; 4831 CLEAR_FIELD(lcs_chars); 4832 entries = ARRAY_LENGTH(lcstab); 4833 if (varp == &wp->w_p_lcs && wp->w_p_lcs[0] == NUL) 4834 varp = &p_lcs; 4835 } 4836 else 4837 { 4838 tab = filltab; 4839 entries = ARRAY_LENGTH(filltab); 4840 } 4841 4842 // first round: check for valid value, second round: assign values 4843 for (round = 0; round <= 1; ++round) 4844 { 4845 if (round > 0) 4846 { 4847 // After checking that the value is valid: set defaults: space for 4848 // 'fillchars', NUL for 'listchars' 4849 for (i = 0; i < entries; ++i) 4850 if (tab[i].cp != NULL) 4851 *(tab[i].cp) = 4852 ((varp == &p_lcs || varp == &wp->w_p_lcs) ? NUL : ' '); 4853 4854 if (varp == &p_lcs || varp == &wp->w_p_lcs) 4855 { 4856 lcs_chars.tab1 = NUL; 4857 lcs_chars.tab3 = NUL; 4858 if (multispace_len > 0) 4859 { 4860 lcs_chars.multispace = ALLOC_MULT(int, multispace_len + 1); 4861 lcs_chars.multispace[multispace_len] = NUL; 4862 } 4863 else 4864 lcs_chars.multispace = NULL; 4865 } 4866 else 4867 { 4868 fill_diff = '-'; 4869 fill_foldopen = '-'; 4870 fill_foldclosed = '+'; 4871 fill_foldsep = '|'; 4872 fill_eob = '~'; 4873 } 4874 } 4875 p = *varp; 4876 while (*p) 4877 { 4878 for (i = 0; i < entries; ++i) 4879 { 4880 len = (int)STRLEN(tab[i].name); 4881 if (STRNCMP(p, tab[i].name, len) == 0 4882 && p[len] == ':' 4883 && p[len + 1] != NUL) 4884 { 4885 c2 = c3 = 0; 4886 s = p + len + 1; 4887 c1 = mb_ptr2char_adv(&s); 4888 if (mb_char2cells(c1) > 1) 4889 return e_invarg; 4890 if (tab[i].cp == &lcs_chars.tab2) 4891 { 4892 if (*s == NUL) 4893 return e_invarg; 4894 c2 = mb_ptr2char_adv(&s); 4895 if (mb_char2cells(c2) > 1) 4896 return e_invarg; 4897 if (!(*s == ',' || *s == NUL)) 4898 { 4899 c3 = mb_ptr2char_adv(&s); 4900 if (mb_char2cells(c3) > 1) 4901 return e_invarg; 4902 } 4903 } 4904 4905 if (*s == ',' || *s == NUL) 4906 { 4907 if (round > 0) 4908 { 4909 if (tab[i].cp == &lcs_chars.tab2) 4910 { 4911 lcs_chars.tab1 = c1; 4912 lcs_chars.tab2 = c2; 4913 lcs_chars.tab3 = c3; 4914 } 4915 else if (tab[i].cp != NULL) 4916 *(tab[i].cp) = c1; 4917 4918 } 4919 p = s; 4920 break; 4921 } 4922 } 4923 } 4924 4925 if (i == entries) 4926 { 4927 len = (int)STRLEN("multispace"); 4928 if ((varp == &p_lcs || varp == &wp->w_p_lcs) 4929 && STRNCMP(p, "multispace", len) == 0 4930 && p[len] == ':' 4931 && p[len + 1] != NUL) 4932 { 4933 s = p + len + 1; 4934 if (round == 0) 4935 { 4936 // Get length of lcs-multispace string in first round 4937 last_multispace = p; 4938 multispace_len = 0; 4939 while (*s != NUL && *s != ',') 4940 { 4941 c1 = mb_ptr2char_adv(&s); 4942 if (mb_char2cells(c1) > 1) 4943 return e_invarg; 4944 ++multispace_len; 4945 } 4946 if (multispace_len == 0) 4947 // lcs-multispace cannot be an empty string 4948 return e_invarg; 4949 p = s; 4950 } 4951 else 4952 { 4953 int multispace_pos = 0; 4954 4955 while (*s != NUL && *s != ',') 4956 { 4957 c1 = mb_ptr2char_adv(&s); 4958 if (p == last_multispace) 4959 lcs_chars.multispace[multispace_pos++] = c1; 4960 } 4961 p = s; 4962 } 4963 } 4964 else 4965 return e_invarg; 4966 } 4967 4968 if (*p == ',') 4969 ++p; 4970 } 4971 } 4972 if (tab == lcstab) 4973 { 4974 if (wp->w_lcs_chars.multispace != NULL) 4975 vim_free(wp->w_lcs_chars.multispace); 4976 wp->w_lcs_chars = lcs_chars; 4977 } 4978 4979 return NULL; // no error 4980 } 4981 4982