aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbfredl <bjorn.linse@gmail.com>2023-09-20 10:08:05 +0200
committerbfredl <bjorn.linse@gmail.com>2023-09-29 15:38:25 +0200
commite33269578b5bea2528cc48afc5b009eb8d4ad1d6 (patch)
tree7f96465b64cd638d0b247932e7075a8ea587faf2
parentaf7d317f3ff31d5ac5d8724b5057a422e1451b54 (diff)
downloadrneovim-e33269578b5bea2528cc48afc5b009eb8d4ad1d6.tar.gz
rneovim-e33269578b5bea2528cc48afc5b009eb8d4ad1d6.tar.bz2
rneovim-e33269578b5bea2528cc48afc5b009eb8d4ad1d6.zip
refactor(grid): unify the two put-text-on-the-screen code paths
The screen grid refactors will continue until morale improves. Jokes aside, this is quite a central installment in the series. Before this refactor, there were two fundamentally distinct codepaths for getting some text on the screen: - the win_line() -> grid_put_linebuf() -> ui_line() call chain used for buffer text, with linebuf_char as a temporary scratch buffer - the grid_line_start/grid_line_puts/grid_line_flush() -> ui_line() path used for every thing else: statuslines, messages and the command line. Here the grid->chars[] array itself doubles as a scratch buffer. With this refactor, the later family of functions still exist, however they now as well render to linebuf_char just like win_line() did, and grid_put_linebuf() is called in the end to calculate delta changes. This means we don't need any duplicate logic for delta calculations anymore. Later down the line, it will be possible to share more logic operating on this scratch buffer, like doing 'rightleft' reversal and arabic shaping as a post-processing step.
-rw-r--r--src/nvim/cmdexpand.c2
-rw-r--r--src/nvim/drawline.c40
-rw-r--r--src/nvim/drawscreen.c49
-rw-r--r--src/nvim/grid.c345
-rw-r--r--src/nvim/normal.c2
-rw-r--r--src/nvim/popupmenu.c2
-rw-r--r--src/nvim/statusline.c6
7 files changed, 200 insertions, 246 deletions
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
index 290cc336b8..e5a97e28bb 100644
--- a/src/nvim/cmdexpand.c
+++ b/src/nvim/cmdexpand.c
@@ -608,7 +608,7 @@ static void redraw_wildmenu(expand_T *xp, int num_matches, char **matches, int m
grid_line_fill(clen, Columns, fillchar, attr);
- grid_line_flush(false);
+ grid_line_flush();
}
win_redraw_last_status(topframe);
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c
index 9da421e79b..5baaa913b3 100644
--- a/src/nvim/drawline.c
+++ b/src/nvim/drawline.c
@@ -1825,7 +1825,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
|| (number_only && wlv.draw_state > WL_STC))
&& wlv.filler_todo <= 0) {
draw_virt_text(wp, buf, win_col_offset, &wlv.col, wp->w_p_rl ? -1 : grid->cols, wlv.row);
- grid_put_linebuf(grid, wlv.row, 0, wlv.col, -grid->cols, wp->w_p_rl, wp, bg_attr, false);
+ win_put_linebuf(wp, wlv.row, 0, wlv.col, -grid->cols, bg_attr, false);
// Pretend we have finished updating the window. Except when
// 'cursorcolumn' is set.
if (wp->w_p_cuc) {
@@ -2956,7 +2956,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
wp->w_p_rl ? -1 : grid->cols, 0, wp->w_p_rl);
}
draw_virt_text(wp, buf, win_col_offset, &wlv.col, wp->w_p_rl ? -1 : grid->cols, wlv.row);
- grid_put_linebuf(grid, wlv.row, 0, wlv.col, grid->cols, wp->w_p_rl, wp, bg_attr, false);
+ win_put_linebuf(wp, wlv.row, 0, wlv.col, grid->cols, bg_attr, false);
wlv.row++;
// Update w_cline_height and w_cline_folded if the cursor line was
@@ -3229,7 +3229,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
draw_virt_text(wp, buf, win_col_offset, &draw_col, wp->w_p_rl ? -1 : grid->cols, wlv.row);
}
- grid_put_linebuf(grid, wlv.row, 0, draw_col, grid->cols, wp->w_p_rl, wp, bg_attr, wrap);
+ win_put_linebuf(wp, wlv.row, 0, draw_col, grid->cols, bg_attr, wrap);
if (wrap) {
ScreenGrid *current_grid = grid;
int current_row = wlv.row, dummy_col = 0; // dummy_col unused
@@ -3297,3 +3297,37 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
xfree(wlv.saved_p_extra_free);
return wlv.row;
}
+
+static void win_put_linebuf(win_T *wp, int row, int coloff, int endcol, int clear_width,
+ int bg_attr, bool wrap)
+{
+ ScreenGrid *grid = &wp->w_grid;
+
+ // Take care of putting "<<<" on the first line for 'smoothscroll'.
+ if (row == 0 && wp->w_skipcol > 0
+ // do not overwrite the 'showbreak' text with "<<<"
+ && *get_showbreak_value(wp) == NUL
+ // do not overwrite the 'listchars' "precedes" text with "<<<"
+ && !(wp->w_p_list && wp->w_p_lcs_chars.prec != 0)) {
+ int off = 0;
+ if (wp->w_p_nu && wp->w_p_rnu) {
+ // do not overwrite the line number, change "123 text" to "123<<<xt".
+ while (off < grid->cols && ascii_isdigit(schar_get_ascii(linebuf_char[off]))) {
+ off++;
+ }
+ }
+
+ for (int i = 0; i < 3 && off < grid->cols; i++) {
+ if (off + 1 < grid->cols && linebuf_char[off + 1] == NUL) {
+ // When the first half of a double-width character is
+ // overwritten, change the second half to a space.
+ linebuf_char[off + 1] = schar_from_ascii(' ');
+ }
+ linebuf_char[off] = schar_from_ascii('<');
+ linebuf_attr[off] = HL_ATTR(HLF_AT);
+ off++;
+ }
+ }
+
+ grid_put_linebuf(grid, row, coloff, 0, endcol, clear_width, wp->w_p_rl, bg_attr, wrap, false);
+}
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index f65146fd16..06e4cd48e7 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -771,20 +771,20 @@ static void win_redr_border(win_T *wp)
if (adj[1]) {
grid_line_put_schar(icol + adj[3], chars[2], attrs[2]);
}
- grid_line_flush(false);
+ grid_line_flush();
}
for (int i = 0; i < irow; i++) {
if (adj[3]) {
grid_line_start(grid, i + adj[0]);
grid_line_put_schar(0, chars[7], attrs[7]);
- grid_line_flush(false);
+ grid_line_flush();
}
if (adj[1]) {
int ic = (i == 0 && !adj[0] && chars[2]) ? 2 : 3;
grid_line_start(grid, i + adj[0]);
grid_line_put_schar(icol + adj[3], chars[ic], attrs[ic]);
- grid_line_flush(false);
+ grid_line_flush();
}
}
@@ -807,7 +807,7 @@ static void win_redr_border(win_T *wp)
if (adj[1]) {
grid_line_put_schar(icol + adj[3], chars[4], attrs[4]);
}
- grid_line_flush(false);
+ grid_line_flush();
}
}
@@ -1098,7 +1098,7 @@ int showmode(void)
&& !(p_ch == 0 && !ui_has(kUIMessages))) {
grid_line_start(&msg_grid_adj, Rows - 1);
win_redr_ruler(ruler_win);
- grid_line_flush(false);
+ grid_line_flush();
}
redraw_cmdline = false;
@@ -1370,25 +1370,25 @@ static void draw_sep_connectors_win(win_T *wp)
bool bot_left = !(win_at_bottom || win_at_left);
bool bot_right = !(win_at_bottom || win_at_right);
- if (top_left || top_right) {
+ if (top_left) {
grid_line_start(&default_grid, wp->w_winrow - 1);
- if (top_left) {
- grid_line_put_schar(wp->w_wincol - 1, get_corner_sep_connector(wp, WC_TOP_LEFT), hl);
- }
- if (top_right) {
- grid_line_put_schar(W_ENDCOL(wp), get_corner_sep_connector(wp, WC_TOP_RIGHT), hl);
- }
- grid_line_flush(false);
+ grid_line_put_schar(wp->w_wincol - 1, get_corner_sep_connector(wp, WC_TOP_LEFT), hl);
+ grid_line_flush();
+ }
+ if (top_right) {
+ grid_line_start(&default_grid, wp->w_winrow - 1);
+ grid_line_put_schar(W_ENDCOL(wp), get_corner_sep_connector(wp, WC_TOP_RIGHT), hl);
+ grid_line_flush();
}
- if (bot_left || bot_right) {
+ if (bot_left) {
grid_line_start(&default_grid, W_ENDROW(wp));
- if (bot_left) {
- grid_line_put_schar(wp->w_wincol - 1, get_corner_sep_connector(wp, WC_BOTTOM_LEFT), hl);
- }
- if (bot_right) {
- grid_line_put_schar(W_ENDCOL(wp), get_corner_sep_connector(wp, WC_BOTTOM_RIGHT), hl);
- }
- grid_line_flush(false);
+ grid_line_put_schar(wp->w_wincol - 1, get_corner_sep_connector(wp, WC_BOTTOM_LEFT), hl);
+ grid_line_flush();
+ }
+ if (bot_right) {
+ grid_line_start(&default_grid, W_ENDROW(wp));
+ grid_line_put_schar(W_ENDCOL(wp), get_corner_sep_connector(wp, WC_BOTTOM_RIGHT), hl);
+ grid_line_flush();
}
}
@@ -2394,8 +2394,11 @@ static void win_update(win_T *wp, DecorProviders *providers)
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, symbol, symbol, at_attr);
+ // TODO(bfredl): this display ">@@@" when ">" was a left-halve
+ // maybe "@@@@" is preferred when this happens.
+ grid_line_start(&wp->w_grid, wp->w_grid.rows - 1);
+ grid_line_fill(MAX(start_col, 0), wp->w_grid.cols, symbol, at_attr);
+ grid_line_flush();
set_empty_rows(wp, srow);
wp->w_botline = lnum;
} else {
diff --git a/src/nvim/grid.c b/src/nvim/grid.c
index da72054540..1d9cd80ec3 100644
--- a/src/nvim/grid.c
+++ b/src/nvim/grid.c
@@ -190,21 +190,16 @@ void grid_invalidate(ScreenGrid *grid)
(void)memset(grid->attrs, -1, sizeof(sattr_T) * (size_t)grid->rows * (size_t)grid->cols);
}
-bool grid_invalid_row(ScreenGrid *grid, int row)
+static bool grid_invalid_row(ScreenGrid *grid, int row)
{
return grid->attrs[grid->line_offset[row]] < 0;
}
-static int line_off2cells(schar_T *line, size_t off, size_t max_off)
-{
- return (off + 1 < max_off && line[off + 1] == 0) ? 2 : 1;
-}
-
/// Return number of display cells for char at grid->chars[off].
/// We make sure that the offset used is less than "max_off".
static int grid_off2cells(ScreenGrid *grid, size_t off, size_t max_off)
{
- return line_off2cells(grid->chars, off, max_off);
+ return (off + 1 < max_off && grid->chars[off + 1] == 0) ? 2 : 1;
}
/// Return true if the character at "row"/"col" on the screen is the left side
@@ -261,18 +256,12 @@ void grid_getbytes(ScreenGrid *grid, int row, int col, char *bytes, int *attrp)
schar_get(bytes, grid->chars[off]);
}
-static bool check_grid(ScreenGrid *grid, int row, int col)
-{
- grid_adjust(&grid, &row, &col);
- // Safety check. The check for negative row and column is to fix issue
- // vim/vim#4102. TODO(neovim): find out why row/col could be negative.
- if (grid->chars == NULL
- || row >= grid->rows || row < 0
- || col >= grid->cols || col < 0) {
- return false;
- }
- return true;
-}
+static ScreenGrid *grid_line_grid = NULL;
+static int grid_line_row = -1;
+static int grid_line_coloff = 0;
+static int grid_line_maxcol = 0;
+static int grid_line_first = INT_MAX;
+static int grid_line_last = 0;
/// put string 'text' on the window grid at position 'row' and 'col', with
/// attributes 'attr', and update contents of 'grid'
@@ -280,26 +269,32 @@ static bool check_grid(ScreenGrid *grid, int row, int col)
/// Note: only outputs within one row!
int grid_puts(ScreenGrid *grid, const char *text, int textlen, int row, int col, int attr)
{
- if (!check_grid(grid, row, col)) {
+ grid_line_start(grid, row);
+
+ // Safety check. The check for negative row and column is to fix issue
+ // vim/vim#4102. TODO(neovim): find out why row/col could be negative.
+ int off_col = grid_line_coloff + col;
+ if (grid_line_grid->chars == NULL
+ || grid_line_row >= grid_line_grid->rows || grid_line_row < 0
+ || off_col >= grid_line_grid->cols || off_col < 0) {
if (rdb_flags & RDB_INVALID) {
abort();
+ } else {
+ grid_line_grid = NULL;
+ return 0;
}
- return 0;
}
- grid_line_start(grid, row);
int len = grid_line_puts(col, text, textlen, attr);
- grid_line_flush(true);
+ if (grid_line_last > grid_line_first) {
+ // TODO(bfredl): this is bullshit. message.c should manage its own cursor movements
+ int col_pos = MIN(grid_line_coloff + grid_line_last, grid_line_grid->cols - 1);
+ ui_grid_cursor_goto(grid_line_grid->handle, grid_line_row, col_pos);
+ }
+ grid_line_flush();
return len;
}
-static ScreenGrid *grid_line_grid = NULL;
-static int grid_line_row = -1;
-static int grid_line_coloff = 0;
-static int grid_line_first = INT_MAX;
-static int grid_line_last = 0;
-static bool grid_line_was_invalid = false;
-
/// Start a group of grid_line_puts calls that builds a single grid line.
///
/// Must be matched with a grid_line_flush call before moving to
@@ -308,56 +303,46 @@ void grid_line_start(ScreenGrid *grid, int row)
{
int col = 0;
grid_adjust(&grid, &row, &col);
- assert(grid_line_row == -1);
+ assert(grid_line_grid == NULL);
grid_line_row = row;
grid_line_grid = grid;
grid_line_coloff = col;
- // TODO(bfredl): ugly hackaround, will be fixed in STAGE 2
- grid_line_was_invalid = grid != &default_grid && grid_invalid_row(grid, row);
+ grid_line_first = (int)linebuf_size;
+ grid_line_maxcol = grid->cols - grid_line_coloff;
+ grid_line_last = 0;
}
void grid_line_put_schar(int col, schar_T schar, int attr)
{
- assert(grid_line_row >= 0);
- ScreenGrid *grid = grid_line_grid;
+ assert(grid_line_grid);
- size_t off = grid->line_offset[grid_line_row] + (size_t)col;
- if (grid->attrs[off] != attr || grid->chars[off] != schar || rdb_flags & RDB_NODELTA) {
- grid->chars[off] = schar;
- grid->attrs[off] = attr;
+ linebuf_char[col] = schar;
+ linebuf_attr[col] = attr;
- grid_line_first = MIN(grid_line_first, col);
- // TODO(bfredl): Y U NO DOUBLEWIDTH?
- grid_line_last = MAX(grid_line_last, col + 1);
- }
- grid->vcols[off] = -1;
+ grid_line_first = MIN(grid_line_first, col);
+ // TODO(bfredl): Y U NO DOUBLEWIDTH?
+ grid_line_last = MAX(grid_line_last, col + 1);
+ linebuf_vcol[col] = -1;
}
/// like grid_puts(), but output "text[len]". When "len" is -1 output up to
/// a NUL.
int grid_line_puts(int col, const char *text, int textlen, int attr)
{
- size_t off;
const char *ptr = text;
int len = textlen;
int c;
- size_t max_off;
int u8cc[MAX_MCO];
- bool clear_next_cell = false;
int prev_c = 0; // previous Arabic character
int pc, nc, nc1;
int pcc[MAX_MCO];
- assert(grid_line_row >= 0);
- ScreenGrid *grid = grid_line_grid;
- int row = grid_line_row;
- col += grid_line_coloff;
+ assert(grid_line_grid);
- off = grid->line_offset[row] + (size_t)col;
int start_col = col;
- max_off = grid->line_offset[row] + (size_t)grid->cols;
- while (col < grid->cols
+ int max_col = grid_line_maxcol;
+ while (col < max_col
&& (len < 0 || (int)(ptr - text) < len)
&& *ptr != NUL) {
c = (unsigned char)(*ptr);
@@ -394,7 +379,7 @@ int grid_line_puts(int col, const char *text, int textlen, int attr)
} else {
prev_c = u8c;
}
- if (col + mbyte_cells > grid->cols) {
+ if (col + mbyte_cells > max_col) {
// Only 1 cell left, but character requires 2 cells:
// display a '>' in the last column to avoid wrapping. */
c = '>';
@@ -408,55 +393,29 @@ int grid_line_puts(int col, const char *text, int textlen, int attr)
// an edge case, treat it as such..
buf = schar_from_cc(u8c, u8cc);
- int need_redraw = grid->chars[off] != buf
- || (mbyte_cells == 2 && grid->chars[off + 1] != 0)
- || grid->attrs[off] != attr
- || exmode_active
- || rdb_flags & RDB_NODELTA;
-
- if (need_redraw) {
- // When at the end of the text and overwriting a two-cell
- // character with a one-cell character, need to clear the next
- // cell. Also when overwriting the left half of a two-cell char
- // with the right half of a two-cell char. Do this only once
- // (utf8_off2cells() may return 2 on the right half).
- if (clear_next_cell) {
- clear_next_cell = false;
- } else if ((len < 0 ? ptr[mbyte_blen] == NUL : ptr + mbyte_blen >= text + len)
- && ((mbyte_cells == 1
- && grid_off2cells(grid, off, max_off) > 1)
- || (mbyte_cells == 2
- && grid_off2cells(grid, off, max_off) == 1
- && grid_off2cells(grid, off + 1, max_off) > 1))) {
- clear_next_cell = true;
- }
-
- // When at the start of the text and overwriting the right half of a
- // two-cell character in the same grid, truncate that into a '>'.
- if (ptr == text && col > 0 && grid->chars[off] == 0) {
- grid->chars[off - 1] = schar_from_ascii('>');
- }
+ // When at the start of the text and overwriting the right half of a
+ // two-cell character in the same grid, truncate that into a '>'.
+ if (ptr == text && col > grid_line_first && col < grid_line_last
+ && linebuf_char[col] == 0) {
+ linebuf_char[col - 1] = schar_from_ascii('>');
+ }
- grid->chars[off] = buf;
- grid->attrs[off] = attr;
- grid->vcols[off] = -1;
- if (mbyte_cells == 2) {
- grid->chars[off + 1] = 0;
- grid->attrs[off + 1] = attr;
- grid->vcols[off + 1] = -1;
- }
- grid_line_first = MIN(grid_line_first, col);
- grid_line_last = MAX(grid_line_last, col + mbyte_cells);
+ linebuf_char[col] = buf;
+ linebuf_attr[col] = attr;
+ linebuf_vcol[col] = -1;
+ if (mbyte_cells == 2) {
+ linebuf_char[col + 1] = 0;
+ linebuf_attr[col + 1] = attr;
+ linebuf_vcol[col + 1] = -1;
}
- off += (size_t)mbyte_cells;
col += mbyte_cells;
ptr += mbyte_blen;
- if (clear_next_cell) {
- // This only happens at the end, display one space next.
- ptr = " ";
- len = -1;
- }
+ }
+
+ if (col > start_col) {
+ grid_line_first = MIN(grid_line_first, start_col);
+ grid_line_last = MAX(grid_line_last, col);
}
return col - start_col;
@@ -464,56 +423,30 @@ int grid_line_puts(int col, const char *text, int textlen, int attr)
void grid_line_fill(int start_col, int end_col, int c, int attr)
{
- ScreenGrid *grid = grid_line_grid;
- size_t lineoff = grid->line_offset[grid_line_row];
- start_col += grid_line_coloff;
- end_col += grid_line_coloff;
-
schar_T sc = schar_from_char(c);
for (int col = start_col; col < end_col; col++) {
- size_t off = lineoff + (size_t)col;
- if (grid->chars[off] != sc || grid->attrs[off] != attr || rdb_flags & RDB_NODELTA) {
- grid->chars[off] = sc;
- grid->attrs[off] = attr;
- grid_line_first = MIN(grid_line_first, col);
- grid_line_last = MAX(grid_line_last, col + 1);
- }
- grid->vcols[off] = -1;
+ linebuf_char[col] = sc;
+ linebuf_attr[col] = attr;
+ linebuf_vcol[col] = -1;
}
+ grid_line_first = MIN(grid_line_first, start_col);
+ grid_line_last = MAX(grid_line_last, end_col);
}
/// End a group of grid_line_puts 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 grid_line_flush(bool set_cursor)
+void grid_line_flush(void)
{
- assert(grid_line_row != -1);
- if (grid_line_first < grid_line_last) {
- // When drawing over the right half of a double-wide char clear out the
- // left half. Only needed in a terminal.
- if (grid_line_was_invalid && grid_line_first == 0) {
- // redraw the previous cell, make it empty
- grid_line_first = -1;
- }
- if (set_cursor) {
- ui_grid_cursor_goto(grid_line_grid->handle, grid_line_row,
- MIN(grid_line_last, grid_line_grid->cols - 1));
- }
- if (!grid_line_grid->throttled) {
- ui_line(grid_line_grid, grid_line_row, grid_line_first, grid_line_last,
- grid_line_last, 0, false);
- } else if (grid_line_grid->dirty_col) {
- if (grid_line_last > grid_line_grid->dirty_col[grid_line_row]) {
- grid_line_grid->dirty_col[grid_line_row] = grid_line_last;
- }
- }
- grid_line_first = INT_MAX;
- grid_line_last = 0;
- }
- grid_line_row = -1;
+ ScreenGrid *grid = grid_line_grid;
grid_line_grid = NULL;
+ if (!(grid_line_first < grid_line_last)) {
+ return;
+ }
+
+ int row = grid_line_row;
+
+ bool invalid_row = grid != &default_grid && grid_invalid_row(grid, row) && grid_line_first == 0;
+ grid_put_linebuf(grid, row, grid_line_coloff, grid_line_first, grid_line_last, grid_line_last,
+ false, 0, false, invalid_row);
}
/// Fill the grid from "start_row" to "end_row" (exclusive), from "start_col"
@@ -607,13 +540,14 @@ void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int
/// - the attributes are different
/// - the character is multi-byte and the next byte is different
/// - the character is two cells wide and the second cell differs.
-static int grid_char_needs_redraw(ScreenGrid *grid, size_t off_from, size_t off_to, int cols)
+static int grid_char_needs_redraw(ScreenGrid *grid, int col, size_t off_to, int cols)
{
return (cols > 0
- && ((linebuf_char[off_from] != grid->chars[off_to]
- || linebuf_attr[off_from] != grid->attrs[off_to]
- || (line_off2cells(linebuf_char, off_from, off_from + (size_t)cols) > 1
- && linebuf_char[off_from + 1] != grid->chars[off_to + 1]))
+ && ((linebuf_char[col] != grid->chars[off_to]
+ || linebuf_attr[col] != grid->attrs[off_to]
+ || (cols > 1 && linebuf_char[col + 1] == 0
+ && linebuf_char[col + 1] != grid->chars[off_to + 1]))
+ || exmode_active // TODO(bfredl): what in the actual fuck
|| rdb_flags & RDB_NODELTA));
}
@@ -623,30 +557,27 @@ static int grid_char_needs_redraw(ScreenGrid *grid, size_t off_from, size_t off_
/// "endcol" gives the columns where valid characters are.
/// "clear_width" is the width of the window. It's > 0 if the rest of the line
/// needs to be cleared, negative otherwise.
-/// "rlflag" is true in a rightleft window:
+/// "rl" is true for rightleft text, like a window with 'rightleft' option set
/// 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.
-void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int clear_width,
- int rlflag, win_T *wp, int bg_attr, bool wrap)
+void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol, int clear_width,
+ int rl, int bg_attr, bool wrap, bool invalid_row)
{
- int col = 0;
bool redraw_next; // redraw_this for next character
bool clear_next = false;
- bool topline = row == 0;
int char_cells; // 1: normal char
// 2: occupies two display cells
int start_dirty = -1, end_dirty = 0;
- assert(row < grid->rows);
+ assert(0 <= row && row < grid->rows);
// TODO(bfredl): check all callsites and eliminate
// Check for illegal col, just in case
if (endcol > grid->cols) {
endcol = grid->cols;
}
- const size_t max_off_from = (size_t)grid->cols;
grid_adjust(&grid, &row, &coloff);
// Safety check. Avoids clang warnings down the call stack.
@@ -655,45 +586,21 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle
return;
}
- size_t off_from = 0;
size_t off_to = grid->line_offset[row] + (size_t)coloff;
const size_t max_off_to = grid->line_offset[row] + (size_t)grid->cols;
- // Take care of putting "<<<" on the first line for 'smoothscroll'.
- if (topline && wp->w_skipcol > 0
- // do not overwrite the 'showbreak' text with "<<<"
- && *get_showbreak_value(wp) == NUL
- // do not overwrite the 'listchars' "precedes" text with "<<<"
- && !(wp->w_p_list && wp->w_p_lcs_chars.prec != 0)) {
- size_t off = 0;
- size_t skip = 0;
- if (wp->w_p_nu && wp->w_p_rnu) {
- // do not overwrite the line number, change "123 text" to
- // "123<<<xt".
- while (skip < max_off_from && ascii_isdigit(schar_get_ascii(linebuf_char[off]))) {
- off++;
- skip++;
- }
- }
-
- for (size_t i = 0; i < 3 && i + skip < max_off_from; i++) {
- if (line_off2cells(linebuf_char, off, max_off_from) > 1) {
- // When the first half of a double-width character is
- // overwritten, change the second half to a space.
- linebuf_char[off + 1] = schar_from_ascii(' ');
- }
- linebuf_char[off] = schar_from_ascii('<');
- linebuf_attr[off] = HL_ATTR(HLF_AT);
- off++;
- }
+ // When at the start of the text and overwriting the right half of a
+ // two-cell character in the same grid, truncate that into a '>'.
+ if (col > 0 && grid->chars[off_to + (size_t)col] == 0) {
+ linebuf_char[col - 1] = schar_from_ascii('>');
+ col--;
}
- if (rlflag) {
+ if (rl) {
// Clear rest first, because it's left of the text.
if (clear_width > 0) {
- while (col <= endcol && grid->chars[off_to] == schar_from_ascii(' ')
- && grid->attrs[off_to] == bg_attr) {
- off_to++;
+ while (col <= endcol && grid->chars[off_to + (size_t)col] == schar_from_ascii(' ')
+ && grid->attrs[off_to + (size_t)col] == bg_attr) {
col++;
}
if (col <= endcol) {
@@ -701,28 +608,26 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle
}
}
col = endcol + 1;
- off_to = grid->line_offset[row] + (size_t)col + (size_t)coloff;
- off_from += (size_t)col;
endcol = (clear_width > 0 ? clear_width : -clear_width);
}
if (bg_attr) {
- assert(off_from == (size_t)col);
for (int c = col; c < endcol; c++) {
linebuf_attr[c] = hl_combine_attr(bg_attr, linebuf_attr[c]);
}
}
- redraw_next = grid_char_needs_redraw(grid, off_from, off_to, endcol - col);
+ redraw_next = grid_char_needs_redraw(grid, col, (size_t)col + off_to, endcol - col);
while (col < endcol) {
char_cells = 1;
- if (col + 1 < endcol) {
- char_cells = line_off2cells(linebuf_char, off_from, max_off_from);
+ if (col + 1 < endcol && linebuf_char[col + 1] == 0) {
+ char_cells = 2;
}
bool redraw_this = redraw_next; // Does character need redraw?
- redraw_next = grid_char_needs_redraw(grid, off_from + (size_t)char_cells,
- off_to + (size_t)char_cells,
+ size_t off = (size_t)col + off_to;
+ redraw_next = grid_char_needs_redraw(grid, col + char_cells,
+ off + (size_t)char_cells,
endcol - col - char_cells);
if (redraw_this) {
@@ -737,53 +642,52 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle
// char over the left half of an existing one
if (col + char_cells == endcol
&& ((char_cells == 1
- && grid_off2cells(grid, off_to, max_off_to) > 1)
+ && grid_off2cells(grid, off, max_off_to) > 1)
|| (char_cells == 2
- && grid_off2cells(grid, off_to, max_off_to) == 1
- && grid_off2cells(grid, off_to + 1, max_off_to) > 1))) {
+ && grid_off2cells(grid, off, max_off_to) == 1
+ && grid_off2cells(grid, off + 1, max_off_to) > 1))) {
clear_next = true;
}
- grid->chars[off_to] = linebuf_char[off_from];
+ grid->chars[off] = linebuf_char[col];
if (char_cells == 2) {
- grid->chars[off_to + 1] = linebuf_char[off_from + 1];
+ grid->chars[off + 1] = linebuf_char[col + 1];
}
- grid->attrs[off_to] = linebuf_attr[off_from];
+ grid->attrs[off] = linebuf_attr[col];
// For simplicity set the attributes of second half of a
// double-wide character equal to the first half.
if (char_cells == 2) {
- grid->attrs[off_to + 1] = linebuf_attr[off_from];
+ grid->attrs[off + 1] = linebuf_attr[col];
}
}
- grid->vcols[off_to] = linebuf_vcol[off_from];
+ grid->vcols[off] = linebuf_vcol[col];
if (char_cells == 2) {
- grid->vcols[off_to + 1] = linebuf_vcol[off_from + 1];
+ grid->vcols[off + 1] = linebuf_vcol[col + 1];
}
- off_to += (size_t)char_cells;
- off_from += (size_t)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.
- grid->chars[off_to] = schar_from_ascii(' ');
+ grid->chars[(size_t)col + off_to] = schar_from_ascii(' ');
end_dirty++;
}
int clear_end = -1;
- if (clear_width > 0 && !rlflag) {
+ if (clear_width > 0 && !rl) {
// blank out the rest of the line
// TODO(bfredl): we could cache winline widths
while (col < clear_width) {
- if (grid->chars[off_to] != schar_from_ascii(' ')
- || grid->attrs[off_to] != bg_attr
+ size_t off = (size_t)col + off_to;
+ if (grid->chars[off] != schar_from_ascii(' ')
+ || grid->attrs[off] != bg_attr
|| rdb_flags & RDB_NODELTA) {
- grid->chars[off_to] = schar_from_ascii(' ');
- grid->attrs[off_to] = bg_attr;
+ grid->chars[off] = schar_from_ascii(' ');
+ grid->attrs[off] = bg_attr;
if (start_dirty == -1) {
start_dirty = col;
end_dirty = col;
@@ -792,9 +696,8 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle
}
clear_end = col + 1;
}
- grid->vcols[off_to] = MAXCOL;
+ grid->vcols[off] = MAXCOL;
col++;
- off_to++;
}
}
@@ -805,8 +708,22 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle
start_dirty = end_dirty;
}
if (clear_end > start_dirty) {
- ui_line(grid, row, coloff + start_dirty, coloff + end_dirty, coloff + clear_end,
- bg_attr, wrap);
+ if (!grid->throttled) {
+ int start_pos = coloff + start_dirty;
+ // When drawing over the right half of a double-wide char clear out the
+ // left half. Only needed in a terminal.
+ if (invalid_row && start_pos == 0) {
+ start_pos = -1;
+ }
+ ui_line(grid, row, start_pos, coloff + end_dirty, coloff + clear_end,
+ bg_attr, wrap);
+ } else if (grid->dirty_col) {
+ // TODO(bfredl): really get rid of the extra psuedo terminal in message.c
+ // by using a linebuf_char copy for "throttled message line"
+ if (clear_end > grid->dirty_col[row]) {
+ grid->dirty_col[row] = clear_end;
+ }
+ }
}
}
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index b6fe24a961..834204e5e1 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -2087,7 +2087,7 @@ static void display_showcmd(void)
// clear the rest of an old message by outputting up to SHOWCMD_COLS spaces
grid_line_puts(sc_col + len, (char *)" " + len, -1, HL_ATTR(HLF_MSG));
- grid_line_flush(false);
+ grid_line_flush();
}
/// When "check" is false, prepare for commands that scroll the window.
diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c
index 488c893bd8..3be2f4cd9f 100644
--- a/src/nvim/popupmenu.c
+++ b/src/nvim/popupmenu.c
@@ -655,7 +655,7 @@ void pum_redraw(void)
i >= thumb_pos && i < thumb_pos + thumb_height ? attr_thumb : attr_scroll);
}
}
- grid_line_flush(false);
+ grid_line_flush();
row++;
}
}
diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c
index bdc2e4a57e..407bf71417 100644
--- a/src/nvim/statusline.c
+++ b/src/nvim/statusline.c
@@ -171,7 +171,7 @@ void win_redr_status(win_T *wp)
}
}
- grid_line_flush(false);
+ grid_line_flush();
}
// May need to draw the character below the vertical separator.
@@ -449,7 +449,7 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
grid_line_fill(col, maxcol, fillchar, curattr);
if (!draw_ruler) {
- grid_line_flush(false);
+ grid_line_flush();
}
// Fill the tab_page_click_defs, w_status_click_defs or w_winbar_click_defs array for clicking
@@ -861,7 +861,7 @@ void draw_tabline(void)
};
}
- grid_line_flush(false);
+ grid_line_flush();
}
// Reset the flag here again, in case evaluating 'tabline' causes it to be