aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/screen.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/screen.c')
-rw-r--r--src/nvim/screen.c274
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;