aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/drawscreen.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/drawscreen.c')
-rw-r--r--src/nvim/drawscreen.c325
1 files changed, 153 insertions, 172 deletions
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index cf8178a30f..15a7294496 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -97,7 +97,7 @@ typedef enum {
static bool redraw_popupmenu = false;
static bool msg_grid_invalid = false;
-static bool resizing = false;
+static bool resizing_autocmd = false;
static char *provider_err = NULL;
@@ -115,7 +115,7 @@ void conceal_check_cursor_line(void)
}
}
-/// Resize the screen to Rows and Columns.
+/// Resize default_grid to Rows and Columns.
///
/// Allocate default_grid.chars[] and other grid arrays.
///
@@ -125,19 +125,18 @@ void conceal_check_cursor_line(void)
/// default_grid.Columns to access items in default_grid.chars[]. Use Rows
/// and Columns for positioning text etc. where the final size of the screen is
/// needed.
-void screenalloc(void)
+bool default_grid_alloc(void)
{
+ static bool resizing = false;
+
// 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;
+ return false;
}
resizing = true;
- int retry_count = 0;
-
-retry:
// Allocation of the screen buffers is done only when the size changes and
// when Rows and Columns have been set and we have started doing full
// screen stuff.
@@ -148,24 +147,9 @@ retry:
|| Columns == 0
|| (!full_screen && default_grid.chars == NULL)) {
resizing = false;
- return;
- }
-
- // Note that the window sizes are updated before reallocating the arrays,
- // thus we must not redraw here!
- RedrawingDisabled++;
-
- // win_new_screensize will recompute floats position, but tell the
- // compositor to not redraw them yet
- ui_comp_set_screen_valid(false);
- if (msg_grid.chars) {
- msg_grid_invalid = true;
+ return false;
}
- win_new_screensize(); // fit the windows in the new sized screen
-
- comp_col(); // recompute columns for shown command and ruler
-
// We're changing the size of the screen.
// - Allocate new arrays for default_grid
// - Move lines from the old arrays into the new arrays, clear extra
@@ -193,26 +177,13 @@ retry:
default_grid.col_offset = 0;
default_grid.handle = DEFAULT_GRID_HANDLE;
- must_redraw = UPD_CLEAR; // need to clear the screen later
-
- RedrawingDisabled--;
-
- // Do not apply autocommands more than 3 times to avoid an endless loop
- // in case applying autocommands always changes Rows or Columns.
- if (starting == 0 && ++retry_count <= 3) {
- apply_autocmds(EVENT_VIMRESIZED, NULL, NULL, false, curbuf);
- // In rare cases, autocommands may have altered Rows or Columns,
- // jump back to check if we need to allocate the screen again.
- goto retry;
- }
-
resizing = false;
+ return true;
}
void screenclear(void)
{
check_for_delay(false);
- screenalloc(); // allocate screen buffers if size changed
int i;
@@ -281,13 +252,6 @@ void screen_resize(int width, int height)
return;
}
- // curwin->w_buffer can be NULL when we are closing a window and the
- // buffer has already been closed and removing a scrollbar causes a resize
- // event. Don't resize then, it will happen after entering another buffer.
- if (curwin->w_buffer == NULL) {
- return;
- }
-
resizing_screen = true;
Rows = height;
@@ -301,16 +265,54 @@ void screen_resize(int width, int height)
width = Columns;
p_lines = Rows;
p_columns = Columns;
- ui_call_grid_resize(1, width, height);
- /// The window layout used to be adjusted here, but it now happens in
- /// screenalloc() (also invoked from screenclear()). That is because the
- /// recursize "resizing_screen" check above may skip this, but not screenalloc().
+ // was invoked recursively from a VimResized autocmd, handled as a loop below
+ if (resizing_autocmd) {
+ return;
+ }
+
+ int retry_count = 0;
+ resizing_autocmd = true;
+
+ bool retry_resize = true;
+ while (retry_resize) {
+ retry_resize = default_grid_alloc();
+
+ // Do not apply autocommands more than 3 times to avoid an endless loop
+ // in case applying autocommands always changes Rows or Columns.
+ if (++retry_count > 3) {
+ break;
+ }
+
+ if (retry_resize) {
+ // In rare cases, autocommands may have altered Rows or Columns,
+ // retry to check if we need to allocate the screen again.
+ apply_autocmds(EVENT_VIMRESIZED, NULL, NULL, false, curbuf);
+ }
+ }
- if (State != MODE_ASKMORE && State != MODE_EXTERNCMD && State != MODE_CONFIRM) {
- screenclear();
+ resizing_autocmd = false;
+
+ ui_call_grid_resize(1, width, height);
+
+ // win_new_screensize will recompute floats position, but tell the
+ // compositor to not redraw them yet
+ ui_comp_set_screen_valid(false);
+ if (msg_grid.chars) {
+ msg_grid_invalid = true;
}
+ // Note that the window sizes are updated before reallocating the arrays,
+ // thus we must not redraw here!
+ RedrawingDisabled++;
+
+ win_new_screensize(); // fit the windows in the new sized screen
+
+ comp_col(); // recompute columns for shown command and ruler
+
+ RedrawingDisabled--;
+ redraw_all_later(UPD_CLEAR);
+
if (starting != NO_SCREEN) {
maketitle();
@@ -320,14 +322,11 @@ void screen_resize(int width, int height)
// We only redraw when it's needed:
// - While at the more prompt or executing an external command, don't
// redraw, but position the cursor.
- // - While editing the command line, only redraw that.
+ // - While editing the command line, only redraw that. TODO: lies
// - in Ex mode, don't redraw anything.
// - Otherwise, redraw right now, and position the cursor.
- // Always need to call update_screen() or screenalloc(), to make
- // sure Rows/Columns and the size of the screen is correct!
if (State == MODE_ASKMORE || State == MODE_EXTERNCMD || State == MODE_CONFIRM
|| exmode_active) {
- screenalloc();
if (msg_grid.chars) {
msg_grid_validate();
}
@@ -341,7 +340,7 @@ void screen_resize(int width, int height)
}
if (State & MODE_CMDLINE) {
redraw_popupmenu = false;
- update_screen(UPD_NOT_VALID);
+ update_screen();
redrawcmdline();
if (pum_drawn()) {
cmdline_pum_display(false);
@@ -350,12 +349,12 @@ void screen_resize(int width, int height)
update_topline(curwin);
if (pum_drawn()) {
// TODO(bfredl): ins_compl_show_pum wants to redraw the screen first.
- // For now make sure the nested update_screen(0) won't redraw the
+ // For now make sure the nested update_screen() won't redraw the
// pum at the old position. Try to untangle this later.
redraw_popupmenu = false;
ins_compl_show_pum();
}
- update_screen(UPD_NOT_VALID);
+ update_screen();
if (redrawing()) {
setcursor();
}
@@ -370,9 +369,7 @@ void screen_resize(int width, int height)
///
/// Most code shouldn't call this directly, rather use redraw_later() and
/// and redraw_all_later() to mark parts of the screen as needing a redraw.
-///
-/// @param type set to a UPD_NOT_VALID to force redraw of entire screen
-int update_screen(int type)
+int update_screen(void)
{
static bool did_intro = false;
bool is_stl_global = global_stl_height() > 0;
@@ -380,7 +377,7 @@ int update_screen(int type)
// Don't do anything if the screen structures are (not yet) valid.
// 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) {
+ if (resizing_autocmd || !default_grid.chars) {
return FAIL;
}
@@ -389,37 +386,25 @@ int update_screen(int type)
diff_redraw(true);
}
- if (must_redraw) {
- if (type < must_redraw) { // use maximal type
- type = must_redraw;
- }
-
- // must_redraw is reset here, so that when we run into some weird
- // reason to redraw while busy redrawing (e.g., asynchronous
- // scrolling), or update_topline() in win_update() will cause a
- // scroll, or a decoration provider requires a redraw, the screen
- // will be redrawn later or in win_update().
- must_redraw = 0;
- }
-
- // Need to update w_lines[].
- if (curwin->w_lines_valid == 0 && type < UPD_NOT_VALID) {
- type = UPD_NOT_VALID;
- }
-
// Postpone the redrawing when it's not needed and when being called
// recursively.
if (!redrawing() || updating_screen) {
- must_redraw = type;
- if (type > UPD_INVERTED_ALL) {
- curwin->w_lines_valid = 0; // don't use w_lines[].wl_size now
- }
return FAIL;
}
+
+ int type = must_redraw;
+
+ // must_redraw is reset here, so that when we run into some weird
+ // reason to redraw while busy redrawing (e.g., asynchronous
+ // scrolling), or update_topline() in win_update() will cause a
+ // scroll, or a decoration provider requires a redraw, the screen
+ // will be redrawn later or in win_update().
+ must_redraw = 0;
+
updating_screen = 1;
- display_tick++; // let syntax code know we're in a next round of
- // display updating
+ display_tick++; // let syntax code know we're in a next round of
+ // display updating
// Tricky: vim code can reset msg_scrolled behind our back, so need
// separate bookkeeping for now.
@@ -443,68 +428,47 @@ int update_screen(int type)
msg_grid.cols, false);
}
}
- if (msg_use_msgsep()) {
- msg_grid.throttled = false;
- // UPD_CLEAR is already handled
- if (type == UPD_NOT_VALID && !ui_has(kUIMultigrid) && msg_scrolled) {
- ui_comp_set_screen_valid(false);
- for (int i = valid; i < Rows - p_ch; i++) {
- grid_clear_line(&default_grid, default_grid.line_offset[i],
- Columns, false);
- }
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_floating) {
- continue;
- }
- if (W_ENDROW(wp) > valid) {
- wp->w_redr_type = MAX(wp->w_redr_type, UPD_NOT_VALID);
- }
- if (!is_stl_global && W_ENDROW(wp) + wp->w_status_height > valid) {
- wp->w_redr_status = true;
- }
- }
- if (is_stl_global && Rows - p_ch - 1 > valid) {
- curwin->w_redr_status = true;
- }
+ msg_grid.throttled = false;
+ bool was_invalidated = false;
+
+ // UPD_CLEAR is already handled
+ if (type == UPD_NOT_VALID && !ui_has(kUIMultigrid) && msg_scrolled) {
+ was_invalidated = ui_comp_set_screen_valid(false);
+ for (int i = valid; i < Rows - p_ch; i++) {
+ grid_clear_line(&default_grid, default_grid.line_offset[i],
+ Columns, false);
}
- msg_grid_set_pos(Rows - (int)p_ch, false);
- msg_grid_invalid = false;
- } else if (msg_scrolled > Rows - 5) { // clearing is faster
- type = UPD_CLEAR;
- } else if (type != UPD_CLEAR) {
- check_for_delay(false);
- grid_ins_lines(&default_grid, 0, msg_scrolled, Rows, 0, Columns);
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_floating) {
continue;
}
- if (wp->w_winrow < msg_scrolled) {
- if (W_ENDROW(wp) > msg_scrolled
- && wp->w_redr_type < UPD_REDRAW_TOP
- && wp->w_lines_valid > 0
- && wp->w_topline == wp->w_lines[0].wl_lnum) {
- wp->w_upd_rows = msg_scrolled - wp->w_winrow;
- wp->w_redr_type = UPD_REDRAW_TOP;
- } else {
- wp->w_redr_type = UPD_NOT_VALID;
- if (wp->w_winrow + wp->w_winbar_height <= msg_scrolled) {
- wp->w_redr_status = true;
- }
- }
+ if (W_ENDROW(wp) > valid) {
+ // TODO(bfredl): too pessimistic. type could be UPD_NOT_VALID
+ // only because windows that are above the separator.
+ wp->w_redr_type = MAX(wp->w_redr_type, UPD_NOT_VALID);
+ }
+ if (!is_stl_global && W_ENDROW(wp) + wp->w_status_height > valid) {
+ wp->w_redr_status = true;
}
}
- if (is_stl_global && Rows - p_ch - 1 <= msg_scrolled) {
+ if (is_stl_global && Rows - p_ch - 1 > valid) {
curwin->w_redr_status = true;
}
- redraw_cmdline = true;
- redraw_tabline = true;
+ }
+ msg_grid_set_pos(Rows - (int)p_ch, false);
+ msg_grid_invalid = false;
+ if (was_invalidated) {
+ // screen was only invalid for the msgarea part.
+ // @TODO(bfredl): using the same "valid" flag
+ // for both messages and floats moving is bit of a mess.
+ ui_comp_set_screen_valid(true);
}
msg_scrolled = 0;
msg_scrolled_at_flush = 0;
need_wait_return = false;
}
- win_ui_flush();
+ win_ui_flush(true);
msg_ext_check_clear();
// reset cmdline_row now (may have been changed temporarily)
@@ -518,7 +482,8 @@ int update_screen(int type)
}
if (type == UPD_CLEAR) { // first clear screen
- screenclear(); // will reset clear_cmdline
+ screenclear(); // will reset clear_cmdline
+ // and set UPD_NOT_VALID for each window
cmdline_screen_cleared(); // clear external cmdline state
type = UPD_NOT_VALID;
// must_redraw may be set indirectly, avoid another redraw later
@@ -528,16 +493,15 @@ int update_screen(int type)
default_grid.valid = true;
}
- // After disabling msgsep the grid might not have been deallocated yet,
- // hence we also need to check msg_grid.chars
- if (type == UPD_NOT_VALID && (msg_use_grid() || msg_grid.chars)) {
+ // might need to clear space on default_grid for the message area.
+ if (type == UPD_NOT_VALID && clear_cmdline && !ui_has(kUIMessages)) {
grid_fill(&default_grid, Rows - (int)p_ch, Rows, 0, Columns, ' ', ' ', 0);
}
ui_comp_set_screen_valid(true);
DecorProviders providers;
- decor_providers_start(&providers, type, &provider_err);
+ decor_providers_start(&providers, &provider_err);
// "start" callback could have changed highlights for global elements
if (win_check_ns_hl(NULL)) {
@@ -551,31 +515,14 @@ int update_screen(int type)
// Force redraw when width of 'number' or 'relativenumber' column
// changes.
+ // TODO(bfredl): special casing curwin here is SÅ JÄVLA BULL.
+ // Either this should be done for all windows or not at all.
if (curwin->w_redr_type < UPD_NOT_VALID
&& curwin->w_nrwidth != ((curwin->w_p_nu || curwin->w_p_rnu)
? number_width(curwin) : 0)) {
curwin->w_redr_type = UPD_NOT_VALID;
}
- // Only start redrawing if there is really something to do.
- if (type == UPD_INVERTED) {
- update_curswant();
- }
- if (curwin->w_redr_type < type
- && !((type == UPD_VALID
- && curwin->w_lines[0].wl_valid
- && curwin->w_topfill == curwin->w_old_topfill
- && curwin->w_botfill == curwin->w_old_botfill
- && curwin->w_topline == curwin->w_lines[0].wl_lnum)
- || (type == UPD_INVERTED
- && VIsual_active
- && curwin->w_old_cursor_lnum == curwin->w_cursor.lnum
- && curwin->w_old_visual_mode == VIsual_mode
- && (curwin->w_valid & VALID_VIRTCOL)
- && curwin->w_old_curswant == curwin->w_curswant))) {
- curwin->w_redr_type = type;
- }
-
// Redraw the tab pages line if needed.
if (redraw_tabline || type >= UPD_NOT_VALID) {
update_window_hl(curwin, type >= UPD_NOT_VALID);
@@ -746,6 +693,8 @@ void show_cursor_info(bool always)
if (!always && !redrawing()) {
return;
}
+
+ win_check_ns_hl(curwin);
if ((*p_stl != NUL || *curwin->w_p_stl != NUL)
&& (curwin->w_status_height || global_stl_height())) {
redraw_custom_statusline(curwin);
@@ -762,6 +711,7 @@ void show_cursor_info(bool always)
maketitle();
}
+ win_check_ns_hl(NULL);
// Redraw the tab pages line if needed.
if (redraw_tabline) {
draw_tabline();
@@ -993,6 +943,9 @@ win_update_start:
bool scrolled_down = false; // true when scrolled down when w_topline got smaller a bit
bool top_to_mod = false; // redraw above mod_top
+ int bot_scroll_start = 999; // first line that needs to be redrawn due to
+ // scrolling. only used for EOB
+
int row; // current window row to display
linenr_T lnum; // current buffer lnum to display
int idx; // current index in w_lines[]
@@ -1017,7 +970,9 @@ win_update_start:
type = wp->w_redr_type;
if (type >= UPD_NOT_VALID) {
+ // TODO(bfredl): should only be implied for CLEAR, not NOT_VALID!
wp->w_redr_status = true;
+
wp->w_lines_valid = 0;
}
@@ -1096,12 +1051,12 @@ win_update_start:
} else {
const matchitem_T *cur = wp->w_match_head;
while (cur != NULL) {
- if (cur->match.regprog != NULL
- && re_multiline(cur->match.regprog)) {
+ if (cur->mit_match.regprog != NULL
+ && re_multiline(cur->mit_match.regprog)) {
top_to_mod = true;
break;
}
- cur = cur->next;
+ cur = cur->mit_next;
}
}
}
@@ -1168,6 +1123,7 @@ win_update_start:
mod_bot = MAXLNUM;
}
}
+
wp->w_redraw_top = 0; // reset for next time
wp->w_redraw_bot = 0;
@@ -1238,6 +1194,7 @@ win_update_start:
// If not the last window, delete the lines at the bottom.
// win_ins_lines may fail when the terminal can't do it.
win_scroll_lines(wp, 0, i);
+ bot_scroll_start = 0;
if (wp->w_lines_valid != 0) {
// Need to update rows that are new, stop at the
// first one that scrolled down.
@@ -1298,6 +1255,7 @@ win_update_start:
if (row > 0) {
win_scroll_lines(wp, 0, -row);
bot_start = wp->w_grid.rows - row;
+ bot_scroll_start = bot_start;
}
if ((row == 0 || bot_start < 999) && wp->w_lines_valid != 0) {
// Skip the lines (below the deleted lines) that are still
@@ -1443,7 +1401,7 @@ win_update_start:
pos.lnum += cursor_above ? 1 : -1) {
colnr_T t;
- pos.col = (colnr_T)STRLEN(ml_get_buf(wp->w_buffer, pos.lnum, false));
+ pos.col = (colnr_T)strlen(ml_get_buf(wp->w_buffer, pos.lnum, false));
getvvcol(wp, &pos, NULL, NULL, &t);
if (toc < t) {
toc = t;
@@ -1675,6 +1633,7 @@ win_update_start:
// need to redraw until the end of the window.
// Inserting/deleting lines has no use.
bot_start = 0;
+ bot_scroll_start = 0;
} else {
// Able to count old number of rows: Count new window
// rows, and may insert/delete lines
@@ -1705,6 +1664,7 @@ win_update_start:
} else {
win_scroll_lines(wp, row, xtra_rows);
bot_start = wp->w_grid.rows + xtra_rows;
+ bot_scroll_start = bot_start;
}
} else if (xtra_rows > 0) {
// May scroll text down. If there is not enough
@@ -1714,6 +1674,7 @@ win_update_start:
mod_bot = MAXLNUM;
} else {
win_scroll_lines(wp, row + old_rows, xtra_rows);
+ bot_scroll_start = 0;
if (top_end > row + old_rows) {
// Scrolled the part at the top that requires
// updating down.
@@ -1885,24 +1846,28 @@ win_update_start:
wp->w_filler_rows = wp->w_grid.rows - srow;
} else if (dy_flags & DY_TRUNCATE) { // 'display' has "truncate"
int scr_row = wp->w_grid.rows - 1;
+ int symbol = wp->w_p_fcs_chars.lastline;
+ char fillbuf[12]; // 2 characters of 6 bytes
+ int charlen = utf_char2bytes(symbol, &fillbuf[0]);
+ utf_char2bytes(symbol, &fillbuf[charlen]);
// Last line isn't finished: Display "@@@" in the last screen line.
- grid_puts_len(&wp->w_grid, (char_u *)"@@", MIN(wp->w_grid.cols, 2), scr_row, 0, at_attr);
-
- grid_fill(&wp->w_grid, scr_row, scr_row + 1, 2, wp->w_grid.cols,
- '@', ' ', at_attr);
+ grid_puts_len(&wp->w_grid, fillbuf, MIN(wp->w_grid.cols, 2) * charlen, scr_row, 0, at_attr);
+ grid_fill(&wp->w_grid, scr_row, scr_row + 1, 2, wp->w_grid.cols, symbol, ' ', at_attr);
set_empty_rows(wp, srow);
wp->w_botline = lnum;
} else if (dy_flags & DY_LASTLINE) { // 'display' has "lastline"
int start_col = wp->w_grid.cols - 3;
+ int symbol = wp->w_p_fcs_chars.lastline;
// Last line isn't finished: Display "@@@" at the end.
grid_fill(&wp->w_grid, wp->w_grid.rows - 1, wp->w_grid.rows,
- MAX(start_col, 0), wp->w_grid.cols, '@', '@', at_attr);
+ MAX(start_col, 0), wp->w_grid.cols, symbol, symbol, at_attr);
set_empty_rows(wp, srow);
wp->w_botline = lnum;
} else {
- win_draw_end(wp, '@', ' ', true, srow, wp->w_grid.rows, HLF_AT);
+ win_draw_end(wp, wp->w_p_fcs_chars.lastline, ' ', true, srow, wp->w_grid.rows, HLF_AT);
+ set_empty_rows(wp, srow);
wp->w_botline = lnum;
}
} else {
@@ -1923,8 +1888,19 @@ win_update_start:
// Make sure the rest of the screen is blank.
// write the "eob" character from 'fillchars' to rows that aren't part
// of the file.
- win_draw_end(wp, wp->w_p_fcs_chars.eob, ' ', false, row, wp->w_grid.rows,
+ // TODO(bfredl): just keep track of the valid EOB area from last redraw?
+ int lastline = bot_scroll_start;
+ if (mid_end >= row) {
+ lastline = MIN(lastline, mid_start);
+ }
+ // if (mod_bot > buf->b_ml.ml_line_count + 1) {
+ if (mod_bot > buf->b_ml.ml_line_count) {
+ lastline = 0;
+ }
+
+ win_draw_end(wp, wp->w_p_fcs_chars.eob, ' ', false, MAX(lastline, row), wp->w_grid.rows,
HLF_EOB);
+ set_empty_rows(wp, row);
}
kvi_destroy(line_providers);
@@ -1983,7 +1959,7 @@ win_update_start:
}
}
-/// Redraw a window later, with update_screen(type).
+/// Redraw a window later, with wp->w_redr_type >= type.
///
/// Set must_redraw only if not already set to a higher value.
/// e.g. if must_redraw is UPD_CLEAR, type UPD_NOT_VALID will do nothing.
@@ -2036,12 +2012,14 @@ void redraw_buf_later(buf_T *buf, int type)
}
}
-void redraw_buf_line_later(buf_T *buf, linenr_T line)
+void redraw_buf_line_later(buf_T *buf, linenr_T line, bool force)
{
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer == buf
- && line >= wp->w_topline && line < wp->w_botline) {
- redrawWinline(wp, line);
+ if (wp->w_buffer == buf) {
+ redrawWinline(wp, MIN(line, buf->b_ml.ml_line_count));
+ if (force && line > buf->b_ml.ml_line_count) {
+ wp->w_redraw_bot = line;
+ }
}
}
}
@@ -2117,10 +2095,13 @@ void redraw_statuslines(void)
{
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_redr_status) {
+ win_check_ns_hl(wp);
win_redr_winbar(wp);
win_redr_status(wp);
}
}
+
+ win_check_ns_hl(NULL);
if (redraw_tabline) {
draw_tabline();
}