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