diff options
author | bfredl <bjorn.linse@gmail.com> | 2024-01-04 14:07:25 +0100 |
---|---|---|
committer | bfredl <bjorn.linse@gmail.com> | 2024-01-11 19:53:21 +0100 |
commit | 478b1af511b2ce9ecaeade910ce227b900b83429 (patch) | |
tree | 334cc7e14a57d688ddc4038dda678c04b2eb57d2 /src | |
parent | d54156ed08b84e6c7f22334a4f3a4d4f84798604 (diff) | |
download | rneovim-478b1af511b2ce9ecaeade910ce227b900b83429.tar.gz rneovim-478b1af511b2ce9ecaeade910ce227b900b83429.tar.bz2 rneovim-478b1af511b2ce9ecaeade910ce227b900b83429.zip |
refactor(screen): simplify grid_clear() and win_draw_end()
grid_put_linebuf() used grid_clear() internally to handle clearing the
left part of a rightleft line. By reimplementing that internally, we
can instead use grid_put_linebuf() as the implementation of
grid_clear(), which in the end is a net reduction of code as grid_fill()
is used for nothing else.
win_draw_end: Implement "draw_margin" on a per-row basis which closer reflects how
terminals work. Also use the magic mirror for 'rightleft'
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/drawline.c | 9 | ||||
-rw-r--r-- | src/nvim/drawscreen.c | 93 | ||||
-rw-r--r-- | src/nvim/grid.c | 218 | ||||
-rw-r--r-- | src/nvim/message.c | 3 | ||||
-rw-r--r-- | src/nvim/ui.c | 7 |
5 files changed, 126 insertions, 204 deletions
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index a4d98f09f4..aae374c8a7 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -2611,8 +2611,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // When the window is too narrow draw all "@" lines. if (leftcols_width >= wp->w_grid.cols && wp->w_p_wrap) { - win_draw_end(wp, schar_from_ascii('@'), schar_from_ascii(' '), true, wlv.row, - wp->w_grid.rows, HLF_AT); + win_draw_end(wp, schar_from_ascii('@'), true, wlv.row, wp->w_grid.rows, HLF_AT); set_empty_rows(wp, wlv.row); wlv.row = endrow; } @@ -2836,8 +2835,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // When the window is too narrow draw all "@" lines. if (wlv.col <= leftcols_width) { - win_draw_end(wp, schar_from_ascii('@'), schar_from_ascii(' '), true, wlv.row, - wp->w_grid.rows, HLF_AT); + win_draw_end(wp, schar_from_ascii('@'), true, wlv.row, wp->w_grid.rows, HLF_AT); set_empty_rows(wp, wlv.row); wlv.row = endrow; } @@ -2883,8 +2881,7 @@ static void win_put_linebuf(win_T *wp, int row, int coloff, int endcol, int clea int start_col = 0; if (wp->w_p_rl) { - linebuf_mirror(&start_col, &clear_width, grid->cols); - endcol = grid->cols - 1 - endcol; + linebuf_mirror(&start_col, &endcol, &clear_width, grid->cols); } // Take care of putting "<<<" on the first line for 'smoothscroll'. diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index 12f06e13cc..964fdb141a 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -1296,9 +1296,11 @@ static void draw_vsep_win(win_T *wp) } // draw the vertical separator right of this window - int hl = win_hl_attr(wp, HLF_C); - schar_T c = wp->w_p_fcs_chars.vert; - grid_fill(&default_grid, wp->w_winrow, W_ENDROW(wp), W_ENDCOL(wp), W_ENDCOL(wp) + 1, c, c, hl); + for (int row = wp->w_winrow; row < W_ENDROW(wp); row++) { + grid_line_start(&default_grid, row); + grid_line_put_schar(W_ENDCOL(wp), wp->w_p_fcs_chars.vert, win_hl_attr(wp, HLF_C)); + grid_line_flush(); + } } /// Draw the horizontal separator below window "wp" @@ -1309,9 +1311,9 @@ static void draw_hsep_win(win_T *wp) } // draw the horizontal separator below this window - int hl = win_hl_attr(wp, HLF_C); - schar_T c = wp->w_p_fcs_chars.horiz; - grid_fill(&default_grid, W_ENDROW(wp), W_ENDROW(wp) + 1, wp->w_wincol, W_ENDCOL(wp), c, c, hl); + grid_line_start(&default_grid, W_ENDROW(wp)); + grid_line_fill(wp->w_wincol, W_ENDCOL(wp), wp->w_p_fcs_chars.horiz, win_hl_attr(wp, HLF_C)); + grid_line_flush(); } /// Get the separator connector for specified window corner of window "wp" @@ -2395,7 +2397,7 @@ static void win_update(win_T *wp) set_empty_rows(wp, srow); wp->w_botline = lnum; } else { - win_draw_end(wp, wp->w_p_fcs_chars.lastline, schar_from_ascii(' '), true, srow, + 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; @@ -2429,7 +2431,7 @@ static void win_update(win_T *wp) lastline = 0; } - win_draw_end(wp, wp->w_p_fcs_chars.eob, schar_from_ascii(' '), false, MAX(lastline, row), + 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); @@ -2517,60 +2519,43 @@ void win_scroll_lines(win_T *wp, int row, int line_count) } } -/// Call grid_clear() with columns adjusted for 'rightleft' if needed. -/// Return the new offset. -static int win_clear_end(win_T *wp, int off, int width, int row, int endrow, int attr) -{ - int nn = off + width; - const int endcol = wp->w_grid.cols; - - if (nn > endcol) { - nn = endcol; - } - - if (wp->w_p_rl) { - grid_clear(&wp->w_grid, row, endrow, endcol - nn, endcol - off, attr); - } else { - grid_clear(&wp->w_grid, row, endrow, off, nn, attr); - } - - return nn; -} - /// Clear lines near the end of the window and mark the unused lines with "c1". -/// Use "c2" as filler character. /// When "draw_margin" is true, then draw the sign/fold/number columns. -void win_draw_end(win_T *wp, schar_T c1, schar_T c2, bool draw_margin, int row, int endrow, - hlf_T hl) +void win_draw_end(win_T *wp, schar_T c1, bool draw_margin, int startrow, int endrow, hlf_T hl) { assert(hl >= 0 && hl < HLF_COUNT); - int n = 0; - - if (draw_margin) { - // draw the fold column - int fdc = compute_foldcolumn(wp, 0); - if (fdc > 0) { - n = win_clear_end(wp, n, fdc, row, endrow, win_hl_attr(wp, HLF_FC)); - } - // draw the sign column - int count = wp->w_scwidth; - if (count > 0) { - n = win_clear_end(wp, n, SIGN_WIDTH * count, row, endrow, win_hl_attr(wp, HLF_SC)); + for (int row = startrow; row < endrow; row++) { + grid_line_start(&wp->w_grid, row); + + int n = 0; + if (draw_margin) { + // draw the fold column + int fdc = MAX(0, compute_foldcolumn(wp, 0)); + n = grid_line_fill(n, n + fdc, schar_from_ascii(' '), win_hl_attr(wp, HLF_FC)); + + // draw the sign column + n = grid_line_fill(n, n + wp->w_scwidth, schar_from_ascii(' '), win_hl_attr(wp, HLF_FC)); + + // draw the number column + if ((wp->w_p_nu || wp->w_p_rnu) && vim_strchr(p_cpo, CPO_NUMCOL) == NULL) { + int width = number_width(wp) + 1; + n = grid_line_fill(n, n + width, schar_from_ascii(' '), win_hl_attr(wp, HLF_N)); + } } - // draw the number column - if ((wp->w_p_nu || wp->w_p_rnu) && vim_strchr(p_cpo, CPO_NUMCOL) == NULL) { - n = win_clear_end(wp, n, number_width(wp) + 1, row, endrow, win_hl_attr(wp, HLF_N)); + + int attr = hl_combine_attr(win_bg_attr(wp), win_hl_attr(wp, (int)hl)); + + if (n < wp->w_grid.cols) { + grid_line_put_schar(n, c1, 0); // base attr is inherited from clear + n++; } - } - int attr = hl_combine_attr(win_bg_attr(wp), win_hl_attr(wp, (int)hl)); + grid_line_clear_end(n, wp->w_grid.cols, attr); - const int endcol = wp->w_grid.cols; - if (wp->w_p_rl) { - grid_fill(&wp->w_grid, row, endrow, 0, endcol - 1 - n, c2, c2, attr); - grid_fill(&wp->w_grid, row, endrow, endcol - 1 - n, endcol - n, c1, c2, attr); - } else { - grid_fill(&wp->w_grid, row, endrow, n, endcol, c1, c2, attr); + if (wp->w_p_rl) { + grid_line_mirror(); + } + grid_line_flush(); } } diff --git a/src/nvim/grid.c b/src/nvim/grid.c index f529e14b35..0f8a0f8ab8 100644 --- a/src/nvim/grid.c +++ b/src/nvim/grid.c @@ -334,6 +334,9 @@ 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; +static int grid_line_clear_to = 0; +static int grid_line_clear_attr = 0; +static bool grid_line_rl = false; /// Start a group of grid_line_puts calls that builds a single grid line. /// @@ -342,14 +345,18 @@ static int grid_line_last = 0; void grid_line_start(ScreenGrid *grid, int row) { int col = 0; + grid_line_maxcol = grid->cols; grid_adjust(&grid, &row, &col); assert(grid_line_grid == NULL); grid_line_row = row; grid_line_grid = grid; grid_line_coloff = col; grid_line_first = (int)linebuf_size; - grid_line_maxcol = grid->cols - grid_line_coloff; + grid_line_maxcol = MIN(grid_line_maxcol, grid->cols - grid_line_coloff); grid_line_last = 0; + grid_line_clear_to = 0; + grid_line_clear_attr = 0; + grid_line_rl = false; assert((size_t)grid_line_maxcol <= linebuf_size); @@ -465,11 +472,11 @@ int grid_line_puts(int col, const char *text, int textlen, int attr) return col - start_col; } -void grid_line_fill(int start_col, int end_col, schar_T sc, int attr) +int grid_line_fill(int start_col, int end_col, schar_T sc, int attr) { end_col = MIN(end_col, grid_line_maxcol); if (start_col >= end_col) { - return; + return end_col; } for (int col = start_col; col < end_col; col++) { @@ -480,6 +487,17 @@ void grid_line_fill(int start_col, int end_col, schar_T sc, int attr) grid_line_first = MIN(grid_line_first, start_col); grid_line_last = MAX(grid_line_last, end_col); + return end_col; +} + +void grid_line_clear_end(int start_col, int end_col, int attr) +{ + if (grid_line_first > start_col) { + grid_line_first = start_col; + grid_line_last = start_col; + } + grid_line_clear_to = end_col; + grid_line_clear_attr = attr; } /// move the cursor to a position in a currently rendered line. @@ -490,13 +508,15 @@ void grid_line_cursor_goto(int col) void grid_line_mirror(void) { - if (grid_line_first >= grid_line_last) { + grid_line_clear_to = MAX(grid_line_last, grid_line_clear_to); + if (grid_line_first >= grid_line_clear_to) { return; } - linebuf_mirror(&grid_line_first, &grid_line_last, grid_line_maxcol); + linebuf_mirror(&grid_line_first, &grid_line_last, &grid_line_clear_to, grid_line_maxcol); + grid_line_rl = true; } -void linebuf_mirror(int *firstp, int *lastp, int maxcol) +void linebuf_mirror(int *firstp, int *lastp, int *clearp, int maxcol) { int first = *firstp; int last = *lastp; @@ -529,8 +549,9 @@ void linebuf_mirror(int *firstp, int *lastp, int maxcol) linebuf_vcol[mirror - col] = scratch_vcol[col]; } - *lastp = maxcol - first; - *firstp = maxcol - last; + *firstp = maxcol - *clearp; + *clearp = maxcol - first; + *lastp = maxcol - last; } /// End a group of grid_line_puts calls and send the screen buffer to the UI layer. @@ -538,13 +559,14 @@ void grid_line_flush(void) { ScreenGrid *grid = grid_line_grid; grid_line_grid = NULL; - assert(grid_line_last <= grid_line_maxcol); - if (grid_line_first >= grid_line_last) { + grid_line_clear_to = MAX(grid_line_last, grid_line_clear_to); + assert(grid_line_clear_to <= grid_line_maxcol); + if (grid_line_first >= grid_line_clear_to) { return; } grid_put_linebuf(grid, grid_line_row, grid_line_coloff, grid_line_first, grid_line_last, - grid_line_last, false, 0, false); + grid_line_clear_to, grid_line_rl, grid_line_clear_attr, false); } /// flush grid line but only if on a valid row @@ -565,94 +587,15 @@ void grid_line_flush_if_valid_row(void) void grid_clear(ScreenGrid *grid, int start_row, int end_row, int start_col, int end_col, int attr) { - grid_fill(grid, start_row, end_row, start_col, end_col, schar_from_ascii(' '), - schar_from_ascii(' '), attr); -} - -/// Fill the grid from "start_row" to "end_row" (exclusive), from "start_col" -/// to "end_col" (exclusive) with character "c1" in first column followed by -/// "c2" in the other columns. Use attributes "attr". -void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int end_col, schar_T c1, - schar_T c2, int attr) -{ - int row_off = 0; - int col_off = 0; - grid_adjust(&grid, &row_off, &col_off); - start_row += row_off; - end_row += row_off; - start_col += col_off; - end_col += col_off; - - // safety check - if (end_row > grid->rows) { - end_row = grid->rows; - } - if (end_col > grid->cols) { - end_col = grid->cols; - } - - // nothing to do - if (start_row >= end_row || start_col >= end_col) { - return; - } - 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->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->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; - } - - int col = start_col; - schar_T sc = c1; - 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) { - grid->chars[off] = sc; - grid->attrs[off] = attr; - if (dirty_first == INT_MAX) { - dirty_first = col; - } - dirty_last = col + 1; - } - grid->vcols[off] = -1; - if (col == start_col) { - sc = c2; - } - } - if (dirty_last > dirty_first) { - if (grid->throttled) { - // Note: assumes msg_grid is the only throttled grid - assert(grid == &msg_grid); - int dirty = 0; - if (attr != HL_ATTR(HLF_MSG) || c2 != ' ') { - dirty = dirty_last; - } else if (c1 != ' ') { - dirty = dirty_first + 1; - } - if (grid->dirty_col && dirty > grid->dirty_col[row]) { - grid->dirty_col[row] = dirty; - } - } else { - int last = c2 != ' ' ? dirty_last : dirty_first + (c1 != ' '); - ui_line(grid, row, dirty_first, last, dirty_last, attr, false); - } + grid_line_start(grid, row); + end_col = MIN(end_col, grid_line_maxcol); + if (grid_line_row >= grid_line_grid->rows || start_col >= end_col) { + grid_line_grid = NULL; // TODO(bfredl): make callers behave instead + return; } + grid_line_clear_end(start_col, end_col, attr); + grid_line_flush(); } } @@ -712,19 +655,12 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol col--; } + int clear_start = endcol; if (rl) { - // Clear rest first, because it's left of the text. - if (clear_width > 0) { - 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) { - grid_clear(grid, row, row + 1, col + coloff, endcol + coloff + 1, bg_attr); - } - } - col = endcol + 1; + clear_start = col; + col = endcol; endcol = clear_width; + clear_width = col; } if (p_arshape && !p_tbidi && endcol > col) { @@ -797,45 +733,49 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol end_dirty++; } - int clear_end = -1; - if (clear_width > 0 && !rl) { - // blank out the rest of the line - // TODO(bfredl): we could cache winline widths - while (col < clear_width) { - 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] = schar_from_ascii(' '); - grid->attrs[off] = bg_attr; - if (start_dirty == -1) { - start_dirty = col; - end_dirty = col; - } else if (clear_end == -1) { - end_dirty = endcol; - } - clear_end = col + 1; + int clear_dirty_start = -1, clear_end = -1; + // blank out the rest of the line + // TODO(bfredl): we could cache winline widths + col = clear_start; + while (col < clear_width) { + 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] = schar_from_ascii(' '); + grid->attrs[off] = bg_attr; + if (clear_dirty_start == -1) { + clear_dirty_start = col; } - grid->vcols[off] = MAXCOL; - col++; + clear_end = col + 1; } + grid->vcols[off] = MAXCOL; + col++; } - if (clear_end < end_dirty) { + if (rl && start_dirty != -1 && clear_dirty_start != -1) { + if (grid->throttled || clear_dirty_start >= start_dirty - 5) { + // cannot draw now or too small to be worth a separate "clear" event + start_dirty = clear_dirty_start; + } else { + ui_line(grid, row, invalid_row, coloff + clear_dirty_start, coloff + clear_dirty_start, + coloff + clear_end, bg_attr, wrap); + } clear_end = end_dirty; + } else { + if (start_dirty == -1) { // clear only + start_dirty = clear_dirty_start; + end_dirty = clear_dirty_start; + } else if (clear_end < end_dirty) { // put only + clear_end = end_dirty; + } else { + end_dirty = endcol; + } } - if (start_dirty == -1) { - start_dirty = end_dirty; - } + if (clear_end > start_dirty) { 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, + ui_line(grid, row, invalid_row, coloff + start_dirty, coloff + end_dirty, coloff + clear_end, bg_attr, wrap); } else if (grid->dirty_col) { // TODO(bfredl): really get rid of the extra pseudo terminal in message.c diff --git a/src/nvim/message.c b/src/nvim/message.c index 2b668db7cd..35cf7d71fb 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -204,6 +204,7 @@ void msg_grid_validate(void) } else if (msg_grid.chars && !msg_scrolled && msg_grid_pos != max_rows) { msg_grid_set_pos(max_rows, false); } + msg_grid_adj.cols = Columns; if (msg_grid.chars && !msg_scrolled && cmdline_row < msg_grid_pos) { // TODO(bfredl): this should already be the case, but fails in some @@ -2349,7 +2350,7 @@ void msg_scroll_flush(void) for (int i = MAX(Rows - MAX(delta, 1), 0); i < Rows; i++) { int row = i - msg_grid_pos; assert(row >= 0); - ui_line(&msg_grid, row, 0, msg_grid.dirty_col[row], msg_grid.cols, + ui_line(&msg_grid, row, false, 0, msg_grid.dirty_col[row], msg_grid.cols, HL_ATTR(HLF_MSG), false); msg_grid.dirty_col[row] = 0; } diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 9116bb22a2..5303a25acc 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -448,13 +448,12 @@ void ui_set_ext_option(UI *ui, UIExtension ext, bool active) } } -void ui_line(ScreenGrid *grid, int row, int startcol, int endcol, int clearcol, int clearattr, - bool wrap) +void ui_line(ScreenGrid *grid, int row, bool invalid_row, int startcol, int endcol, int clearcol, + int clearattr, bool wrap) { assert(0 <= row && row < grid->rows); LineFlags flags = wrap ? kLineFlagWrap : 0; - if (startcol == -1) { - startcol = 0; + if (startcol == 0 && invalid_row) { flags |= kLineFlagInvalid; } |