diff options
Diffstat (limited to 'src/nvim/screen.c')
-rw-r--r-- | src/nvim/screen.c | 1804 |
1 files changed, 860 insertions, 944 deletions
diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 49aeaff3a6..d1453b56d2 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -85,6 +85,7 @@ #include "nvim/fold.h" #include "nvim/indent.h" #include "nvim/getchar.h" +#include "nvim/highlight.h" #include "nvim/main.h" #include "nvim/mark.h" #include "nvim/mbyte.h" @@ -131,6 +132,15 @@ static schar_T *current_ScreenLine; StlClickDefinition *tab_page_click_defs = NULL; long tab_page_click_defs_size = 0; +// for line_putchar. Contains the state that needs to be remembered from +// putting one character to the next. +typedef struct { + const char_u *p; + int prev_c; // previous Arabic character + int prev_c1; // first composing char for prev_c +} LineState; +#define LINE_STATE(p) { p, 0, 0 } + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "screen.c.generated.h" #endif @@ -148,7 +158,7 @@ void redraw_later(int type) void redraw_win_later(win_T *wp, int type) { - if (wp->w_redr_type < type) { + if (!exiting && wp->w_redr_type < type) { wp->w_redr_type = type; if (type >= NOT_VALID) wp->w_lines_valid = 0; @@ -158,15 +168,6 @@ void redraw_win_later(win_T *wp, int type) } /* - * Force a complete redraw later. Also resets the highlighting. To be used - * after executing a shell command that messes up the screen. - */ -void redraw_later_clear(void) -{ - redraw_all_later(CLEAR); -} - -/* * Mark all windows to be redrawn later. */ void redraw_all_later(int type) @@ -202,24 +203,28 @@ void redraw_buf_later(buf_T *buf, int type) * may become invalid and the whole window will have to be redrawn. */ void -redrawWinline ( +redrawWinline( + win_T *wp, linenr_T lnum, int invalid /* window line height is invalid now */ ) { int i; - if (curwin->w_redraw_top == 0 || curwin->w_redraw_top > lnum) - curwin->w_redraw_top = lnum; - if (curwin->w_redraw_bot == 0 || curwin->w_redraw_bot < lnum) - curwin->w_redraw_bot = lnum; - redraw_later(VALID); + if (wp->w_redraw_top == 0 || wp->w_redraw_top > lnum) { + wp->w_redraw_top = lnum; + } + if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lnum) { + wp->w_redraw_bot = lnum; + } + redraw_win_later(wp, VALID); if (invalid) { - /* A w_lines[] entry for this lnum has become invalid. */ - i = find_wl_entry(curwin, lnum); - if (i >= 0) - curwin->w_lines[i].wl_valid = FALSE; + // A w_lines[] entry for this lnum has become invalid. + i = find_wl_entry(wp, lnum); + if (i >= 0) { + wp->w_lines[i].wl_valid = false; + } } } @@ -282,8 +287,11 @@ void update_screen(int type) if (msg_scrolled) { clear_cmdline = true; if (dy_flags & DY_MSGSEP) { + int valid = MAX(Rows - msg_scrollsize(), 0); + if (valid == 0) { + redraw_tabline = true; + } FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - int valid = Rows - msg_scrollsize(); if (wp->w_winrow + wp->w_height > valid) { wp->w_redr_type = NOT_VALID; wp->w_lines_valid = 0; @@ -291,15 +299,13 @@ void update_screen(int type) if (wp->w_winrow + wp->w_height + wp->w_status_height > valid) { wp->w_redr_status = true; } - if (valid == 0) { - redraw_tabline = true; - } } } else if (msg_scrolled > Rows - 5) { // clearing is faster type = CLEAR; } else if (type != CLEAR) { check_for_delay(false); - if (screen_ins_lines(0, 0, msg_scrolled, (int)Rows, NULL) == FAIL) { + if (screen_ins_lines(0, msg_scrolled, (int)Rows, 0, (int)Columns) + == FAIL) { type = CLEAR; } FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { @@ -421,7 +427,7 @@ void update_screen(int type) /* redraw status line after the window to minimize cursor movement */ if (wp->w_redr_status) { - win_redr_status(wp); + win_redr_status(wp, true); // any popup menu will be redrawn below } } end_search_hl(); @@ -476,7 +482,7 @@ int conceal_cursor_line(win_T *wp) /* * Check if the cursor line needs to be redrawn because of 'concealcursor'. */ -void conceal_check_cursur_line(void) +void conceal_check_cursor_line(void) { if (curwin->w_p_cole > 0 && conceal_cursor_line(curwin)) { need_cursor_line_redraw = TRUE; @@ -505,7 +511,8 @@ void update_single_line(win_T *wp, linenr_T lnum) init_search_hl(wp); start_search_hl(); prepare_search_hl(wp, lnum); - win_line(wp, lnum, row, row + wp->w_lines[j].wl_size, false); + update_window_hl(wp, false); + win_line(wp, lnum, row, row + wp->w_lines[j].wl_size, false, false); end_search_hl(); break; } @@ -540,51 +547,57 @@ static void update_finish(void) updating_screen = FALSE; } -void update_debug_sign(buf_T *buf, linenr_T lnum) +void update_debug_sign(const buf_T *const buf, const linenr_T lnum) { - int doit = FALSE; - win_foldinfo.fi_level = 0; - - /* update/delete a specific mark */ - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (buf != NULL && lnum > 0) { - if (wp->w_buffer == buf && lnum >= wp->w_topline - && lnum < wp->w_botline) { - if (wp->w_redraw_top == 0 || wp->w_redraw_top > lnum) { - wp->w_redraw_top = lnum; - } - if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lnum) { - wp->w_redraw_bot = lnum; - } - redraw_win_later(wp, VALID); + bool doit = false; + win_foldinfo.fi_level = 0; + + // update/delete a specific mark + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (buf != NULL && lnum > 0) { + if (wp->w_buffer == buf && lnum >= wp->w_topline + && lnum < wp->w_botline) { + if (wp->w_redraw_top == 0 || wp->w_redraw_top > lnum) { + wp->w_redraw_top = lnum; + } + if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lnum) { + wp->w_redraw_bot = lnum; } - } else { redraw_win_later(wp, VALID); } - if (wp->w_redr_type != 0) { - doit = TRUE; - } + } else { + redraw_win_later(wp, VALID); } - - /* Return when there is nothing to do, screen updating is already - * happening (recursive call) or still starting up. */ - if (!doit || updating_screen || starting) { - return; + if (wp->w_redr_type != 0) { + doit = true; } + } - /* update all windows that need updating */ - update_prepare(); + // Return when there is nothing to do, screen updating is already + // happening (recursive call), messages on the screen or still starting up. + if (!doit + || updating_screen + || State == ASKMORE + || State == HITRETURN + || msg_scrolled + || starting) { + return; + } - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_redr_type != 0) { - win_update(wp); - } - if (wp->w_redr_status) { - win_redr_status(wp); - } + // update all windows that need updating + update_prepare(); + + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (wp->w_redr_type != 0) { + update_window_hl(wp, wp->w_redr_type >= NOT_VALID); + win_update(wp); + } + if (wp->w_redr_status) { + win_redr_status(wp, false); } + } - update_finish(); + update_finish(); } /* @@ -774,16 +787,18 @@ static void win_update(win_T *wp) } } - (void)hasFoldingWin(wp, mod_top, &mod_top, NULL, TRUE, NULL); - if (mod_top > lnumt) + (void)hasFoldingWin(wp, mod_top, &mod_top, NULL, true, NULL); + if (mod_top > lnumt) { mod_top = lnumt; + } - /* Now do the same for the bottom line (one above mod_bot). */ - --mod_bot; - (void)hasFoldingWin(wp, mod_bot, NULL, &mod_bot, TRUE, NULL); - ++mod_bot; - if (mod_bot < lnumb) + // Now do the same for the bottom line (one above mod_bot). + mod_bot--; + (void)hasFoldingWin(wp, mod_bot, NULL, &mod_bot, true, NULL); + mod_bot++; + if (mod_bot < lnumb) { mod_bot = lnumb; + } } /* When a change starts above w_topline and the end is below @@ -824,13 +839,6 @@ static void win_update(win_T *wp) type = VALID; } - /* Trick: we want to avoid clearing the screen twice. screenclear() will - * set "screen_cleared" to TRUE. The special value MAYBE (which is still - * non-zero and thus not FALSE) will indicate that screenclear() was not - * called. */ - if (screen_cleared) - screen_cleared = MAYBE; - /* * If there are no changes on the screen that require a complete redraw, * handle three cases: @@ -866,7 +874,7 @@ static void win_update(win_T *wp) ++j; if (j >= wp->w_height - 2) break; - (void)hasFoldingWin(wp, ln, NULL, &ln, TRUE, NULL); + (void)hasFoldingWin(wp, ln, NULL, &ln, true, NULL); } } else j = wp->w_lines[0].wl_lnum - wp->w_topline; @@ -876,15 +884,11 @@ static void win_update(win_T *wp) if (wp->w_lines[0].wl_lnum != wp->w_topline) i += diff_check_fill(wp, wp->w_lines[0].wl_lnum) - wp->w_old_topfill; - if (i < wp->w_height - 2) { /* less than a screen off */ - /* - * Try to insert the correct number of lines. - * If not the last window, delete the lines at the bottom. - * win_ins_lines may fail when the terminal can't do it. - */ - if (i > 0) - check_for_delay(FALSE); - if (win_ins_lines(wp, 0, i, FALSE, wp == firstwin) == OK) { + if (i < wp->w_height - 2) { // less than a screen off + // Try to insert the correct number of lines. + // If not the last window, delete the lines at the bottom. + // win_ins_lines may fail when the terminal can't do it. + if (win_ins_lines(wp, 0, i) == OK) { if (wp->w_lines_valid != 0) { /* Need to update rows that are new, stop at the * first one that scrolled down. */ @@ -942,11 +946,11 @@ static void win_update(win_T *wp) /* ... but don't delete new filler lines. */ row -= wp->w_topfill; if (row > 0) { - check_for_delay(FALSE); - if (win_del_lines(wp, 0, row, FALSE, wp == firstwin) == OK) + if (win_del_lines(wp, 0, row) == OK) { bot_start = wp->w_height - row; - else - mid_start = 0; /* redraw all lines */ + } else { + mid_start = 0; // redraw all lines + } } if ((row == 0 || bot_start < 999) && wp->w_lines_valid != 0) { /* @@ -978,35 +982,16 @@ static void win_update(win_T *wp) * when it won't get updated below. */ if (wp->w_p_diff && bot_start > 0) wp->w_lines[0].wl_size = - plines_win_nofill(wp, wp->w_topline, TRUE) + plines_win_nofill(wp, wp->w_topline, true) + wp->w_topfill; } } } - /* When starting redraw in the first line, redraw all lines. When - * there is only one window it's probably faster to clear the screen - * first. */ + // When starting redraw in the first line, redraw all lines. if (mid_start == 0) { mid_end = wp->w_height; - if (ONE_WINDOW) { - /* Clear the screen when it was not done by win_del_lines() or - * win_ins_lines() above, "screen_cleared" is FALSE or MAYBE - * then. */ - if (screen_cleared != TRUE) - screenclear(); - /* The screen was cleared, redraw the tab pages line. */ - if (redraw_tabline) - draw_tabline(); - } - } - - /* When win_del_lines() or win_ins_lines() caused the screen to be - * cleared (only happens for the first window) or when screenclear() - * was called directly above, "must_redraw" will have been set to - * NOT_VALID, need to reset it here to avoid redrawing twice. */ - if (screen_cleared == TRUE) - must_redraw = 0; + } } else { /* Not VALID or INVERTED: redraw all lines. */ mid_start = 0; @@ -1217,15 +1202,13 @@ static void win_update(win_T *wp) * with. It is used further down when the line doesn't fit. */ srow = row; - /* - * Update a line when it is in an area that needs updating, when it - * has changes or w_lines[idx] is invalid. - * bot_start may be halfway through a wrapped line after using - * win_del_lines(), check if the current line includes it. - * When syntax folding is being used, the saved syntax states will - * already have been updated, we can't see where the syntax state is - * the same again, just update until the end of the window. - */ + // Update a line when it is in an area that needs updating, when it + // has changes or w_lines[idx] is invalid. + // "bot_start" may be halfway a wrapped line after using + // win_del_lines(), check if the current line includes it. + // When syntax folding is being used, the saved syntax states will + // already have been updated, we can't see where the syntax state is + // the same again, just update until the end of the window. if (row < top_end || (row >= mid_start && row < mid_end) || top_to_mod @@ -1294,15 +1277,15 @@ static void win_update(win_T *wp) /* Able to count old number of rows: Count new window * rows, and may insert/delete lines */ j = idx; - for (l = lnum; l < mod_bot; ++l) { - if (hasFoldingWin(wp, l, NULL, &l, TRUE, NULL)) - ++new_rows; - else if (l == wp->w_topline) - new_rows += plines_win_nofill(wp, l, TRUE) - + wp->w_topfill; - else - new_rows += plines_win(wp, l, TRUE); - ++j; + for (l = lnum; l < mod_bot; l++) { + if (hasFoldingWin(wp, l, NULL, &l, true, NULL)) { + new_rows++; + } else if (l == wp->w_topline) { + new_rows += plines_win_nofill(wp, l, true) + wp->w_topfill; + } else { + new_rows += plines_win(wp, l, true); + } + j++; if (new_rows > wp->w_height - row - 2) { /* it's getting too much, must redraw the rest */ new_rows = 9999; @@ -1315,31 +1298,29 @@ static void win_update(win_T *wp) * remaining text or scrolling fails, must redraw the * rest. If scrolling works, must redraw the text * below the scrolled text. */ - if (row - xtra_rows >= wp->w_height - 2) + if (row - xtra_rows >= wp->w_height - 2) { mod_bot = MAXLNUM; - else { - check_for_delay(FALSE); - if (win_del_lines(wp, row, - -xtra_rows, FALSE, FALSE) == FAIL) + } else { + if (win_del_lines(wp, row, -xtra_rows) == FAIL) { mod_bot = MAXLNUM; - else - bot_start = wp->w_height + xtra_rows; + } else { + bot_start = wp->w_height + xtra_rows; + } } } else if (xtra_rows > 0) { /* May scroll text down. If there is not enough * remaining text of scrolling fails, must redraw the * rest. */ - if (row + xtra_rows >= wp->w_height - 2) + if (row + xtra_rows >= wp->w_height - 2) { mod_bot = MAXLNUM; - else { - check_for_delay(FALSE); - if (win_ins_lines(wp, row + old_rows, - xtra_rows, FALSE, FALSE) == FAIL) + } else { + if (win_ins_lines(wp, row + old_rows, xtra_rows) == FAIL) { mod_bot = MAXLNUM; - else if (top_end > row + old_rows) - /* Scrolled the part at the top that requires - * updating down. */ + } else if (top_end > row + old_rows) { + // Scrolled the part at the top that requires + // updating down. top_end += xtra_rows; + } } } @@ -1423,7 +1404,7 @@ static void win_update(win_T *wp) /* * Display one line. */ - row = win_line(wp, lnum, srow, wp->w_height, mod_top == 0); + row = win_line(wp, lnum, srow, wp->w_height, mod_top == 0, false); wp->w_lines[idx].wl_folded = FALSE; wp->w_lines[idx].wl_lastlnum = lnum; @@ -1432,12 +1413,17 @@ static void win_update(win_T *wp) } wp->w_lines[idx].wl_lnum = lnum; - wp->w_lines[idx].wl_valid = TRUE; - if (row > wp->w_height) { /* past end of screen */ - /* we may need the size of that too long line later on */ - if (dollar_vcol == -1) - wp->w_lines[idx].wl_size = plines_win(wp, lnum, TRUE); - ++idx; + wp->w_lines[idx].wl_valid = true; + + // Past end of the window or end of the screen. Note that after + // resizing wp->w_height may be end up too big. That's a problem + // elsewhere, but prevent a crash here. + if (row > wp->w_height || row + wp->w_winrow >= Rows) { + // we may need the size of that too long line later on + if (dollar_vcol == -1) { + wp->w_lines[idx].wl_size = plines_win(wp, lnum, true); + } + idx++; break; } if (dollar_vcol == -1) @@ -1445,7 +1431,13 @@ static void win_update(win_T *wp) ++idx; lnum += fold_count + 1; } else { - /* This line does not need updating, advance to the next one */ + if (wp->w_p_rnu) { + // 'relativenumber' set: The text doesn't need to be drawn, but + // the number column nearly always does. + (void)win_line(wp, lnum, srow, wp->w_height, true, true); + } + + // This line does not need to be drawn, advance to the next one. row += wp->w_lines[idx++].wl_size; if (row > wp->w_height) /* past end of screen */ break; @@ -1479,6 +1471,8 @@ static void win_update(win_T *wp) wp->w_empty_rows = 0; wp->w_filler_rows = 0; if (!eof && !didline) { + int at_attr = hl_combine_attr(wp->w_hl_attr_normal, + win_hl_attr(wp, HLF_AT)); if (lnum == wp->w_topline) { /* * Single line that does not fit! @@ -1493,12 +1487,11 @@ static void win_update(win_T *wp) int scr_row = wp->w_winrow + wp->w_height - 1; // Last line isn't finished: Display "@@@" in the last screen line. - screen_puts_len((char_u *)"@@", 2, scr_row, wp->w_wincol, - win_hl_attr(wp, HLF_AT)); + screen_puts_len((char_u *)"@@", 2, scr_row, wp->w_wincol, at_attr); screen_fill(scr_row, scr_row + 1, (int)wp->w_wincol + 2, (int)W_ENDCOL(wp), - '@', ' ', win_hl_attr(wp, HLF_AT)); + '@', ' ', at_attr); set_empty_rows(wp, srow); wp->w_botline = lnum; } else if (dy_flags & DY_LASTLINE) { // 'display' has "lastline" @@ -1506,7 +1499,7 @@ static void win_update(win_T *wp) screen_fill(wp->w_winrow + wp->w_height - 1, wp->w_winrow + wp->w_height, W_ENDCOL(wp) - 3, W_ENDCOL(wp), - '@', '@', win_hl_attr(wp, HLF_AT)); + '@', '@', at_attr); set_empty_rows(wp, srow); wp->w_botline = lnum; } else { @@ -1514,8 +1507,7 @@ static void win_update(win_T *wp) wp->w_botline = lnum; } } else { - draw_vsep_win(wp, row); - if (eof) { /* we hit the end of the file */ + if (eof) { // we hit the end of the file wp->w_botline = buf->b_ml.ml_line_count + 1; j = diff_check_fill(wp, wp->w_botline); if (j > 0 && !wp->w_botfill) { @@ -1539,6 +1531,10 @@ static void win_update(win_T *wp) win_draw_end(wp, fill_eob, ' ', row, wp->w_height, HLF_EOB); } + if (wp->w_redr_type >= REDRAW_TOP) { + draw_vsep_win(wp, 0); + } + /* Reset the type of redrawing required, the window has been updated. */ wp->w_redr_type = 0; wp->w_old_topfill = wp->w_topfill; @@ -1604,7 +1600,7 @@ static void win_draw_end(win_T *wp, int c1, int c2, int row, int endrow, hlf_T h # define FDC_OFF n int fdc = compute_foldcolumn(wp, 0); - int attr = win_hl_attr(wp, hl); + int attr = hl_combine_attr(wp->w_hl_attr_normal, win_hl_attr(wp, hl)); if (wp->w_p_rl) { // No check for cmdline window: should never be right-left. @@ -1705,6 +1701,56 @@ static int compute_foldcolumn(win_T *wp, int col) return fdc; } +/// Put a single char from an UTF-8 buffer into a line buffer. +/// +/// Handles composing chars and arabic shaping state. +static int line_putchar(LineState *s, schar_T *dest, int maxcells, bool rl) +{ + const char_u *p = s->p; + int cells = utf_ptr2cells(p); + int c_len = utfc_ptr2len(p); + int u8c, u8cc[MAX_MCO]; + if (cells > maxcells) { + return -1; + } + u8c = utfc_ptr2char(p, u8cc); + if (*p < 0x80 && u8cc[0] == 0) { + schar_from_ascii(dest[0], *p); + s->prev_c = u8c; + } else { + if (p_arshape && !p_tbidi && arabic_char(u8c)) { + // Do Arabic shaping. + int pc, pc1, nc; + int pcc[MAX_MCO]; + int firstbyte = *p; + + // The idea of what is the previous and next + // character depends on 'rightleft'. + if (rl) { + pc = s->prev_c; + pc1 = s->prev_c1; + nc = utf_ptr2char(p + c_len); + s->prev_c1 = u8cc[0]; + } else { + pc = utfc_ptr2char(p + c_len, pcc); + nc = s->prev_c; + pc1 = pcc[0]; + } + s->prev_c = u8c; + + u8c = arabic_shape(u8c, &firstbyte, &u8cc[0], pc, pc1, nc); + } else { + s->prev_c = u8c; + } + schar_from_cc(dest[0], u8c, u8cc); + } + if (cells > 1) { + dest[1][0] = 0; + } + s->p += c_len; + return cells; +} + /* * Display one folded line. */ @@ -1837,13 +1883,7 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T * Right-left text is put in columns 0 - number-col, normal text is put * in columns number-col - window-width. */ - int cells; - int u8c, u8cc[MAX_MCO]; int idx; - int c_len; - char_u *p; - int prev_c = 0; // previous Arabic character - int prev_c1 = 0; // first composing char for prev_c if (wp->w_p_rl) { idx = off; @@ -1851,50 +1891,20 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T idx = off + col; } - // Store multibyte characters in ScreenLines[] et al. correctly. - for (p = text; *p != NUL; ) { - cells = (*mb_ptr2cells)(p); - c_len = (*mb_ptr2len)(p); - if (col + cells > wp->w_width - (wp->w_p_rl ? col : 0)) { - break; - } - u8c = utfc_ptr2char(p, u8cc); - if (*p < 0x80 && u8cc[0] == 0) { - schar_from_ascii(ScreenLines[idx], *p); - prev_c = u8c; - } else { - if (p_arshape && !p_tbidi && arabic_char(u8c)) { - // Do Arabic shaping. - int pc, pc1, nc; - int pcc[MAX_MCO]; - int firstbyte = *p; - - // The idea of what is the previous and next - // character depends on 'rightleft'. - if (wp->w_p_rl) { - pc = prev_c; - pc1 = prev_c1; - nc = utf_ptr2char(p + c_len); - prev_c1 = u8cc[0]; - } else { - pc = utfc_ptr2char(p + c_len, pcc); - nc = prev_c; - pc1 = pcc[0]; - } - prev_c = u8c; + LineState s = LINE_STATE(text); - u8c = arabic_shape(u8c, &firstbyte, &u8cc[0], pc, pc1, nc); - } else { - prev_c = u8c; - } - schar_from_cc(ScreenLines[idx], u8c, u8cc); - } - if (cells > 1) { - ScreenLines[idx + 1][0] = 0; + while (*s.p != NUL) { + // TODO(bfredl): cargo-culted from the old Vim code: + // if(col + cells > wp->w_width - (wp->w_p_rl ? col : 0)) { break; } + // This is obvious wrong. If Vim ever fixes this, solve for "cells" again + // in the correct condition. + int maxcells = wp->w_width - col - (wp->w_p_rl ? col : 0); + int cells = line_putchar(&s, &ScreenLines[idx], maxcells, wp->w_p_rl); + if (cells == -1) { + break; } col += cells; idx += cells; - p += c_len; } /* Fill the rest of the line with the fold filler */ @@ -1991,7 +2001,7 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T } screen_line(row + wp->w_winrow, wp->w_wincol, wp->w_width, - wp->w_width, false, wp, 0); + wp->w_width, false, wp, wp->w_hl_attr_normal, false); /* * Update w_cline_height and w_cline_folded if the cursor line was @@ -2088,7 +2098,8 @@ win_line ( linenr_T lnum, int startrow, int endrow, - bool nochange /* not updating for changed text */ + bool nochange, // not updating for changed text + bool number_only // only update the number column ) { unsigned off; // offset in ScreenLines/ScreenAttrs @@ -2125,11 +2136,11 @@ win_line ( int n_skip = 0; /* nr of chars to skip for 'nowrap' */ - int fromcol, tocol; /* start/end of inverting */ - int fromcol_prev = -2; /* start of inverting after cursor */ - int noinvcur = FALSE; /* don't invert the cursor */ - pos_T *top, *bot; - int lnum_in_visual_area = FALSE; + int fromcol = 0, tocol = 0; // start/end of inverting + int fromcol_prev = -2; // start of inverting after cursor + int noinvcur = false; // don't invert the cursor + pos_T *top, *bot; + int lnum_in_visual_area = false; pos_T pos; long v; @@ -2180,17 +2191,16 @@ win_line ( match_T *shl; // points to search_hl or a match int shl_flag; // flag to indicate whether search_hl // has been processed or not - int prevcol_hl_flag; // flag to indicate whether prevcol + bool prevcol_hl_flag; // flag to indicate whether prevcol // equals startcol of search_hl or one // of the matches int prev_c = 0; // previous Arabic character int prev_c1 = 0; // first composing char for prev_c - int did_line_attr = 0; bool search_attr_from_match = false; // if search_attr is from :match - bool has_bufhl = false; // this buffer has highlight matches - int bufhl_attr = 0; // attributes desired by bufhl BufhlLineInfo bufhl_info; // bufhl data for this line + bool has_bufhl = false; // this buffer has highlight matches + bool do_virttext = false; // draw virtual text for this line /* draw_state: items that are drawn in sequence: */ #define WL_START 0 /* nothing done yet */ @@ -2232,156 +2242,165 @@ win_line ( row = startrow; screen_row = row + wp->w_winrow; - /* - * To speed up the loop below, set extra_check when there is linebreak, - * trailing white space and/or syntax processing to be done. - */ - extra_check = wp->w_p_lbr; - if (syntax_present(wp) && !wp->w_s->b_syn_error) { - /* Prepare for syntax highlighting in this line. When there is an - * error, stop syntax highlighting. */ - save_did_emsg = did_emsg; - did_emsg = FALSE; - syntax_start(wp, lnum); - if (did_emsg) - wp->w_s->b_syn_error = TRUE; - else { - did_emsg = save_did_emsg; - has_syntax = TRUE; - extra_check = TRUE; - } - } - - if (bufhl_start_line(wp->w_buffer, lnum, &bufhl_info)) { - has_bufhl = true; - extra_check = true; - } + if (!number_only) { + // To speed up the loop below, set extra_check when there is linebreak, + // trailing white space and/or syntax processing to be done. + extra_check = wp->w_p_lbr; + if (syntax_present(wp) && !wp->w_s->b_syn_error) { + // Prepare for syntax highlighting in this line. When there is an + // error, stop syntax highlighting. + save_did_emsg = did_emsg; + did_emsg = false; + syntax_start(wp, lnum); + if (did_emsg) { + wp->w_s->b_syn_error = true; + } else { + did_emsg = save_did_emsg; + has_syntax = true; + extra_check = true; + } + } - /* Check for columns to display for 'colorcolumn'. */ - color_cols = wp->w_buffer->terminal ? NULL : wp->w_p_cc_cols; - if (color_cols != NULL) - draw_color_col = advance_color_col(VCOL_HLC, &color_cols); - - if (wp->w_p_spell - && *wp->w_s->b_p_spl != NUL - && !GA_EMPTY(&wp->w_s->b_langp) - && *(char **)(wp->w_s->b_langp.ga_data) != NULL) { - /* Prepare for spell checking. */ - has_spell = true; - extra_check = TRUE; - - /* Get the start of the next line, so that words that wrap to the next - * line are found too: "et<line-break>al.". - * Trick: skip a few chars for C/shell/Vim comments */ - nextline[SPWORDLEN] = NUL; - if (lnum < wp->w_buffer->b_ml.ml_line_count) { - line = ml_get_buf(wp->w_buffer, lnum + 1, FALSE); - spell_cat_line(nextline + SPWORDLEN, line, SPWORDLEN); - } - - /* When a word wrapped from the previous line the start of the current - * line is valid. */ - if (lnum == checked_lnum) - cur_checked_col = checked_col; - checked_lnum = 0; - - /* When there was a sentence end in the previous line may require a - * word starting with capital in this line. In line 1 always check - * the first word. */ - if (lnum != capcol_lnum) - cap_col = -1; - if (lnum == 1) - cap_col = 0; - capcol_lnum = 0; - } + if (bufhl_start_line(wp->w_buffer, lnum, &bufhl_info)) { + if (kv_size(bufhl_info.line->items)) { + has_bufhl = true; + extra_check = true; + } + if (kv_size(bufhl_info.line->virt_text)) { + do_virttext = true; + } + } - /* - * handle visual active in this window - */ - fromcol = -10; - tocol = MAXCOL; - if (VIsual_active && wp->w_buffer == curwin->w_buffer) { - /* Visual is after curwin->w_cursor */ - if (ltoreq(curwin->w_cursor, VIsual)) { - top = &curwin->w_cursor; - bot = &VIsual; - } else { /* Visual is before curwin->w_cursor */ - top = &VIsual; - bot = &curwin->w_cursor; + // Check for columns to display for 'colorcolumn'. + color_cols = wp->w_buffer->terminal ? NULL : wp->w_p_cc_cols; + if (color_cols != NULL) { + draw_color_col = advance_color_col(VCOL_HLC, &color_cols); } - lnum_in_visual_area = (lnum >= top->lnum && lnum <= bot->lnum); - if (VIsual_mode == Ctrl_V) { /* block mode */ - if (lnum_in_visual_area) { - fromcol = wp->w_old_cursor_fcol; - tocol = wp->w_old_cursor_lcol; - } - } else { /* non-block mode */ - if (lnum > top->lnum && lnum <= bot->lnum) - fromcol = 0; - else if (lnum == top->lnum) { - if (VIsual_mode == 'V') /* linewise */ + + if (wp->w_p_spell + && *wp->w_s->b_p_spl != NUL + && !GA_EMPTY(&wp->w_s->b_langp) + && *(char **)(wp->w_s->b_langp.ga_data) != NULL) { + // Prepare for spell checking. + has_spell = true; + extra_check = true; + + // Get the start of the next line, so that words that wrap to the next + // line are found too: "et<line-break>al.". + // Trick: skip a few chars for C/shell/Vim comments + nextline[SPWORDLEN] = NUL; + if (lnum < wp->w_buffer->b_ml.ml_line_count) { + line = ml_get_buf(wp->w_buffer, lnum + 1, false); + spell_cat_line(nextline + SPWORDLEN, line, SPWORDLEN); + } + + // When a word wrapped from the previous line the start of the current + // line is valid. + if (lnum == checked_lnum) { + cur_checked_col = checked_col; + } + checked_lnum = 0; + + // When there was a sentence end in the previous line may require a + // word starting with capital in this line. In line 1 always check + // the first word. + if (lnum != capcol_lnum) { + cap_col = -1; + } + if (lnum == 1) { + cap_col = 0; + } + capcol_lnum = 0; + } + + // + // handle visual active in this window + // + fromcol = -10; + tocol = MAXCOL; + if (VIsual_active && wp->w_buffer == curwin->w_buffer) { + // Visual is after curwin->w_cursor + if (ltoreq(curwin->w_cursor, VIsual)) { + top = &curwin->w_cursor; + bot = &VIsual; + } else { // Visual is before curwin->w_cursor + top = &VIsual; + bot = &curwin->w_cursor; + } + lnum_in_visual_area = (lnum >= top->lnum && lnum <= bot->lnum); + if (VIsual_mode == Ctrl_V) { // block mode + if (lnum_in_visual_area) { + fromcol = wp->w_old_cursor_fcol; + tocol = wp->w_old_cursor_lcol; + } + } else { // non-block mode + if (lnum > top->lnum && lnum <= bot->lnum) { fromcol = 0; - else { - getvvcol(wp, top, (colnr_T *)&fromcol, NULL, NULL); - if (gchar_pos(top) == NUL) - tocol = fromcol + 1; + } else if (lnum == top->lnum) { + if (VIsual_mode == 'V') { // linewise + fromcol = 0; + } else { + getvvcol(wp, top, (colnr_T *)&fromcol, NULL, NULL); + if (gchar_pos(top) == NUL) { + tocol = fromcol + 1; + } + } } - } - if (VIsual_mode != 'V' && lnum == bot->lnum) { - if (*p_sel == 'e' && bot->col == 0 - && bot->coladd == 0 - ) { - fromcol = -10; - tocol = MAXCOL; - } else if (bot->col == MAXCOL) - tocol = MAXCOL; - else { - pos = *bot; - if (*p_sel == 'e') - getvvcol(wp, &pos, (colnr_T *)&tocol, NULL, NULL); - else { - getvvcol(wp, &pos, NULL, NULL, (colnr_T *)&tocol); - ++tocol; + if (VIsual_mode != 'V' && lnum == bot->lnum) { + if (*p_sel == 'e' && bot->col == 0 + && bot->coladd == 0) { + fromcol = -10; + tocol = MAXCOL; + } else if (bot->col == MAXCOL) { + tocol = MAXCOL; + } else { + pos = *bot; + if (*p_sel == 'e') { + getvvcol(wp, &pos, (colnr_T *)&tocol, NULL, NULL); + } else { + getvvcol(wp, &pos, NULL, NULL, (colnr_T *)&tocol); + tocol++; + } } } } - } - /* Check if the character under the cursor should not be inverted */ - if (!highlight_match && lnum == curwin->w_cursor.lnum && wp == curwin - ) - noinvcur = TRUE; + // Check if the character under the cursor should not be inverted + if (!highlight_match && lnum == curwin->w_cursor.lnum && wp == curwin) { + noinvcur = true; + } - /* if inverting in this line set area_highlighting */ - if (fromcol >= 0) { + // if inverting in this line set area_highlighting + if (fromcol >= 0) { + area_highlighting = true; + attr = win_hl_attr(wp, HLF_V); + } + // handle 'incsearch' and ":s///c" highlighting + } else if (highlight_match + && wp == curwin + && lnum >= curwin->w_cursor.lnum + && lnum <= curwin->w_cursor.lnum + search_match_lines) { + if (lnum == curwin->w_cursor.lnum) { + getvcol(curwin, &(curwin->w_cursor), + (colnr_T *)&fromcol, NULL, NULL); + } else { + fromcol = 0; + } + if (lnum == curwin->w_cursor.lnum + search_match_lines) { + pos.lnum = lnum; + pos.col = search_match_endcol; + getvcol(curwin, &pos, (colnr_T *)&tocol, NULL, NULL); + } else { + tocol = MAXCOL; + } + // do at least one character; happens when past end of line + if (fromcol == tocol) { + tocol = fromcol + 1; + } area_highlighting = true; - attr = win_hl_attr(wp, HLF_V); + attr = win_hl_attr(wp, HLF_I); } } - /* - * handle 'incsearch' and ":s///c" highlighting - */ - else if (highlight_match - && wp == curwin - && lnum >= curwin->w_cursor.lnum - && lnum <= curwin->w_cursor.lnum + search_match_lines) { - if (lnum == curwin->w_cursor.lnum) - getvcol(curwin, &(curwin->w_cursor), - (colnr_T *)&fromcol, NULL, NULL); - else - fromcol = 0; - if (lnum == curwin->w_cursor.lnum + search_match_lines) { - pos.lnum = lnum; - pos.col = search_match_endcol; - getvcol(curwin, &pos, (colnr_T *)&tocol, NULL, NULL); - } else - tocol = MAXCOL; - /* do at least one character; happens when past end of line */ - if (fromcol == tocol) - tocol = fromcol + 1; - area_highlighting = true; - attr = win_hl_attr(wp, HLF_I); - } filler_lines = diff_check(wp, lnum); if (filler_lines < 0) { @@ -2407,15 +2426,15 @@ win_line ( if (wp->w_p_cul && lnum == wp->w_cursor.lnum && !(wp == curwin && VIsual_active)) { int cul_attr = win_hl_attr(wp, HLF_CUL); - HlAttrs *aep = syn_cterm_attr2entry(cul_attr); + HlAttrs ae = syn_attr2entry(cul_attr); // We make a compromise here (#7383): // * low-priority CursorLine if fg is not set // * high-priority ("same as Vim" priority) CursorLine if fg is set - if (aep->rgb_fg_color == -1 && aep->cterm_fg_color == 0) { + if (ae.rgb_fg_color == -1 && ae.cterm_fg_color == 0) { line_attr_lowprio = cul_attr; } else { - if (line_attr != 0 && !(State & INSERT) && bt_quickfix(wp->w_buffer) + if (!(State & INSERT) && bt_quickfix(wp->w_buffer) && qf_current_entry(wp) == lnum) { line_attr = hl_combine_attr(cul_attr, line_attr); } else { @@ -2427,7 +2446,7 @@ win_line ( // If this line has a sign with line highlighting set line_attr. v = buf_getsigntype(wp->w_buffer, lnum, SIGN_LINEHL); if (v != 0) { - line_attr = sign_get_attr((int)v, true); + line_attr = sign_get_attr((int)v, SIGN_LINEHL); } // Highlight the current line in the quickfix window. @@ -2442,10 +2461,11 @@ win_line ( line = ml_get_buf(wp->w_buffer, lnum, FALSE); ptr = line; - if (has_spell) { - /* For checking first word with a capital skip white space. */ - if (cap_col == 0) - cap_col = (int)(skipwhite(line) - line); + if (has_spell && !number_only) { + // For checking first word with a capital skip white space. + if (cap_col == 0) { + cap_col = (int)getwhitecols(line); + } /* To be able to spell-check over line boundaries copy the end of the * current line into nextline[]. Above the start of the next line was @@ -2494,7 +2514,7 @@ win_line ( v = wp->w_skipcol; else v = wp->w_leftcol; - if (v > 0) { + if (v > 0 && !number_only) { char_u *prev_ptr = ptr; while (vcol < v && *ptr != NUL) { c = win_lbr_chartabsize(wp, line, ptr, (colnr_T)vcol, NULL); @@ -2603,7 +2623,7 @@ win_line ( */ cur = wp->w_match_head; shl_flag = false; - while (cur != NULL || !shl_flag) { + while ((cur != NULL || !shl_flag) && !number_only) { if (!shl_flag) { shl = &search_hl; shl_flag = true; @@ -2734,7 +2754,7 @@ win_line ( p_extra = extra; p_extra[n_extra] = NUL; } - char_attr = sign_get_attr(text_sign, FALSE); + char_attr = sign_get_attr(text_sign, SIGN_TEXT); } } } @@ -2781,12 +2801,17 @@ win_line ( c_extra = ' '; n_extra = number_width(wp) + 1; char_attr = win_hl_attr(wp, HLF_N); - // When 'cursorline' is set highlight the line number of - // the current line differently. - // TODO(vim): Can we use CursorLine instead of CursorLineNr - // when CursorLineNr isn't set? - if ((wp->w_p_cul || wp->w_p_rnu) - && lnum == wp->w_cursor.lnum) { + + int num_sign = buf_getsigntype(wp->w_buffer, lnum, SIGN_NUMHL); + if (num_sign != 0) { + // :sign defined with "numhl" highlight. + char_attr = sign_get_attr(num_sign, SIGN_NUMHL); + } else if ((wp->w_p_cul || wp->w_p_rnu) + && lnum == wp->w_cursor.lnum) { + // When 'cursorline' is set highlight the line number of + // the current line differently. + // TODO(vim): Can we use CursorLine instead of CursorLineNr + // when CursorLineNr isn't set? char_attr = win_hl_attr(wp, HLF_CLN); } } @@ -2807,7 +2832,7 @@ win_line ( // if need_showbreak is set, breakindent also applies if (wp->w_p_bri && (row != startrow || need_showbreak) && filler_lines == 0) { - char_attr = wp->w_hl_attr_normal; + char_attr = 0; if (diff_hlf != (hlf_T)0) { char_attr = win_hl_attr(wp, diff_hlf); @@ -2867,18 +2892,18 @@ win_line ( p_extra = saved_p_extra; char_attr = saved_char_attr; } else { - char_attr = wp->w_hl_attr_normal; + char_attr = 0; } } } - /* When still displaying '$' of change command, stop at cursor */ - if (dollar_vcol >= 0 && wp == curwin - && lnum == wp->w_cursor.lnum && vcol >= (long)wp->w_virtcol - && filler_todo <= 0 - ) { + // When still displaying '$' of change command, stop at cursor + if ((dollar_vcol >= 0 && wp == curwin + && lnum == wp->w_cursor.lnum && vcol >= (long)wp->w_virtcol + && filler_todo <= 0) + || (number_only && draw_state > WL_NR)) { screen_line(screen_row, wp->w_wincol, col, -wp->w_width, wp->w_p_rl, wp, - wp->w_hl_attr_normal); + wp->w_hl_attr_normal, false); // Pretend we have finished updating the window. Except when // 'cursorcolumn' is set. if (wp->w_p_cuc) { @@ -2892,16 +2917,17 @@ win_line ( if (draw_state == WL_LINE && area_highlighting) { /* handle Visual or match highlighting in this line */ if (vcol == fromcol - || (has_mbyte && vcol + 1 == fromcol && n_extra == 0 - && (*mb_ptr2cells)(ptr) > 1) + || (vcol + 1 == fromcol && n_extra == 0 + && utf_ptr2cells(ptr) > 1) || ((int)vcol_prev == fromcol_prev - && vcol_prev < vcol /* not at margin */ - && vcol < tocol)) - area_attr = attr; /* start highlighting */ - else if (area_attr != 0 - && (vcol == tocol - || (noinvcur && (colnr_T)vcol == wp->w_virtcol))) - area_attr = 0; /* stop highlighting */ + && vcol_prev < vcol // not at margin + && vcol < tocol)) { + area_attr = attr; // start highlighting + } else if (area_attr != 0 && (vcol == tocol + || (noinvcur + && (colnr_T)vcol == wp->w_virtcol))) { + area_attr = 0; // stop highlighting + } if (!n_extra) { /* @@ -3009,6 +3035,11 @@ win_line ( if (shl != &search_hl && cur != NULL) cur = cur->next; } + // Only highlight one character after the last column. + if (*ptr == NUL + && (wp->w_p_list && lcs_eol_one == -1)) { + search_attr = 0; + } } if (diff_hlf != (hlf_T)0) { @@ -3045,7 +3076,7 @@ win_line ( if (has_syntax) { char_attr = syntax_attr; } else { - char_attr = wp->w_hl_attr_normal; + char_attr = 0; } } } @@ -3284,8 +3315,7 @@ win_line ( did_emsg = FALSE; syntax_attr = get_syntax_attr((colnr_T)v - 1, - has_spell ? &can_spell : - NULL, FALSE); + has_spell ? &can_spell : NULL, false); if (did_emsg) { wp->w_s->b_syn_error = TRUE; @@ -3309,7 +3339,7 @@ win_line ( else syntax_flags = get_syntax_info(&syntax_seqnr); } else if (!attr_pri) { - char_attr = wp->w_hl_attr_normal; + char_attr = 0; } /* Check spelling (unless at the end of the line). @@ -3391,7 +3421,7 @@ win_line ( } if (has_bufhl && v > 0) { - bufhl_attr = bufhl_get_attr(&bufhl_info, (colnr_T)v); + int bufhl_attr = bufhl_get_attr(&bufhl_info, (colnr_T)v); if (bufhl_attr != 0) { if (!attr_pri) { char_attr = hl_combine_attr(char_attr, bufhl_attr); @@ -3408,7 +3438,7 @@ win_line ( // Found last space before word: check for line break. if (wp->w_p_lbr && c0 == c && vim_isbreak(c) && !vim_isbreak((int)(*ptr))) { - int mb_off = has_mbyte ? (*mb_head_off)(line, ptr - 1) : 0; + int mb_off = utf_head_off(line, ptr - 1); char_u *p = ptr - (mb_off + 1); // TODO: is passing p for start of the line OK? n_extra = win_lbr_chartabsize(wp, line, p, (colnr_T)vcol, NULL) - 1; @@ -3511,7 +3541,7 @@ win_line ( xfree(p_extra_free); p_extra_free = p; for (i = 0; i < tab_len; i++) { - mb_char2bytes(lcs_tab2, p); + utf_char2bytes(lcs_tab2, p); p += mb_char2len(lcs_tab2); n_extra += mb_char2len(lcs_tab2) - (saved_nextra > 0 ? 1: 0); } @@ -3645,32 +3675,6 @@ win_line ( (col < wp->w_width))) { c = ' '; ptr--; // put it back at the NUL - } else if ((diff_hlf != (hlf_T)0 || line_attr_lowprio || line_attr) - && (wp->w_p_rl - ? (col >= 0) - : (col - boguscols < wp->w_width))) { - // Highlight until the right side of the window - c = ' '; - ptr--; // put it back at the NUL - - // Remember we do the char for line highlighting. - did_line_attr++; - - // don't do search HL for the rest of the line - if ((line_attr_lowprio || line_attr) - && char_attr == search_attr && col > 0) { - char_attr = line_attr; - } - if (diff_hlf == HLF_TXD) { - diff_hlf = HLF_CHD; - if (attr == 0 || char_attr != attr) { - char_attr = win_hl_attr(wp, diff_hlf); - if (wp->w_p_cul && lnum == wp->w_cursor.lnum) { - char_attr = hl_combine_attr(char_attr, - win_hl_attr(wp, HLF_CUL)); - } - } - } } } @@ -3787,7 +3791,7 @@ win_line ( /* * At end of the text line or just after the last character. */ - if (c == NUL || did_line_attr == 1) { + if (c == NUL) { long prevcol = (long)(ptr - line) - (c == NUL); /* we're not really at that column when skipping some text */ @@ -3817,9 +3821,8 @@ win_line ( || lnum == VIsual.lnum || lnum == curwin->w_cursor.lnum) && c == NUL) - /* highlight 'hlsearch' match at end of line */ - || (prevcol_hl_flag == TRUE && did_line_attr <= 1) - )) { + // highlight 'hlsearch' match at end of line + || prevcol_hl_flag)) { int n = 0; if (wp->w_p_rl) { @@ -3863,10 +3866,11 @@ win_line ( } } - if (wp->w_hl_attr_normal != 0) { - char_attr = hl_combine_attr(wp->w_hl_attr_normal, char_attr); + int eol_attr = char_attr; + if (wp->w_p_cul && lnum == wp->w_cursor.lnum) { + eol_attr = hl_combine_attr(win_hl_attr(wp, HLF_CUL), eol_attr); } - ScreenAttrs[off] = char_attr; + ScreenAttrs[off] = eol_attr; if (wp->w_p_rl) { --col; --off; @@ -3877,25 +3881,12 @@ win_line ( ++vcol; eol_hl_off = 1; } - } - - /* - * At end of the text line. - */ - if (c == NUL) { - if (eol_hl_off > 0 && vcol - eol_hl_off == (long)wp->w_virtcol - && lnum == wp->w_cursor.lnum) { - /* highlight last char after line */ - --col; - --off; - --vcol; - } - - /* Highlight 'cursorcolumn' & 'colorcolumn' past end of the line. */ - if (wp->w_p_wrap) + // Highlight 'cursorcolumn' & 'colorcolumn' past end of the line. + if (wp->w_p_wrap) { v = wp->w_skipcol; - else + } else { v = wp->w_leftcol; + } /* check if line ends before left margin */ if (vcol < v + col - win_col_off(wp)) @@ -3913,40 +3904,105 @@ win_line ( && (int)wp->w_virtcol < wp->w_width * (row - startrow + 1) + v && lnum != wp->w_cursor.lnum) - || draw_color_col) - && !wp->w_p_rl - ) { + || draw_color_col || line_attr_lowprio || line_attr + || diff_hlf != (hlf_T)0 || do_virttext)) { int rightmost_vcol = 0; int i; - if (wp->w_p_cuc) + VirtText virt_text = do_virttext ? bufhl_info.line->virt_text + : (VirtText)KV_INITIAL_VALUE; + size_t virt_pos = 0; + LineState s = LINE_STATE((char_u *)""); + int virt_attr = 0; + + // Make sure alignment is the same regardless + // if listchars=eol:X is used or not. + bool delay_virttext = lcs_eol == lcs_eol_one && eol_hl_off == 0; + + if (wp->w_p_cuc) { rightmost_vcol = wp->w_virtcol; - if (draw_color_col) - /* determine rightmost colorcolumn to possibly draw */ - for (i = 0; color_cols[i] >= 0; ++i) - if (rightmost_vcol < color_cols[i]) + } + + if (draw_color_col) { + // determine rightmost colorcolumn to possibly draw + for (i = 0; color_cols[i] >= 0; i++) { + if (rightmost_vcol < color_cols[i]) { rightmost_vcol = color_cols[i]; + } + } + } int cuc_attr = win_hl_attr(wp, HLF_CUC); int mc_attr = win_hl_attr(wp, HLF_MC); - while (col < wp->w_width) { - schar_from_ascii(ScreenLines[off], ' '); - col++; + int diff_attr = 0; + if (diff_hlf == HLF_TXD) { + diff_hlf = HLF_CHD; + } + if (diff_hlf != 0) { + diff_attr = win_hl_attr(wp, diff_hlf); + } + + int base_attr = hl_combine_attr(line_attr_lowprio, diff_attr); + if (base_attr || line_attr) { + rightmost_vcol = INT_MAX; + } + + int col_stride = wp->w_p_rl ? -1 : 1; + + while (wp->w_p_rl ? col >= 0 : col < wp->w_width) { + int cells = -1; + if (do_virttext && !delay_virttext) { + if (*s.p == NUL) { + if (virt_pos < virt_text.size) { + s.p = (char_u *)kv_A(virt_text, virt_pos).text; + int hl_id = kv_A(virt_text, virt_pos).hl_id; + virt_attr = hl_id > 0 ? syn_id2attr(hl_id) : 0; + virt_pos++; + } else { + do_virttext = false; + } + } + if (*s.p != NUL) { + cells = line_putchar(&s, &ScreenLines[off], wp->w_width - col, + false); + } + } + delay_virttext = false; + + if (cells == -1) { + schar_from_ascii(ScreenLines[off], ' '); + cells = 1; + } + col += cells * col_stride; if (draw_color_col) { draw_color_col = advance_color_col(VCOL_HLC, &color_cols); } + int attr = base_attr; + if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol) { - ScreenAttrs[off++] = cuc_attr; + attr = cuc_attr; } else if (draw_color_col && VCOL_HLC == *color_cols) { - ScreenAttrs[off++] = mc_attr; - } else { - ScreenAttrs[off++] = wp->w_hl_attr_normal; + attr = mc_attr; + } + + if (do_virttext) { + attr = hl_combine_attr(attr, virt_attr); } - if (VCOL_HLC >= rightmost_vcol) + attr = hl_combine_attr(attr, line_attr); + + ScreenAttrs[off] = attr; + if (cells == 2) { + ScreenAttrs[off+1] = attr; + } + off += cells * col_stride; + + if (VCOL_HLC >= rightmost_vcol && *s.p == NUL + && virt_pos >= virt_text.size) { break; + } ++vcol; } @@ -3963,7 +4019,7 @@ win_line ( } } screen_line(screen_row, wp->w_wincol, col, wp->w_width, wp->w_p_rl, wp, - wp->w_hl_attr_normal); + wp->w_hl_attr_normal, false); row++; /* @@ -4171,8 +4227,22 @@ win_line ( || (wp->w_p_list && lcs_eol != NUL && p_extra != at_end_str) || (n_extra != 0 && (c_extra != NUL || *p_extra != NUL))) ) { + bool wrap = wp->w_p_wrap // Wrapping enabled. + && filler_todo <= 0 // Not drawing diff filler lines. + && lcs_eol_one != -1 // Haven't printed the lcs_eol character. + && row != endrow - 1 // Not the last line being displayed. + && wp->w_width == Columns // Window spans the width of the screen. + && !wp->w_p_rl; // Not right-to-left. screen_line(screen_row, wp->w_wincol, col - boguscols, - wp->w_width, wp->w_p_rl, wp, wp->w_hl_attr_normal); + wp->w_width, wp->w_p_rl, wp, wp->w_hl_attr_normal, wrap); + if (wrap) { + // Force a redraw of the first column of the next line. + ScreenAttrs[LineOffset[screen_row + 1]] = -1; + + // Remember that the line wraps, used for modeless copy. + LineWraps[screen_row] = true; + } + boguscols = 0; ++row; ++screen_row; @@ -4189,7 +4259,6 @@ win_line ( && filler_todo <= 0 ) { win_draw_end(wp, '@', ' ', row, wp->w_height, HLF_AT); - draw_vsep_win(wp, row); row = endrow; } @@ -4199,53 +4268,6 @@ win_line ( break; } - if (ui_current_row() == screen_row - 1 - && filler_todo <= 0 - && wp->w_width == Columns) { - /* Remember that the line wraps, used for modeless copy. */ - LineWraps[screen_row - 1] = TRUE; - - /* - * Special trick to make copy/paste of wrapped lines work with - * xterm/screen: write an extra character beyond the end of - * the line. This will work with all terminal types - * (regardless of the xn,am settings). - * Only do this if the cursor is on the current line - * (something has been written in it). - * Don't do this for double-width characters. - * Don't do this for a window not at the right screen border. - */ - if (!(has_mbyte - && ((*mb_off2cells)(LineOffset[screen_row], - LineOffset[screen_row] + screen_Columns) - == 2 - || (*mb_off2cells)(LineOffset[screen_row - 1] - + (int)Columns - 2, - LineOffset[screen_row] + screen_Columns) - == 2)) - ) { - /* First make sure we are at the end of the screen line, - * then output the same character again to let the - * terminal know about the wrap. If the terminal doesn't - * auto-wrap, we overwrite the character. */ - if (ui_current_col() != wp->w_width) - screen_char(LineOffset[screen_row - 1] - + (unsigned)Columns - 1, - screen_row - 1, (int)(Columns - 1)); - - /* When there is a multi-byte character, just output a - * space to keep it simple. */ - if (ScreenLines[LineOffset[screen_row - 1] - + (Columns - 1)][1] != 0) { - ui_putc(' '); - } else { - ui_puts(ScreenLines[LineOffset[screen_row - 1] + (Columns - 1)]); - } - /* force a redraw of the first char on the next line */ - ScreenAttrs[LineOffset[screen_row]] = (sattr_T)-1; - } - } - col = 0; off = (unsigned)(current_ScreenLine - ScreenLines); if (wp->w_p_rl) { @@ -4295,7 +4317,7 @@ static int char_needs_redraw(int off_from, int off_to, int cols) return (cols > 0 && ((schar_cmp(ScreenLines[off_from], ScreenLines[off_to]) || ScreenAttrs[off_from] != ScreenAttrs[off_to] - || ((*mb_off2cells)(off_from, off_from + cols) > 1 + || (utf_off2cells(off_from, off_from + cols) > 1 && schar_cmp(ScreenLines[off_from + 1], ScreenLines[off_to + 1]))) || p_wd < 0)); @@ -4311,24 +4333,23 @@ static int char_needs_redraw(int off_from, int off_to, int cols) * "rlflag" is TRUE in a rightleft window: * When TRUE and "clear_width" > 0, clear columns 0 to "endcol" * When FALSE and "clear_width" > 0, clear columns "endcol" to "clear_width" + * If "wrap" is true, then hint to the UI that "row" contains a line + * which has wrapped into the next row. */ -static void screen_line(int row, int coloff, int endcol, - int clear_width, int rlflag, win_T *wp, int bg_attr) +static void screen_line(int row, int coloff, int endcol, int clear_width, + int rlflag, win_T *wp, int bg_attr, bool wrap) { unsigned off_from; unsigned off_to; unsigned max_off_from; unsigned max_off_to; int col = 0; - int hl; - int force = FALSE; /* force update rest of the line */ - int redraw_this /* bool: does character need redraw? */ - ; - int redraw_next; /* redraw_this for next character */ - int clear_next = FALSE; - int char_cells; /* 1: normal char */ - /* 2: occupies two display cells */ -# define CHAR_CELLS char_cells + bool redraw_this; // Does character need redraw? + bool redraw_next; // redraw_this for next character + bool clear_next = false; + int char_cells; // 1: normal char + // 2: occupies two display cells + int start_dirty = -1, end_dirty = 0; /* Check for illegal row and col, just in case. */ if (row >= Rows) @@ -4336,7 +4357,6 @@ static void screen_line(int row, int coloff, int endcol, if (endcol > Columns) endcol = Columns; - off_from = (unsigned)(current_ScreenLine - ScreenLines); off_to = LineOffset[row] + coloff; max_off_from = off_from + screen_Columns; @@ -4373,28 +4393,31 @@ static void screen_line(int row, int coloff, int endcol, redraw_next = char_needs_redraw(off_from, off_to, endcol - col); while (col < endcol) { - if (has_mbyte && (col + 1 < endcol)) - char_cells = (*mb_off2cells)(off_from, max_off_from); - else - char_cells = 1; - + char_cells = 1; + if (col + 1 < endcol) { + char_cells = utf_off2cells(off_from, max_off_from); + } redraw_this = redraw_next; - redraw_next = force || char_needs_redraw(off_from + CHAR_CELLS, - off_to + CHAR_CELLS, endcol - col - CHAR_CELLS); - + redraw_next = char_needs_redraw(off_from + char_cells, + off_to + char_cells, + endcol - col - char_cells); if (redraw_this) { + if (start_dirty == -1) { + start_dirty = col; + } + end_dirty = col + char_cells; // When writing a single-width character over a double-width // character and at the end of the redrawn text, need to clear out // the right halve of the old character. // Also required when writing the right halve of a double-width // char over the left halve of an existing one - if (has_mbyte && col + char_cells == endcol + if (col + char_cells == endcol && ((char_cells == 1 - && (*mb_off2cells)(off_to, max_off_to) > 1) + && utf_off2cells(off_to, max_off_to) > 1) || (char_cells == 2 - && (*mb_off2cells)(off_to, max_off_to) == 1 - && (*mb_off2cells)(off_to + 1, max_off_to) > 1))) { + && utf_off2cells(off_to, max_off_to) == 1 + && utf_off2cells(off_to + 1, max_off_to) > 1))) { clear_next = true; } @@ -4404,58 +4427,63 @@ static void screen_line(int row, int coloff, int endcol, } ScreenAttrs[off_to] = ScreenAttrs[off_from]; - /* For simplicity set the attributes of second half of a - * double-wide character equal to the first half. */ - if (char_cells == 2) + // For simplicity set the attributes of second half of a + // double-wide character equal to the first half. + if (char_cells == 2) { ScreenAttrs[off_to + 1] = ScreenAttrs[off_from]; - - screen_char(off_to, row, col + coloff); + } } - off_to += CHAR_CELLS; - off_from += CHAR_CELLS; - col += CHAR_CELLS; + off_to += char_cells; + off_from += char_cells; + col += char_cells; } if (clear_next) { /* Clear the second half of a double-wide character of which the left * half was overwritten with a single-wide character. */ schar_from_ascii(ScreenLines[off_to], ' '); - screen_char(off_to, row, col + coloff); + end_dirty++; } + int clear_end = -1; if (clear_width > 0 && !rlflag) { // blank out the rest of the line - while (col < clear_width && ScreenLines[off_to][0] == ' ' - && ScreenLines[off_to][1] == NUL - && ScreenAttrs[off_to] == bg_attr - ) { - ++off_to; - ++col; - } - if (col < clear_width) { - screen_fill(row, row + 1, col + coloff, clear_width + coloff, ' ', ' ', - bg_attr); - off_to += clear_width - col; - col = clear_width; - } - } - - if (clear_width > 0) { - // For a window that's left of another, draw the separator char. - if (col + coloff < Columns && wp->w_vsep_width > 0) { - int c = fillchar_vsep(wp, &hl); - schar_T sc; - schar_from_char(sc, c); - - if (schar_cmp(ScreenLines[off_to], sc) - || ScreenAttrs[off_to] != hl) { - schar_copy(ScreenLines[off_to], sc); - ScreenAttrs[off_to] = hl; - screen_char(off_to, row, col + coloff); - } - } else - LineWraps[row] = FALSE; + // TODO(bfredl): we could cache winline widths + while (col < clear_width) { + if (ScreenLines[off_to][0] != ' ' || ScreenLines[off_to][1] != NUL + || ScreenAttrs[off_to] != bg_attr) { + ScreenLines[off_to][0] = ' '; + ScreenLines[off_to][1] = NUL; + ScreenAttrs[off_to] = bg_attr; + if (start_dirty == -1) { + start_dirty = col; + end_dirty = col; + } else if (clear_end == -1) { + end_dirty = endcol; + } + clear_end = col+1; + } + col++; + off_to++; + } + } + + if (clear_width > 0 || wp->w_width != Columns) { + // If we cleared after the end of the line, it did not wrap. + // For vsplit, line wrapping is not possible. + LineWraps[row] = false; + } + + if (clear_end < end_dirty) { + clear_end = end_dirty; + } + if (start_dirty == -1) { + start_dirty = end_dirty; + } + if (clear_end > start_dirty) { + ui_line(row, coloff+start_dirty, coloff+end_dirty, coloff+clear_end, + bg_attr, wrap); } } @@ -4489,14 +4517,18 @@ void status_redraw_all(void) } } -/* - * mark all status lines of the current buffer for redraw - */ +/// Marks all status lines of the current buffer for redraw. void status_redraw_curbuf(void) { + status_redraw_buf(curbuf); +} + +/// Marks all status lines of the specified buffer for redraw. +void status_redraw_buf(buf_T *buf) +{ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_status_height != 0 && wp->w_buffer == curbuf) { - wp->w_redr_status = TRUE; + if (wp->w_status_height != 0 && wp->w_buffer == buf) { + wp->w_redr_status = true; redraw_later(VALID); } } @@ -4509,7 +4541,7 @@ void redraw_statuslines(void) { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_redr_status) { - win_redr_status(wp); + win_redr_status(wp, false); } } if (redraw_tabline) @@ -4738,11 +4770,11 @@ win_redr_status_matches ( /* Put the wildmenu just above the command line. If there is * no room, scroll the screen one line up. */ if (cmdline_row == Rows - 1) { - screen_del_lines(0, 0, 1, (int)Rows, NULL); - ++msg_scrolled; + screen_del_lines(0, 1, (int)Rows, 0, (int)Columns); + msg_scrolled++; } else { - ++cmdline_row; - ++row; + cmdline_row++; + row++; } wild_menu_showing = WM_SCROLLED; } else { @@ -4776,7 +4808,9 @@ win_redr_status_matches ( /// Redraw the status line of window `wp`. /// /// If inversion is possible we use it. Else '=' characters are used. -static void win_redr_status(win_T *wp) +/// If "ignore_pum" is true, also redraw statusline when the popup menu is +/// displayed. +static void win_redr_status(win_T *wp, int ignore_pum) { int row; char_u *p; @@ -4799,7 +4833,7 @@ static void win_redr_status(win_T *wp) if (wp->w_status_height == 0) { // no status line, can only be last window redraw_cmdline = true; - } else if (!redrawing() || pum_drawn()) { + } else if (!redrawing() || (!ignore_pum && pum_drawn())) { // Don't redraw right now, do it later. Don't update status line when // popup menu is visible and may be drawn over it wp->w_redr_status = true; @@ -4851,8 +4885,8 @@ static void win_redr_status(win_T *wp) // Find first character that will fit. // Going from start to end is much faster for DBCS. for (i = 0; p[i] != NUL && clen >= this_ru_col - 1; - i += (*mb_ptr2len)(p + i)) { - clen -= (*mb_ptr2cells)(p + i); + i += utfc_ptr2len(p + i)) { + clen -= utf_ptr2cells(p + i); } len = clen; if (i > 0) { @@ -5098,14 +5132,16 @@ win_redr_custom ( /* fill up with "fillchar" */ while (width < maxwidth && len < (int)sizeof(buf) - 1) { - len += (*mb_char2bytes)(fillchar, buf + len); - ++width; + len += utf_char2bytes(fillchar, buf + len); + width++; } buf[len] = NUL; /* * Draw each snippet with the specified highlighting. */ + screen_puts_line_start(row); + curattr = attr; p = buf; for (n = 0; hltab[n].start != NULL; n++) { @@ -5126,6 +5162,8 @@ win_redr_custom ( // Make sure to use an empty string instead of p, if p is beyond buf + len. screen_puts(p >= buf + len ? (char_u *)"" : p, row, col, curattr); + screen_puts_line_flush(false); + if (wp == NULL) { // Fill the tab_page_click_defs array for clicking in the tab pages line. col = 0; @@ -5203,7 +5241,7 @@ void screen_putchar(int c, int row, int col, int attr) { char_u buf[MB_MAXBYTES + 1]; - buf[(*mb_char2bytes)(c, buf)] = NUL; + buf[utf_char2bytes(c, buf)] = NUL; screen_puts(buf, row, col, attr); } @@ -5223,7 +5261,6 @@ void screen_getbytes(int row, int col, char_u *bytes, int *attrp) } } - /* * Put string '*text' on the screen at position 'row' and 'col', with * attributes 'attr', and update ScreenLines[] and ScreenAttrs[]. @@ -5235,6 +5272,20 @@ void screen_puts(char_u *text, int row, int col, int attr) screen_puts_len(text, -1, row, col, attr); } +static int put_dirty_row = -1; +static int put_dirty_first = -1; +static int put_dirty_last = 0; + +/// Start a group of screen_puts_len calls that builds a single screen line. +/// +/// Must be matched with a screen_puts_line_flush call before moving to +/// another line. +void screen_puts_line_start(int row) +{ + assert(put_dirty_row == -1); + put_dirty_row = row; +} + /* * Like screen_puts(), but output "text[len]". When "len" is -1 output up to * a NUL. @@ -5258,6 +5309,16 @@ void screen_puts_len(char_u *text, int textlen, int row, int col, int attr) int force_redraw_next = FALSE; int need_redraw; + bool do_flush = false; + if (put_dirty_row == -1) { + screen_puts_line_start(row); + do_flush = true; + } else { + if (row != put_dirty_row) { + abort(); + } + } + if (ScreenLines == NULL || row >= screen_Rows) /* safety check */ return; off = LineOffset[row] + col; @@ -5268,9 +5329,12 @@ void screen_puts_len(char_u *text, int textlen, int row, int col, int attr) schar_from_ascii(ScreenLines[off - 1], ' '); ScreenAttrs[off - 1] = 0; // redraw the previous cell, make it empty - screen_char(off - 1, row, col - 1); - /* force the cell at "col" to be redrawn */ - force_redraw_next = TRUE; + if (put_dirty_first == -1) { + put_dirty_first = col-1; + } + put_dirty_last = col+1; + // force the cell at "col" to be redrawn + force_redraw_next = true; } max_off = LineOffset[row] + screen_Columns; @@ -5331,15 +5395,15 @@ void screen_puts_len(char_u *text, int textlen, int row, int col, int attr) // character with a one-cell character, need to clear the next // cell. Also when overwriting the left halve of a two-cell char // with the right halve of a two-cell char. Do this only once - // (mb_off2cells() may return 2 on the right halve). + // (utf8_off2cells() may return 2 on the right halve). if (clear_next_cell) { clear_next_cell = false; } else if ((len < 0 ? ptr[mbyte_blen] == NUL : ptr + mbyte_blen >= text + len) - && ((mbyte_cells == 1 && (*mb_off2cells)(off, max_off) > 1) + && ((mbyte_cells == 1 && utf_off2cells(off, max_off) > 1) || (mbyte_cells == 2 - && (*mb_off2cells)(off, max_off) == 1 - && (*mb_off2cells)(off + 1, max_off) > 1))) { + && utf_off2cells(off, max_off) == 1 + && utf_off2cells(off + 1, max_off) > 1))) { clear_next_cell = true; } @@ -5349,8 +5413,12 @@ void screen_puts_len(char_u *text, int textlen, int row, int col, int attr) ScreenLines[off + 1][0] = 0; ScreenAttrs[off + 1] = attr; } - screen_char(off, row, col); + if (put_dirty_first == -1) { + put_dirty_first = col; + } + put_dirty_last = col+mbyte_cells; } + off += mbyte_cells; col += mbyte_cells; ptr += mbyte_blen; @@ -5361,13 +5429,32 @@ void screen_puts_len(char_u *text, int textlen, int row, int col, int attr) } } - /* If we detected the next character needs to be redrawn, but the text - * doesn't extend up to there, update the character here. */ - if (force_redraw_next && col < screen_Columns) { - screen_char(off, row, col); + if (do_flush) { + screen_puts_line_flush(true); } } +/// End a group of screen_puts_len calls and send the screen buffer to the UI +/// layer. +/// +/// @param set_cursor Move the visible cursor to the end of the changed region. +/// This is a workaround for not yet refactored code paths +/// and shouldn't be used in new code. +void screen_puts_line_flush(bool set_cursor) +{ + assert(put_dirty_row != -1); + if (put_dirty_first != -1) { + if (set_cursor) { + ui_cursor_goto(put_dirty_row, put_dirty_last); + } + ui_line(put_dirty_row, put_dirty_first, put_dirty_last, put_dirty_last, 0, + false); + put_dirty_first = -1; + put_dirty_last = 0; + } + put_dirty_row = -1; +} + /* * Prepare for 'hlsearch' highlighting. */ @@ -5391,41 +5478,6 @@ static void end_search_hl(void) } } -static void update_window_hl(win_T *wp, bool invalid) -{ - if (!wp->w_hl_needs_update && !invalid) { - return; - } - wp->w_hl_needs_update = false; - - // determine window specific background set in 'winhighlight' - if (wp != curwin && wp->w_hl_ids[HLF_INACTIVE] > 0) { - wp->w_hl_attr_normal = syn_id2attr(wp->w_hl_ids[HLF_INACTIVE]); - } else if (wp->w_hl_id_normal > 0) { - wp->w_hl_attr_normal = syn_id2attr(wp->w_hl_id_normal); - } else { - wp->w_hl_attr_normal = 0; - } - if (wp != curwin) { - wp->w_hl_attr_normal = hl_combine_attr(HL_ATTR(HLF_INACTIVE), - wp->w_hl_attr_normal); - } - - for (int hlf = 0; hlf < (int)HLF_COUNT; hlf++) { - int attr; - if (wp->w_hl_ids[hlf] > 0) { - attr = syn_id2attr(wp->w_hl_ids[hlf]); - } else { - attr = HL_ATTR(hlf); - } - if (wp->w_hl_attr_normal != 0) { - attr = hl_combine_attr(wp->w_hl_attr_normal, attr); - } - wp->w_hl_attrs[hlf] = attr; - } -} - - /* * Init for calling prepare_search_hl(). @@ -5488,10 +5540,12 @@ static void prepare_search_hl(win_T *wp, linenr_T lnum) && re_multiline(shl->rm.regprog)) { if (shl->first_lnum == 0) { for (shl->first_lnum = lnum; - shl->first_lnum > wp->w_topline; --shl->first_lnum) - if (hasFoldingWin(wp, shl->first_lnum - 1, - NULL, NULL, TRUE, NULL)) + shl->first_lnum > wp->w_topline; + shl->first_lnum--) { + if (hasFoldingWin(wp, shl->first_lnum - 1, NULL, NULL, true, NULL)) { break; + } + } } if (cur != NULL) { cur->pos.cur = 0; @@ -5692,32 +5746,6 @@ next_search_hl_pos( return 0; } -/* - * Put character ScreenLines["off"] on the screen at position "row" and "col", - * using the attributes from ScreenAttrs["off"]. - */ -static void screen_char(unsigned off, int row, int col) -{ - // Check for illegal values, just in case (could happen just after resizing). - if (row >= screen_Rows || col >= screen_Columns) { - return; - } - - // Outputting the last character on the screen may scrollup the screen. - // Don't to it! Mark the character invalid (update it when scrolled up) - // FIXME: The premise here is not actually true (cf. deferred wrap). - if (row == screen_Rows - 1 && col == screen_Columns - 1 - // account for first command-line character in rightleft mode - && !cmdmsg_rl) { - ScreenAttrs[off] = (sattr_T)-1; - return; - } - - ui_cursor_goto(row, col); - ui_set_highlight(ScreenAttrs[off]); - - ui_puts(ScreenLines[off]); -} /* * Fill the screen from 'start_row' to 'end_row', from 'start_col' to 'end_col' @@ -5726,12 +5754,6 @@ static void screen_char(unsigned off, int row, int col) */ void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1, int c2, int attr) { - int row; - int col; - int off; - int end_off; - int did_delete; - int c; schar_T sc; if (end_row > screen_Rows) /* safety check */ @@ -5743,8 +5765,7 @@ void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1, || start_col >= end_col) /* nothing to do */ return; - /* it's a "normal" terminal when not in a GUI or cterm */ - for (row = start_row; row < end_row; ++row) { + for (int row = start_row; row < end_row; row++) { if (has_mbyte) { // When drawing over the right halve of a double-wide char clear // out the left halve. When drawing over the left halve of a @@ -5757,71 +5778,53 @@ void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1, screen_puts_len((char_u *)" ", 1, row, end_col, 0); } } - /* - * Try to use delete-line termcap code, when no attributes or in a - * "normal" terminal, where a bold/italic space is just a - * space. - */ - did_delete = FALSE; - if (c2 == ' ' - && end_col == Columns - && attr == 0) { - /* - * check if we really need to clear something - */ - col = start_col; - if (c1 != ' ') /* don't clear first char */ - ++col; - - off = LineOffset[row] + col; - end_off = LineOffset[row] + end_col; - // skip blanks (used often, keep it fast!) - while (off < end_off && ScreenLines[off][0] == ' ' - && ScreenLines[off][1] == 0 && ScreenAttrs[off] == 0) { - off++; - } - if (off < end_off) { // something to be cleared - col = off - LineOffset[row]; - ui_clear_highlight(); - ui_cursor_goto(row, col); // clear rest of this screen line - ui_call_eol_clear(); - col = end_col - col; - while (col--) { // clear chars in ScreenLines - schar_from_ascii(ScreenLines[off], ' '); - ScreenAttrs[off] = 0; - ++off; - } - } - did_delete = TRUE; /* the chars are cleared now */ - } - - off = LineOffset[row] + start_col; - c = c1; - schar_from_char(sc, c); + int dirty_first = INT_MAX; + int dirty_last = 0; + int col = start_col; + schar_from_char(sc, c1); + int lineoff = LineOffset[row]; for (col = start_col; col < end_col; col++) { + int off = lineoff + col; if (schar_cmp(ScreenLines[off], sc) || ScreenAttrs[off] != attr) { schar_copy(ScreenLines[off], sc); ScreenAttrs[off] = attr; - if (!did_delete || c != ' ') - screen_char(off, row, col); + if (dirty_first == INT_MAX) { + dirty_first = col; + } + dirty_last = col+1; } - ++off; if (col == start_col) { - if (did_delete) - break; - c = c2; - schar_from_char(sc, c); + schar_from_char(sc, c2); } } - if (end_col == Columns) - LineWraps[row] = FALSE; - if (row == Rows - 1) { /* overwritten the command line */ - redraw_cmdline = TRUE; - if (c1 == ' ' && c2 == ' ') - clear_cmdline = FALSE; /* command line has been cleared */ - if (start_col == 0) - mode_displayed = FALSE; /* mode cleared or overwritten */ + if (dirty_last > dirty_first) { + // TODO(bfredl): support a cleared suffix even with a batched line? + if (put_dirty_row == row) { + if (put_dirty_first == -1) { + put_dirty_first = dirty_first; + } + put_dirty_last = MAX(put_dirty_last, dirty_last); + } else { + int last = c2 != ' ' ? dirty_last : dirty_first + (c1 != ' '); + ui_line(row, dirty_first, last, dirty_last, attr, false); + } + } + + if (end_col == Columns) { + LineWraps[row] = false; + } + + // TODO(bfredl): The relevant caller should do this + if (row == Rows - 1) { // overwritten the command line + redraw_cmdline = true; + if (start_col == 0 && end_col == Columns + && c1 == ' ' && c2 == ' ' && attr == 0) { + clear_cmdline = false; // command line has been cleared + } + if (start_col == 0) { + mode_displayed = false; // mode cleared or overwritten + } } } } @@ -5870,7 +5873,6 @@ void screenalloc(bool doclear) int new_row, old_row; int len; static bool entered = false; // avoid recursiveness - static bool done_outofmem_msg = false; int retry_count = 0; retry: @@ -5940,67 +5942,39 @@ retry: win_alloc_lines(aucmd_win); } - if (new_ScreenLines == NULL - || new_ScreenAttrs == NULL - || new_LineOffset == NULL - || new_LineWraps == NULL - || new_tab_page_click_defs == NULL) { - if (ScreenLines != NULL || !done_outofmem_msg) { - // Guess the size. - do_outofmem_msg((Rows + 1) * Columns); - - // Remember we did this to avoid getting outofmem messages over - // and over again. - done_outofmem_msg = true; - } - xfree(new_ScreenLines); - new_ScreenLines = NULL; - xfree(new_ScreenAttrs); - new_ScreenAttrs = NULL; - xfree(new_LineOffset); - new_LineOffset = NULL; - xfree(new_LineWraps); - new_LineWraps = NULL; - xfree(new_tab_page_click_defs); - new_tab_page_click_defs = NULL; - } else { - done_outofmem_msg = FALSE; - - for (new_row = 0; new_row < Rows; ++new_row) { - new_LineOffset[new_row] = new_row * Columns; - new_LineWraps[new_row] = FALSE; - - /* - * If the screen is not going to be cleared, copy as much as - * possible from the old screen to the new one and clear the rest - * (used when resizing the window at the "--more--" prompt or when - * executing an external command, for the GUI). - */ - if (!doclear) { - for (int col = 0; col < Columns; col++) { - schar_from_ascii(new_ScreenLines[new_row * Columns + col], ' '); - } - memset(new_ScreenAttrs + new_row * Columns, - 0, (size_t)Columns * sizeof(*new_ScreenAttrs)); - old_row = new_row + (screen_Rows - Rows); - if (old_row >= 0 && ScreenLines != NULL) { - if (screen_Columns < Columns) - len = screen_Columns; - else - len = Columns; - - memmove(new_ScreenLines + new_LineOffset[new_row], - ScreenLines + LineOffset[old_row], - (size_t)len * sizeof(schar_T)); - memmove(new_ScreenAttrs + new_LineOffset[new_row], - ScreenAttrs + LineOffset[old_row], - (size_t)len * sizeof(new_ScreenAttrs[0])); + for (new_row = 0; new_row < Rows; new_row++) { + new_LineOffset[new_row] = new_row * Columns; + new_LineWraps[new_row] = false; + + // If the screen is not going to be cleared, copy as much as + // possible from the old screen to the new one and clear the rest + // (used when resizing the window at the "--more--" prompt or when + // executing an external command, for the GUI). + if (!doclear) { + for (int col = 0; col < Columns; col++) { + schar_from_ascii(new_ScreenLines[new_row * Columns + col], ' '); + } + memset(new_ScreenAttrs + new_row * Columns, + 0, (size_t)Columns * sizeof(*new_ScreenAttrs)); + old_row = new_row + (screen_Rows - Rows); + if (old_row >= 0 && ScreenLines != NULL) { + if (screen_Columns < Columns) { + len = screen_Columns; + } else { + len = Columns; } + + memmove(new_ScreenLines + new_LineOffset[new_row], + ScreenLines + LineOffset[old_row], + (size_t)len * sizeof(schar_T)); + memmove(new_ScreenAttrs + new_LineOffset[new_row], + ScreenAttrs + LineOffset[old_row], + (size_t)len * sizeof(new_ScreenAttrs[0])); } } - /* Use the last line of the screen for the current line. */ - current_ScreenLine = new_ScreenLines + Rows * Columns; } + // Use the last line of the screen for the current line. + current_ScreenLine = new_ScreenLines + Rows * Columns; free_screenlines(); @@ -6078,55 +6052,52 @@ static void screenclear2(void) return; } - ui_clear_highlight(); // don't want highlighting here - - /* blank out ScreenLines */ - for (i = 0; i < Rows; ++i) { - lineclear(LineOffset[i], (int)Columns); - LineWraps[i] = FALSE; + // blank out ScreenLines + for (i = 0; i < Rows; i++) { + lineclear(LineOffset[i], (int)Columns, true); + LineWraps[i] = false; } - ui_call_clear(); // clear the display + ui_call_grid_clear(1); // clear the display clear_cmdline = false; mode_displayed = false; - screen_cleared = true; // can use contents of ScreenLines now - win_rest_invalid(firstwin); - redraw_cmdline = TRUE; - redraw_tabline = TRUE; - if (must_redraw == CLEAR) /* no need to clear again */ - must_redraw = NOT_VALID; + redraw_all_later(NOT_VALID); + redraw_cmdline = true; + redraw_tabline = true; + if (must_redraw == CLEAR) { + must_redraw = NOT_VALID; // no need to clear again + } compute_cmdrow(); - msg_row = cmdline_row; /* put cursor on last line for messages */ + msg_row = cmdline_row; // put cursor on last line for messages msg_col = 0; - msg_scrolled = 0; /* can't scroll back */ - msg_didany = FALSE; - msg_didout = FALSE; + msg_scrolled = 0; // can't scroll back + msg_didany = false; + msg_didout = false; } /* * Clear one line in ScreenLines. */ -static void lineclear(unsigned off, int width) +static void lineclear(unsigned off, int width, bool valid) { for (int col = 0; col < width; col++) { schar_from_ascii(ScreenLines[off + col], ' '); } - (void)memset(ScreenAttrs + off, 0, (size_t)width * sizeof(sattr_T)); + int fill = valid ? 0 : -1; + (void)memset(ScreenAttrs + off, fill, (size_t)width * sizeof(sattr_T)); } -/* - * Copy part of a Screenline for vertically split window "wp". - */ -static void linecopy(int to, int from, win_T *wp) +/// Copy part of a Screenline for vertically split window. +static void linecopy(int to, int from, int col, int width) { - const unsigned off_to = LineOffset[to] + wp->w_wincol; - const unsigned off_from = LineOffset[from] + wp->w_wincol; + unsigned off_to = LineOffset[to] + col; + unsigned off_from = LineOffset[from] + col; memmove(ScreenLines + off_to, ScreenLines + off_from, - wp->w_width * sizeof(schar_T)); + width * sizeof(schar_T)); memmove(ScreenAttrs + off_to, ScreenAttrs + off_from, - wp->w_width * sizeof(ScreenAttrs[0])); + width * sizeof(sattr_T)); } /* @@ -6136,100 +6107,65 @@ void setcursor(void) { if (redrawing()) { validate_cursor(); + int left_offset = curwin->w_wcol; + if (curwin->w_p_rl) { + // With 'rightleft' set and the cursor on a double-wide character, + // position it on the leftmost column. + left_offset = curwin->w_width - curwin->w_wcol + - ((utf_ptr2cells(get_cursor_pos_ptr()) == 2 + && vim_isprintc(gchar_cursor())) ? 2 : 1); + } ui_cursor_goto(curwin->w_winrow + curwin->w_wrow, - curwin->w_wincol + ( - /* With 'rightleft' set and the cursor on a double-wide - * character, position it on the leftmost column. */ - curwin->w_p_rl ? (curwin->w_width - curwin->w_wcol - ( - (has_mbyte - && (*mb_ptr2cells)(get_cursor_pos_ptr()) == 2 - && vim_isprintc(gchar_cursor())) ? 2 : - 1)) : - curwin->w_wcol)); + curwin->w_wincol + left_offset); } } /// Insert 'line_count' lines at 'row' in window 'wp'. -/// If 'invalid' is TRUE the wp->w_lines[].wl_lnum is invalidated. -/// If 'mayclear' is TRUE the screen will be cleared if it is faster than -/// scrolling. /// Returns FAIL if the lines are not inserted, OK for success. -int win_ins_lines(win_T *wp, int row, int line_count, int invalid, int mayclear) +int win_ins_lines(win_T *wp, int row, int line_count) { - if (wp->w_height < 5) { - return FAIL; - } - - return win_do_lines(wp, row, line_count, invalid, mayclear, false); + return win_do_lines(wp, row, line_count, false); } /// Delete "line_count" window lines at "row" in window "wp". -/// If "invalid" is TRUE curwin->w_lines[] is invalidated. -/// If "mayclear" is TRUE the screen will be cleared if it is faster than -/// scrolling /// Return OK for success, FAIL if the lines are not deleted. -int win_del_lines(win_T *wp, int row, int line_count, int invalid, int mayclear) +int win_del_lines(win_T *wp, int row, int line_count) { - return win_do_lines(wp, row, line_count, invalid, mayclear, true); + return win_do_lines(wp, row, line_count, true); } // Common code for win_ins_lines() and win_del_lines(). // Returns OK or FAIL when the work has been done. -static int win_do_lines(win_T *wp, int row, int line_count, - int invalid, int mayclear, int del) +static int win_do_lines(win_T *wp, int row, int line_count, int del) { - if (invalid) { - wp->w_lines_valid = 0; - } - if (!redrawing() || line_count <= 0) { return FAIL; } - // only a few lines left: redraw is faster - if (mayclear && Rows - line_count < 5 && wp->w_width == Columns) { - screenclear(); /* will set wp->w_lines_valid to 0 */ - return FAIL; - } - - // Delete all remaining lines + // No lines are being moved, just draw over the entire area if (row + line_count >= wp->w_height) { - screen_fill(wp->w_winrow + row, wp->w_winrow + wp->w_height, - wp->w_wincol, W_ENDCOL(wp), - ' ', ' ', 0); return OK; } // when scrolling, the message on the command line should be cleared, // otherwise it will stay there forever. - clear_cmdline = TRUE; + check_for_delay(false); + clear_cmdline = true; int retval; - ui_set_scroll_region(wp, row); + if (del) { - retval = screen_del_lines(wp->w_winrow + row, 0, line_count, - wp->w_height - row, wp); + retval = screen_del_lines(wp->w_winrow + row, line_count, + wp->w_winrow + wp->w_height, + wp->w_wincol, wp->w_width); } else { - retval = screen_ins_lines(wp->w_winrow + row, 0, line_count, - wp->w_height - row, wp); + retval = screen_ins_lines(wp->w_winrow + row, line_count, + wp->w_winrow + wp->w_height, + wp->w_wincol, wp->w_width); } - ui_reset_scroll_region(); return retval; } /* - * window 'wp' and everything after it is messed up, mark it for redraw - */ -static void win_rest_invalid(win_T *wp) -{ - while (wp != NULL) { - redraw_win_later(wp, NOT_VALID); - wp->w_redr_status = TRUE; - wp = wp->w_next; - } - redraw_cmdline = TRUE; -} - -/* * The rest of the routines in this file perform screen manipulations. The * given operation is performed physically on the screen. The corresponding * change is also made to the internal screen image. In this way, the editor @@ -6240,19 +6176,13 @@ static void win_rest_invalid(win_T *wp) */ -// insert lines on the screen and update ScreenLines[] -// 'end' is the line after the scrolled part. Normally it is Rows. -// When scrolling region used 'off' is the offset from the top for the region. -// 'row' and 'end' are relative to the start of the region. -// -// return FAIL for failure, OK for success. -int screen_ins_lines ( - int off, - int row, - int line_count, - int end, - win_T *wp /* NULL or window to use width from */ -) +/// insert lines on the screen and update ScreenLines[] +/// 'end' is the line after the scrolled part. Normally it is Rows. +/// When scrolling region used 'off' is the offset from the top for the region. +/// 'row' and 'end' are relative to the start of the region. +/// +/// @return FAIL for failure, OK for success. +int screen_ins_lines(int row, int line_count, int end, int col, int width) { int i; int j; @@ -6264,18 +6194,16 @@ int screen_ins_lines ( // Shift LineOffset[] line_count down to reflect the inserted lines. // Clear the inserted lines in ScreenLines[]. - row += off; - end += off; - for (i = 0; i < line_count; ++i) { - if (wp != NULL && wp->w_width != Columns) { + for (i = 0; i < line_count; i++) { + if (width != Columns) { // need to copy part of a line j = end - 1 - i; while ((j -= line_count) >= row) { - linecopy(j + line_count, j, wp); + linecopy(j + line_count, j, col, width); } j += line_count; - lineclear(LineOffset[j] + wp->w_wincol, wp->w_width); - LineWraps[j] = FALSE; + lineclear(LineOffset[j] + col, width, false); + LineWraps[j] = false; } else { j = end - 1 - i; temp = LineOffset[j]; @@ -6284,29 +6212,23 @@ int screen_ins_lines ( LineWraps[j + line_count] = LineWraps[j]; } LineOffset[j + line_count] = temp; - LineWraps[j + line_count] = FALSE; - lineclear(temp, (int)Columns); + LineWraps[j + line_count] = false; + lineclear(temp, (int)Columns, false); } } - ui_call_scroll(-line_count); + ui_call_grid_scroll(1, row, end, col, col+width, -line_count, 0); return OK; } -// delete lines on the screen and update ScreenLines[] -// 'end' is the line after the scrolled part. Normally it is Rows. -// When scrolling region used 'off' is the offset from the top for the region. -// 'row' and 'end' are relative to the start of the region. -// -// Return OK for success, FAIL if the lines are not deleted. -int screen_del_lines ( - int off, - int row, - int line_count, - int end, - win_T *wp /* NULL or window to use width from */ -) +/// delete lines on the screen and update ScreenLines[] +/// 'end' is the line after the scrolled part. Normally it is Rows. +/// When scrolling region used 'off' is the offset from the top for the region. +/// 'row' and 'end' are relative to the start of the region. +/// +/// Return OK for success, FAIL if the lines are not deleted. +int screen_del_lines(int row, int line_count, int end, int col, int width) { int j; int i; @@ -6318,18 +6240,16 @@ int screen_del_lines ( // Now shift LineOffset[] line_count up to reflect the deleted lines. // Clear the inserted lines in ScreenLines[]. - row += off; - end += off; - for (i = 0; i < line_count; ++i) { - if (wp != NULL && wp->w_width != Columns) { + for (i = 0; i < line_count; i++) { + if (width != Columns) { // need to copy part of a line j = row + i; while ((j += line_count) <= end - 1) { - linecopy(j - line_count, j, wp); + linecopy(j - line_count, j, col, width); } j -= line_count; - lineclear(LineOffset[j] + wp->w_wincol, wp->w_width); - LineWraps[j] = FALSE; + lineclear(LineOffset[j] + col, width, false); + LineWraps[j] = false; } else { // whole width, moving the line pointers is faster j = row + i; @@ -6339,16 +6259,17 @@ int screen_del_lines ( LineWraps[j - line_count] = LineWraps[j]; } LineOffset[j - line_count] = temp; - LineWraps[j - line_count] = FALSE; - lineclear(temp, (int)Columns); + LineWraps[j - line_count] = false; + lineclear(temp, (int)Columns, false); } } - ui_call_scroll(line_count); + ui_call_grid_scroll(1, row, end, col, col+width, line_count, 0); return OK; } + /* * show the current mode and ruler * @@ -6982,26 +6903,20 @@ static void win_redr_ruler(win_T *wp, int always) if (this_ru_col + o < width) { // Need at least 3 chars left for get_rel_pos() + NUL. while (this_ru_col + o < width && RULER_BUF_LEN > i + 4) { - if (has_mbyte) - i += (*mb_char2bytes)(fillchar, buffer + i); - else - buffer[i++] = fillchar; - ++o; + i += utf_char2bytes(fillchar, buffer + i); + o++; } get_rel_pos(wp, buffer + i, RULER_BUF_LEN - i); } - /* Truncate at window boundary. */ - if (has_mbyte) { - o = 0; - for (i = 0; buffer[i] != NUL; i += (*mb_ptr2len)(buffer + i)) { - o += (*mb_ptr2cells)(buffer + i); - if (this_ru_col + o > width) { - buffer[i] = NUL; - break; - } + // Truncate at window boundary. + o = 0; + for (i = 0; buffer[i] != NUL; i += utfc_ptr2len(buffer + i)) { + o += utf_ptr2cells(buffer + i); + if (this_ru_col + o > width) { + buffer[i] = NUL; + break; } - } else if (this_ru_col + (int)STRLEN(buffer) > width) - buffer[width - this_ru_col] = NUL; + } screen_puts(buffer, row, this_ru_col + off, attr); i = redraw_cmdline; @@ -7128,11 +7043,12 @@ void screen_resize(int width, int height) update_topline(); if (pum_drawn()) { redraw_later(NOT_VALID); - ins_compl_show_pum(); /* This includes the redraw. */ - } else - update_screen(NOT_VALID); - if (redrawing()) + ins_compl_show_pum(); + } + update_screen(NOT_VALID); + if (redrawing()) { setcursor(); + } } } } |