refactor(grid): get rid of unbatched grid_puts and grid_putchar

This finalizes the long running refactor from the old TUI-focused grid
implementation where text-drawing cursor was not separated from the
visible cursor.

Still, the pattern of setting cursor position together with updating a
line was convenient. Introduce grid_line_cursor_goto() to still allow
this but now being explicit about it.

Only having batched drawing functions makes code involving drawing
a bit longer. But it is better to be explicit, and this highlights
cases where multiple small redraws can be grouped together. This was the
case for most of the changed places (messages, lastline, and :intro)
This commit is contained in:
bfredl 2023-10-05 14:44:13 +02:00
parent f67517bba3
commit a58bb21544
10 changed files with 105 additions and 152 deletions

View File

@ -3330,5 +3330,6 @@ static void win_put_linebuf(win_T *wp, int row, int coloff, int endcol, int clea
}
}
grid_adjust(&grid, &row, &coloff);
grid_put_linebuf(grid, row, coloff, 0, endcol, clear_width, wp->w_p_rl, bg_attr, wrap, false);
}

View File

@ -2382,26 +2382,20 @@ static void win_update(win_T *wp, DecorProviders *providers)
wp->w_botline = lnum;
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(&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);
grid_line_start(&wp->w_grid, wp->w_grid.rows - 1);
grid_line_fill(0, MIN(wp->w_grid.cols, 3), wp->w_p_fcs_chars.lastline, at_attr);
grid_line_fill(3, wp->w_grid.cols, ' ', at_attr);
grid_line_flush();
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.
// 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_fill(MAX(wp->w_grid.cols - 3, 0), wp->w_grid.cols,
wp->w_p_fcs_chars.lastline, at_attr);
grid_line_flush();
set_empty_rows(wp, srow);
wp->w_botline = lnum;

View File

@ -1412,11 +1412,11 @@ static void ins_ctrl_v(void)
// Put a character directly onto the screen. It's not stored in a buffer.
// Used while handling CTRL-K, CTRL-V, etc. in Insert mode.
static int pc_status;
#define PC_STATUS_UNSET 0 // pc_bytes was not set
#define PC_STATUS_RIGHT 1 // right half of double-wide char
#define PC_STATUS_LEFT 2 // left half of double-wide char
#define PC_STATUS_SET 3 // pc_bytes was filled
static char pc_bytes[MB_MAXBYTES + 1]; // saved bytes
#define PC_STATUS_UNSET 0 // nothing was put on screen
#define PC_STATUS_RIGHT 1 // right half of double-wide char
#define PC_STATUS_LEFT 2 // left half of double-wide char
#define PC_STATUS_SET 3 // pc_schar was filled
static schar_T pc_schar; // saved char
static int pc_attr;
static int pc_row;
static int pc_col;
@ -1433,31 +1433,34 @@ void edit_putchar(int c, bool highlight)
attr = 0;
}
pc_row = curwin->w_wrow;
pc_col = 0;
pc_status = PC_STATUS_UNSET;
grid_line_start(&curwin->w_grid, pc_row);
if (curwin->w_p_rl) {
pc_col += curwin->w_grid.cols - 1 - curwin->w_wcol;
const int fix_col = grid_fix_col(&curwin->w_grid, pc_col, pc_row);
pc_col = curwin->w_grid.cols - 1 - curwin->w_wcol;
if (fix_col != pc_col) {
grid_putchar(&curwin->w_grid, ' ', pc_row, fix_col, attr);
if (grid_line_getchar(pc_col, NULL) == NUL) {
grid_line_put_schar(pc_col - 1, schar_from_ascii(' '), attr);
curwin->w_wcol--;
pc_status = PC_STATUS_RIGHT;
}
} else {
pc_col += curwin->w_wcol;
if (grid_lefthalve(&curwin->w_grid, pc_row, pc_col)) {
pc_col = curwin->w_wcol;
if (grid_line_getchar(pc_col + 1, NULL) == NUL) {
// pc_col is the left half of a double-width char
pc_status = PC_STATUS_LEFT;
}
}
// save the character to be able to put it back
if (pc_status == PC_STATUS_UNSET) {
// TODO(bfredl): save the schar_T instead
grid_getbytes(&curwin->w_grid, pc_row, pc_col, pc_bytes, &pc_attr);
pc_schar = grid_line_getchar(pc_col, &pc_attr);
pc_status = PC_STATUS_SET;
}
grid_putchar(&curwin->w_grid, c, pc_row, pc_col, attr);
char buf[MB_MAXBYTES + 1];
grid_line_puts(pc_col, buf, utf_char2bytes(c, buf), attr);
grid_line_flush();
}
}
@ -1537,7 +1540,10 @@ void edit_unputchar(void)
if (pc_status == PC_STATUS_RIGHT || pc_status == PC_STATUS_LEFT) {
redrawWinline(curwin, curwin->w_cursor.lnum);
} else {
grid_puts(&curwin->w_grid, pc_bytes, -1, pc_row, pc_col, pc_attr);
// TODO(bfredl): this could be smarter and also handle the dubyawidth case
grid_line_start(&curwin->w_grid, pc_row);
grid_line_put_schar(pc_col, pc_schar, pc_attr);
grid_line_flush();
}
}
}

View File

@ -6885,7 +6885,7 @@ static void f_screenchar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
c = -1;
} else {
char buf[MB_MAXBYTES + 1];
grid_getbytes(grid, row, col, buf, NULL);
schar_get(buf, grid_getchar(grid, row, col, NULL));
c = utf_ptr2char(buf);
}
rettv->vval.v_number = c;
@ -6906,7 +6906,7 @@ static void f_screenchars(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
char buf[MB_MAXBYTES + 1];
grid_getbytes(grid, row, col, buf, NULL);
schar_get(buf, grid_getchar(grid, row, col, NULL));
int pcc[MAX_MCO];
int c = utfc_ptr2char(buf, pcc);
int composing_len = 0;
@ -6951,7 +6951,7 @@ static void f_screenstring(typval_T *argvars, typval_T *rettv, EvalFuncData fptr
}
char buf[MB_MAXBYTES + 1];
grid_getbytes(grid, row, col, buf, NULL);
schar_get(buf, grid_getchar(grid, row, col, NULL));
rettv->vval.v_string = xstrdup(buf);
}

View File

@ -201,65 +201,23 @@ static bool grid_invalid_row(ScreenGrid *grid, int row)
return grid->attrs[grid->line_offset[row]] < 0;
}
/// 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 (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
/// of a double-width character.
///
/// Caller must make sure "row" and "col" are not invalid!
bool grid_lefthalve(ScreenGrid *grid, int row, int col)
{
grid_adjust(&grid, &row, &col);
return grid_off2cells(grid, grid->line_offset[row] + (size_t)col,
grid->line_offset[row] + (size_t)grid->cols) > 1;
}
/// Correct a position on the screen, if it's the right half of a double-wide
/// char move it to the left half. Returns the corrected column.
int grid_fix_col(ScreenGrid *grid, int col, int row)
{
int coloff = 0;
grid_adjust(&grid, &row, &coloff);
col += coloff;
if (grid->chars != NULL && col > 0
&& grid->chars[grid->line_offset[row] + (size_t)col] == 0) {
return col - 1 - coloff;
}
return col - coloff;
}
/// output a single character directly to the grid
void grid_putchar(ScreenGrid *grid, int c, int row, int col, int attr)
{
char buf[MB_MAXBYTES + 1];
grid_puts(grid, buf, utf_char2bytes(c, buf), row, col, attr);
}
/// Get a single character directly from grid.chars into "bytes", which must
/// have a size of "MB_MAXBYTES + 1".
/// If "attrp" is not NULL, return the character's attribute in "*attrp".
void grid_getbytes(ScreenGrid *grid, int row, int col, char *bytes, int *attrp)
schar_T grid_getchar(ScreenGrid *grid, int row, int col, int *attrp)
{
grid_adjust(&grid, &row, &col);
// safety check
if (grid->chars == NULL || row >= grid->rows || col >= grid->cols) {
return;
return NUL;
}
size_t off = grid->line_offset[row] + (size_t)col;
if (attrp != NULL) {
*attrp = grid->attrs[off];
}
schar_get(bytes, grid->chars[off]);
return grid->chars[off];
}
static ScreenGrid *grid_line_grid = NULL;
@ -269,38 +227,6 @@ 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'
/// @param textlen length of string or -1 to use strlen(text)
/// Note: only outputs within one row!
int grid_puts(ScreenGrid *grid, const char *text, int textlen, int row, int col, int attr)
{
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;
}
}
int len = grid_line_puts(col, text, textlen, attr);
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;
}
/// 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
@ -318,6 +244,25 @@ void grid_line_start(ScreenGrid *grid, int row)
grid_line_last = 0;
}
/// Get present char from current rendered screen line
///
/// This indicates what already is on screen, not the pending render buffer.
///
/// @return char or space if out of bounds
schar_T grid_line_getchar(int col, int *attr)
{
if (col < grid_line_maxcol) {
size_t off = grid_line_grid->line_offset[grid_line_row] + (size_t)col;
if (attr != NULL) {
*attr = grid_line_grid->attrs[off];
}
return grid_line_grid->chars[off];
} else {
// NUL is a very special value (right-half of double width), space is True Neutral™
return schar_from_ascii(' ');
}
}
void grid_line_put_schar(int col, schar_T schar, int attr)
{
assert(grid_line_grid);
@ -331,8 +276,13 @@ void grid_line_put_schar(int col, schar_T schar, int attr)
linebuf_vcol[col] = -1;
}
/// like grid_puts(), but output "text[len]". When "len" is -1 output up to
/// a NUL.
/// Put string "text" at "col" position relative to the grid line from the
/// recent grid_line_start() call.
///
/// @param textlen length of string or -1 to use strlen(text)
/// Note: only outputs within one row!
///
/// @return number of grid cells used
int grid_line_puts(int col, const char *text, int textlen, int attr)
{
const char *ptr = text;
@ -435,8 +385,16 @@ void grid_line_fill(int start_col, int end_col, int c, int attr)
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);
if (start_col < end_col) {
grid_line_first = MIN(grid_line_first, start_col);
grid_line_last = MAX(grid_line_last, end_col);
}
}
/// move the cursor to a position in a currently rendered line.
void grid_line_cursor_goto(int col)
{
ui_grid_cursor_goto(grid_line_grid->handle, grid_line_row, col);
}
/// End a group of grid_line_puts calls and send the screen buffer to the UI layer.
@ -500,27 +458,29 @@ void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int
}
for (int row = start_row; row < end_row; row++) {
int dirty_first = INT_MAX;
int dirty_last = 0;
size_t lineoff = grid->line_offset[row];
// When drawing over the right half of a double-wide char clear
// out the left half. When drawing over the left half of a
// double wide-char clear out the right half. Only needed in a
// terminal.
if (start_col > 0 && grid_fix_col(grid, start_col, row) != start_col) {
grid_puts(grid, " ", 1, row, start_col - 1, 0);
if (start_col > 0 && grid->chars[lineoff + (size_t)start_col] == NUL) {
size_t off = lineoff + (size_t)start_col - 1;
grid->chars[off] = schar_from_ascii(' ');
grid->attrs[off] = attr;
dirty_first = start_col - 1;
}
if (end_col < grid->cols
&& grid_fix_col(grid, end_col, row) != end_col) {
grid_puts(grid, " ", 1, row, end_col, 0);
if (end_col < grid->cols && grid->chars[lineoff + (size_t)end_col] == NUL) {
size_t off = lineoff + (size_t)end_col;
grid->chars[off] = schar_from_ascii(' ');
grid->attrs[off] = attr;
dirty_last = end_col + 1;
}
// if grid was resized (in ext_multigrid mode), the UI has no redraw updates
// for the newly resized grid. It is better mark everything as dirty and
// send all the updates.
int dirty_first = INT_MAX;
int dirty_last = 0;
int col = start_col;
sc = schar_from_char(c1);
size_t lineoff = grid->line_offset[row];
for (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) {
@ -600,8 +560,6 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol
endcol = grid->cols;
}
grid_adjust(&grid, &row, &coloff);
// Safety check. Avoids clang warnings down the call stack.
if (grid->chars == NULL || row >= grid->rows || coloff >= grid->cols) {
DLOG("invalid state, skipped");
@ -662,12 +620,8 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol
// the right half of the old character.
// Also required when writing the right half of a double-width
// char over the left half of an existing one
if (col + char_cells == endcol
&& ((char_cells == 1
&& grid_off2cells(grid, off, max_off_to) > 1)
|| (char_cells == 2
&& grid_off2cells(grid, off, max_off_to) == 1
&& grid_off2cells(grid, off + 1, max_off_to) > 1))) {
if (col + char_cells == endcol && off + (size_t)char_cells < max_off_to
&& grid->chars[off + (size_t)char_cells] == NUL) {
clear_next = true;
}

View File

@ -2959,15 +2959,15 @@ void os_msg(const char *str)
void msg_moremsg(int full)
{
int attr;
char *s = _("-- More --");
attr = hl_combine_attr(HL_ATTR(HLF_MSG), HL_ATTR(HLF_M));
grid_puts(&msg_grid_adj, s, -1, Rows - 1, 0, attr);
int attr = hl_combine_attr(HL_ATTR(HLF_MSG), HL_ATTR(HLF_M));
grid_line_start(&msg_grid_adj, Rows - 1);
int len = grid_line_puts(0, _("-- More --"), -1, attr);
if (full) {
grid_puts(&msg_grid_adj, _(" SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "), -1,
Rows - 1, vim_strsize(s), attr);
len += grid_line_puts(len, _(" SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "),
-1, attr);
}
grid_line_cursor_goto(len);
grid_line_flush();
}
/// Repeat the message for the current mode: MODE_ASKMORE, MODE_EXTERNCMD,

View File

@ -182,7 +182,9 @@ void win_redr_status(win_T *wp)
attr = win_hl_attr(wp, HLF_C);
fillchar = wp->w_p_fcs_chars.vert;
}
grid_putchar(&default_grid, fillchar, W_ENDROW(wp), W_ENDCOL(wp), attr);
grid_line_start(&default_grid, W_ENDROW(wp));
grid_line_put_schar(W_ENDCOL(wp), schar_from_char(fillchar), attr);
grid_line_flush();
}
busy = false;
}

View File

@ -532,7 +532,7 @@ void ui_comp_raw_line(Integer grid, Integer row, Integer startcol, Integer endco
compose_debug(row, row + 1, startcol, clearcol, dbghl_composed, true);
compose_line(row, startcol, clearcol, flags);
} else {
compose_debug(row, row + 1, startcol, endcol, dbghl_normal, false);
compose_debug(row, row + 1, startcol, endcol, dbghl_normal, endcol >= clearcol);
compose_debug(row, row + 1, endcol, clearcol, dbghl_clear, true);
#ifndef NDEBUG
for (int i = 0; i < endcol - startcol; i++) {

View File

@ -2827,7 +2827,7 @@ void intro_message(int colon)
}
if (*mesg != NUL) {
do_intro_line(row, mesg, 0);
do_intro_line((int)row, mesg, 0);
}
row++;
@ -2838,14 +2838,13 @@ void intro_message(int colon)
}
}
static void do_intro_line(long row, char *mesg, int attr)
static void do_intro_line(int row, char *mesg, int attr)
{
char *p;
int l;
int clen;
// Center the message horizontally.
long col = vim_strsize(mesg);
int col = vim_strsize(mesg);
col = (Columns - col) / 2;
@ -2853,21 +2852,18 @@ static void do_intro_line(long row, char *mesg, int attr)
col = 0;
}
grid_line_start(&default_grid, row);
// Split up in parts to highlight <> items differently.
for (p = mesg; *p != NUL; p += l) {
clen = 0;
for (l = 0;
p[l] != NUL && (l == 0 || (p[l] != '<' && p[l - 1] != '>'));
l++) {
clen += ptr2cells(p + l);
l += utfc_ptr2len(p + l) - 1;
}
assert(row <= INT_MAX && col <= INT_MAX);
grid_puts(&default_grid, p, l, (int)row, (int)col,
*p == '<' ? HL_ATTR(HLF_8) : attr);
col += clen;
col += grid_line_puts(col, p, l, *p == '<' ? HL_ATTR(HLF_8) : attr);
}
grid_line_flush();
}
/// ":intro": clear screen, display intro screen and wait for return.

View File

@ -1366,7 +1366,7 @@ describe('ui/ext_messages', function()
feed(":intro<cr>")
screen:expect{grid=[[
|
^ |
|
|
|