diff options
Diffstat (limited to 'src/nvim/screen.c')
| -rw-r--r-- | src/nvim/screen.c | 274 |
1 files changed, 154 insertions, 120 deletions
diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 84c3f169ef..99ccce1793 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -153,6 +153,8 @@ static bool conceal_cursor_used = false; static bool redraw_popupmenu = false; +static bool resizing = false; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "screen.c.generated.h" #endif @@ -169,6 +171,7 @@ void redraw_later(int type) } void redraw_win_later(win_T *wp, int type) + FUNC_ATTR_NONNULL_ALL { if (!exiting && wp->w_redr_type < type) { wp->w_redr_type = type; @@ -241,6 +244,7 @@ redrawWinline( win_T *wp, linenr_T lnum ) + FUNC_ATTR_NONNULL_ALL { if (lnum >= wp->w_topline && lnum < wp->w_botline) { @@ -269,14 +273,16 @@ void update_curbuf(int type) /// and redraw_all_later() to mark parts of the screen as needing a redraw. /// /// @param type set to a NOT_VALID to force redraw of entire screen -void update_screen(int type) +int update_screen(int type) { static int did_intro = FALSE; int did_one; // Don't do anything if the screen structures are (not yet) valid. - if (!default_grid.chars) { - return; + // A VimResized autocmd can invoke redrawing in the middle of a resize, + // which would bypass the checks in screen_resize for popupmenu etc. + if (!default_grid.chars || resizing) { + return FAIL; } if (must_redraw) { @@ -299,9 +305,10 @@ void update_screen(int type) if (!redrawing() || updating_screen) { redraw_later(type); /* remember type for next time */ must_redraw = type; - if (type > INVERTED_ALL) - curwin->w_lines_valid = 0; /* don't use w_lines[].wl_size now */ - return; + if (type > INVERTED_ALL) { + curwin->w_lines_valid = 0; // don't use w_lines[].wl_size now + } + return FAIL; } updating_screen = TRUE; @@ -336,8 +343,7 @@ void update_screen(int type) type = CLEAR; } else if (type != CLEAR) { check_for_delay(false); - grid_ins_lines(&default_grid, 0, msg_scrolled, (int)Rows, - 0, (int)Columns); + grid_ins_lines(&default_grid, 0, msg_scrolled, Rows, 0, Columns); FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_floating) { continue; @@ -482,13 +488,13 @@ void update_screen(int type) } end_search_hl(); + // May need to redraw the popup menu. - if (pum_drawn() && redraw_popupmenu) { + if (pum_drawn() && must_redraw_pum) { pum_redraw(); } send_grid_resize = false; - redraw_popupmenu = false; /* Reset b_mod_set flags. Going through all windows is probably faster * than going through all buffers (there could be many buffers). */ @@ -511,28 +517,30 @@ void update_screen(int type) // either cmdline is cleared, not drawn or mode is last drawn cmdline_was_last_drawn = false; + return OK; } -/* - * Return TRUE if the cursor line in window "wp" may be concealed, according - * to the 'concealcursor' option. - */ -int conceal_cursor_line(win_T *wp) +// Return true if the cursor line in window "wp" may be concealed, according +// to the 'concealcursor' option. +bool conceal_cursor_line(const win_T *wp) + FUNC_ATTR_NONNULL_ALL { int c; - if (*wp->w_p_cocu == NUL) - return FALSE; - if (get_real_state() & VISUAL) + if (*wp->w_p_cocu == NUL) { + return false; + } + if (get_real_state() & VISUAL) { c = 'v'; - else if (State & INSERT) + } else if (State & INSERT) { c = 'i'; - else if (State & NORMAL) + } else if (State & NORMAL) { c = 'n'; - else if (State & CMDLINE) + } else if (State & CMDLINE) { c = 'c'; - else - return FALSE; + } else { + return false; + } return vim_strchr(wp->w_p_cocu, c) != NULL; } @@ -554,7 +562,8 @@ void conceal_check_cursor_line(void) /// /// If true, both old and new cursorline will need /// need to be redrawn when moving cursor within windows. -bool win_cursorline_standout(win_T *wp) +bool win_cursorline_standout(const win_T *wp) + FUNC_ATTR_NONNULL_ALL { return wp->w_p_cul || (wp->w_p_cole > 0 && !conceal_cursor_line(wp)); } @@ -600,8 +609,7 @@ static void win_update(win_T *wp) updating. 999 when no bot area updating */ int scrolled_down = FALSE; /* TRUE when scrolled down when w_topline got smaller a bit */ - matchitem_T *cur; /* points to the match list */ - int top_to_mod = FALSE; /* redraw above mod_top */ + bool top_to_mod = false; // redraw above mod_top int row; /* current window row to display */ linenr_T lnum; /* current buffer lnum to display */ @@ -696,21 +704,20 @@ static void win_update(win_T *wp) if (mod_bot == 0 || mod_bot < buf->b_mod_bot) mod_bot = buf->b_mod_bot; - /* When 'hlsearch' is on and using a multi-line search pattern, a - * change in one line may make the Search highlighting in a - * previous line invalid. Simple solution: redraw all visible - * lines above the change. - * Same for a match pattern. - */ + // When 'hlsearch' is on and using a multi-line search pattern, a + // change in one line may make the Search highlighting in a + // previous line invalid. Simple solution: redraw all visible + // lines above the change. + // Same for a match pattern. if (search_hl.rm.regprog != NULL - && re_multiline(search_hl.rm.regprog)) - top_to_mod = TRUE; - else { - cur = wp->w_match_head; + && re_multiline(search_hl.rm.regprog)) { + top_to_mod = true; + } else { + const matchitem_T *cur = wp->w_match_head; while (cur != NULL) { if (cur->match.regprog != NULL && re_multiline(cur->match.regprog)) { - top_to_mod = TRUE; + top_to_mod = true; break; } cur = cur->next; @@ -1457,7 +1464,7 @@ static void win_update(win_T *wp) // Last line isn't finished: Display "@@@" in the last screen line. grid_puts_len(&wp->w_grid, (char_u *)"@@", 2, scr_row, 0, at_attr); - grid_fill(&wp->w_grid, scr_row, scr_row + 1, 2, (int)wp->w_grid.Columns, + grid_fill(&wp->w_grid, scr_row, scr_row + 1, 2, wp->w_grid.Columns, '@', ' ', at_attr); set_empty_rows(wp, srow); wp->w_botline = lnum; @@ -1468,7 +1475,7 @@ static void win_update(win_T *wp) set_empty_rows(wp, srow); wp->w_botline = lnum; } else { - win_draw_end(wp, '@', ' ', true, srow, wp->w_grid.Rows, at_attr); + win_draw_end(wp, '@', ' ', true, srow, wp->w_grid.Rows, HLF_AT); wp->w_botline = lnum; } } else { @@ -1584,6 +1591,7 @@ static int win_fill_end(win_T *wp, int c1, int c2, int off, int width, int row, static void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, int endrow, hlf_T hl) { + assert(hl >= 0 && hl < HLF_COUNT); int n = 0; if (draw_margin) { @@ -1709,7 +1717,6 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T int col; int txtcol; int off; - int ri; /* Build the fold line: * 1. Add the cmdwin_type for the command-line window @@ -1753,15 +1760,18 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T col += fdc; } -# define RL_MEMSET(p, v, l) if (wp->w_p_rl) { \ - for (ri = 0; ri < l; ri++) { \ - linebuf_attr[off + (wp->w_grid.Columns - (p) - (l)) + ri] = v; \ - } \ - } else { \ - for (ri = 0; ri < l; ri++) { \ - linebuf_attr[off + (p) + ri] = v; \ +# define RL_MEMSET(p, v, l) \ + do { \ + if (wp->w_p_rl) { \ + for (int ri = 0; ri < l; ri++) { \ + linebuf_attr[off + (wp->w_grid.Columns - (p) - (l)) + ri] = v; \ + } \ + } else { \ + for (int ri = 0; ri < l; ri++) { \ + linebuf_attr[off + (p) + ri] = v; \ + } \ } \ - } + } while (0) /* Set all attributes of the 'number' or 'relativenumber' column and the * text */ @@ -1775,7 +1785,9 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T if (len > len_max) { len = len_max; } - copy_text_attr(off + col, (char_u *)" ", len, + char_u space_buf[18] = " "; + assert((size_t)len_max <= sizeof(space_buf)); + copy_text_attr(off + col, space_buf, len, win_hl_attr(wp, HLF_FL)); col += len; } @@ -2061,7 +2073,8 @@ win_line ( int row; // row in the window, excl w_winrow ScreenGrid *grid = &wp->w_grid; // grid specfic to the window - char_u extra[18]; // line number and 'fdc' must fit in here + char_u extra[57]; // sign, line number and 'fdc' must + // fit in here int n_extra = 0; // number of extra chars char_u *p_extra = NULL; // string of extra chars, plus NUL char_u *p_extra_free = NULL; // p_extra needs to be freed @@ -2074,7 +2087,7 @@ win_line ( int lcs_eol_one = wp->w_p_lcs_chars.eol; // 'eol' until it's been used int lcs_prec_todo = wp->w_p_lcs_chars.prec; // 'prec' until it's been used - /* saved "extra" items for when draw_state becomes WL_LINE (again) */ + // saved "extra" items for when draw_state becomes WL_LINE (again) int saved_n_extra = 0; char_u *saved_p_extra = NULL; int saved_c_extra = 0; @@ -2712,15 +2725,24 @@ win_line ( sign_idx, count); if (text_sign != 0) { p_extra = sign_get_text(text_sign); - int symbol_blen = (int)STRLEN(p_extra); if (p_extra != NULL) { + int symbol_blen = (int)STRLEN(p_extra); + c_extra = NUL; c_final = NUL; + + // TODO(oni-link): Is sign text already extended to + // full cell width? + assert((size_t)win_signcol_width(wp) + >= mb_string2cells(p_extra)); // symbol(s) bytes + (filling spaces) (one byte each) n_extra = symbol_blen + (win_signcol_width(wp) - mb_string2cells(p_extra)); + + assert(sizeof(extra) > (size_t)symbol_blen); memset(extra, ' ', sizeof(extra)); - STRNCPY(extra, p_extra, STRLEN(p_extra)); + memcpy(extra, p_extra, symbol_blen); + p_extra = extra; p_extra[n_extra] = NUL; } @@ -3121,7 +3143,7 @@ win_line ( c = '>'; mb_c = c; mb_l = 1; - mb_utf8 = false; + (void)mb_l; multi_attr = win_hl_attr(wp, HLF_AT); // put the pointer back to output the double-width @@ -3132,9 +3154,9 @@ win_line ( n_extra -= mb_l - 1; p_extra += mb_l - 1; } - ++p_extra; + p_extra++; } - --n_extra; + n_extra--; } else { int c0; @@ -3753,15 +3775,14 @@ win_line ( n_attr3 = 1; } - /* - * At end of the text line or just after the last character. - */ + // At end of the text line or just after the last character. if (c == NUL) { - long prevcol = (long)(ptr - line) - (c == NUL); + long prevcol = (long)(ptr - line) - 1; - /* we're not really at that column when skipping some text */ - if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol) - ++prevcol; + // we're not really at that column when skipping some text + if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol) { + prevcol++; + } // Invert at least one char, used for Visual and empty line or // highlight match at end of line. If it's beyond the last @@ -3784,8 +3805,7 @@ win_line ( && ((area_attr != 0 && vcol == fromcol && (VIsual_mode != Ctrl_V || lnum == VIsual.lnum - || lnum == curwin->w_cursor.lnum) - && c == NUL) + || lnum == curwin->w_cursor.lnum)) // highlight 'hlsearch' match at end of line || prevcol_hl_flag)) { int n = 0; @@ -4012,7 +4032,7 @@ win_line ( && filler_todo <= 0 && (wp->w_p_rl ? col == 0 : col == grid->Columns - 1) && (*ptr != NUL - || (wp->w_p_list && lcs_eol_one > 0) + || lcs_eol_one > 0 || (n_extra && (c_extra != NUL || *p_extra != NUL)))) { c = wp->w_p_lcs_chars.ext; char_attr = win_hl_attr(wp, HLF_AT); @@ -4026,14 +4046,15 @@ win_line ( } } - /* advance to the next 'colorcolumn' */ - if (draw_color_col) + // advance to the next 'colorcolumn' + if (draw_color_col) { draw_color_col = advance_color_col(VCOL_HLC, &color_cols); + } - /* Highlight the cursor column if 'cursorcolumn' is set. But don't - * highlight the cursor position itself. - * Also highlight the 'colorcolumn' if it is different than - * 'cursorcolumn' */ + // Highlight the cursor column if 'cursorcolumn' is set. But don't + // highlight the cursor position itself. + // Also highlight the 'colorcolumn' if it is different than + // 'cursorcolumn' vcol_save_attr = -1; if (draw_state == WL_LINE && !lnum_in_visual_area && search_attr == 0 && area_attr == 0) { @@ -4052,10 +4073,8 @@ win_line ( char_attr = hl_combine_attr(line_attr_lowprio, char_attr); } - /* - * Store character to be displayed. - * Skip characters that are left of the screen for 'nowrap'. - */ + // Store character to be displayed. + // Skip characters that are left of the screen for 'nowrap'. vcol_prev = vcol; if (draw_state < WL_LINE || n_skip <= 0) { // @@ -4355,6 +4374,12 @@ static void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, screen_adjust_grid(&grid, &row, &coloff); + // Safety check. Avoids clang warnings down the call stack. + if (grid->chars == NULL || row >= grid->Rows || col >= grid->Columns) { + DLOG("invalid state, skipped"); + return; + } + off_from = 0; off_to = grid->line_offset[row] + coloff; max_off_from = linebuf_size; @@ -4550,17 +4575,21 @@ void redraw_statuslines(void) /* * Redraw all status lines at the bottom of frame "frp". */ -void win_redraw_last_status(frame_T *frp) +void win_redraw_last_status(const frame_T *frp) + FUNC_ATTR_NONNULL_ARG(1) { - if (frp->fr_layout == FR_LEAF) - frp->fr_win->w_redr_status = TRUE; - else if (frp->fr_layout == FR_ROW) { - for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) + if (frp->fr_layout == FR_LEAF) { + frp->fr_win->w_redr_status = true; + } else if (frp->fr_layout == FR_ROW) { + FOR_ALL_FRAMES(frp, frp->fr_child) { win_redraw_last_status(frp); - } else { /* frp->fr_layout == FR_COL */ + } + } else { + assert(frp->fr_layout == FR_COL); frp = frp->fr_child; - while (frp->fr_next != NULL) + while (frp->fr_next != NULL) { frp = frp->fr_next; + } win_redraw_last_status(frp); } } @@ -4641,7 +4670,7 @@ win_redr_status_matches ( int showtail ) { -#define L_MATCH(m) (showtail ? sm_gettail(matches[m]) : matches[m]) +#define L_MATCH(m) (showtail ? sm_gettail(matches[m], false) : matches[m]) int row; char_u *buf; int len; @@ -4798,7 +4827,7 @@ win_redr_status_matches ( grid_puts(&default_grid, selstart, row, selstart_col, HL_ATTR(HLF_WM)); } - grid_fill(&default_grid, row, row + 1, clen, (int)Columns, + grid_fill(&default_grid, row, row + 1, clen, Columns, fillchar, fillchar, attr); } @@ -5141,7 +5170,7 @@ win_redr_custom ( /* * Draw each snippet with the specified highlighting. */ - screen_puts_line_start(row); + grid_puts_line_start(&default_grid, row); curattr = attr; p = buf; @@ -5164,7 +5193,7 @@ win_redr_custom ( grid_puts(&default_grid, p >= buf + len ? (char_u *)"" : p, row, col, curattr); - grid_puts_line_flush(&default_grid, false); + grid_puts_line_flush(false); if (wp == NULL) { // Fill the tab_page_click_defs array for clicking in the tab pages line. @@ -5310,18 +5339,20 @@ void grid_puts(ScreenGrid *grid, char_u *text, int row, int col, int attr) grid_puts_len(grid, text, -1, row, col, attr); } +static ScreenGrid *put_dirty_grid = NULL; static int put_dirty_row = -1; static int put_dirty_first = INT_MAX; static int put_dirty_last = 0; -/// Start a group of screen_puts_len calls that builds a single screen line. +/// Start a group of grid_puts_len calls that builds a single grid line. /// -/// Must be matched with a screen_puts_line_flush call before moving to +/// Must be matched with a grid_puts_line_flush call before moving to /// another line. -void screen_puts_line_start(int row) +void grid_puts_line_start(ScreenGrid *grid, int row) { assert(put_dirty_row == -1); put_dirty_row = row; + put_dirty_grid = grid; } /// like grid_puts(), but output "text[len]". When "len" is -1 output up to @@ -5347,16 +5378,19 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, screen_adjust_grid(&grid, &row, &col); - // safety check - if (grid->chars == NULL || row >= grid->Rows || col >= grid->Columns) { + // Safety check. The check for negative row and column is to fix issue + // vim/vim#4102. TODO: find out why row/col could be negative. + if (grid->chars == NULL + || row >= grid->Rows || row < 0 + || col >= grid->Columns || col < 0) { return; } if (put_dirty_row == -1) { - screen_puts_line_start(row); + grid_puts_line_start(grid, row); do_flush = true; } else { - if (row != put_dirty_row) { + if (grid != put_dirty_grid || row != put_dirty_row) { abort(); } } @@ -5459,31 +5493,31 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, } if (do_flush) { - grid_puts_line_flush(grid, true); + grid_puts_line_flush(true); } } -/// End a group of screen_puts_len calls and send the screen buffer to the UI +/// End a group of grid_puts_len calls and send the screen buffer to the UI /// layer. /// -/// @param grid The grid which contains the buffer. /// @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 grid_puts_line_flush(ScreenGrid *grid, bool set_cursor) +void grid_puts_line_flush(bool set_cursor) { assert(put_dirty_row != -1); if (put_dirty_first < put_dirty_last) { if (set_cursor) { - ui_grid_cursor_goto(grid->handle, put_dirty_row, - MIN(put_dirty_last, grid->Columns-1)); + ui_grid_cursor_goto(put_dirty_grid->handle, put_dirty_row, + MIN(put_dirty_last, put_dirty_grid->Columns-1)); } - ui_line(grid, put_dirty_row, put_dirty_first, put_dirty_last, + ui_line(put_dirty_grid, put_dirty_row, put_dirty_first, put_dirty_last, put_dirty_last, 0, false); put_dirty_first = INT_MAX; put_dirty_last = 0; } put_dirty_row = -1; + put_dirty_grid = NULL; } /* @@ -5971,7 +6005,14 @@ void grid_assign_handle(ScreenGrid *grid) /// needed. void screenalloc(void) { - static bool entered = false; // avoid recursiveness + // It's possible that we produce an out-of-memory message below, which + // will cause this function to be called again. To break the loop, just + // return here. + if (resizing) { + return; + } + resizing = true; + int retry_count = 0; retry: @@ -5985,19 +6026,11 @@ retry: || Rows == 0 || Columns == 0 || (!full_screen && default_grid.chars == NULL)) { + resizing = false; return; } /* - * It's possible that we produce an out-of-memory message below, which - * will cause this function to be called again. To break the loop, just - * return here. - */ - if (entered) - return; - entered = TRUE; - - /* * Note that the window sizes are updated before reallocating the arrays, * thus we must not redraw here! */ @@ -6037,8 +6070,7 @@ retry: must_redraw = CLEAR; // need to clear the screen later - entered = FALSE; - --RedrawingDisabled; + RedrawingDisabled--; /* * Do not apply autocommands more than 3 times to avoid an endless loop @@ -6050,14 +6082,16 @@ retry: * jump back to check if we need to allocate the screen again. */ goto retry; } + + resizing = false; } void grid_alloc(ScreenGrid *grid, int rows, int columns, bool copy, bool valid) { int new_row; ScreenGrid new = *grid; - - size_t ncells = (size_t)((rows+1) * columns); + assert(rows >= 0 && columns >= 0); + size_t ncells = (size_t)rows * columns; new.chars = xmalloc(ncells * sizeof(schar_T)); new.attrs = xmalloc(ncells * sizeof(sattr_T)); new.line_offset = xmalloc((size_t)(rows * sizeof(unsigned))); @@ -6623,7 +6657,7 @@ static void recording_mode(int attr) /* * Draw the tab pages line at the top of the Vim window. */ -static void draw_tabline(void) +void draw_tabline(void) { int tabcount = 0; int tabwidth = 0; @@ -6781,13 +6815,11 @@ static void draw_tabline(void) c = '_'; else c = ' '; - grid_fill(&default_grid, 0, 1, col, (int)Columns, c, c, - attr_fill); + grid_fill(&default_grid, 0, 1, col, Columns, c, c, attr_fill); /* Put an "X" for closing the current tab if there are several. */ if (first_tabpage->tp_next != NULL) { - grid_putchar(&default_grid, 'X', 0, (int)Columns - 1, - attr_nosel); + grid_putchar(&default_grid, 'X', 0, Columns - 1, attr_nosel); tab_page_click_defs[Columns - 1] = (StlClickDefinition) { .type = kStlClickTabClose, .tabnr = 999, @@ -7150,6 +7182,8 @@ void screen_resize(int width, int height) check_shellsize(); height = Rows; width = Columns; + p_lines = Rows; + p_columns = Columns; ui_call_grid_resize(1, width, height); send_grid_resize = true; |
