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