diff options
Diffstat (limited to 'src/nvim/grid.c')
-rw-r--r-- | src/nvim/grid.c | 308 |
1 files changed, 156 insertions, 152 deletions
diff --git a/src/nvim/grid.c b/src/nvim/grid.c index 2ef89b778e..e386853022 100644 --- a/src/nvim/grid.c +++ b/src/nvim/grid.c @@ -24,11 +24,14 @@ #include "nvim/highlight.h" #include "nvim/log.h" #include "nvim/map_defs.h" +#include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/option_vars.h" +#include "nvim/optionstr.h" #include "nvim/types_defs.h" #include "nvim/ui.h" +#include "nvim/ui_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "grid.c.generated.h" @@ -67,7 +70,7 @@ void grid_adjust(ScreenGrid **grid, int *row_off, int *col_off) } } -schar_T schar_from_str(char *str) +schar_T schar_from_str(const char *str) { if (str == NULL) { return 0; @@ -120,6 +123,13 @@ void schar_cache_clear(void) { decor_check_invalid_glyphs(); set_clear(glyph, &glyph_cache); + + // for char options we have stored the original strings. Regenerate + // the parsed schar_T values with the new clean cache. + // This must not return an error as cell widths have not changed. + if (check_chars_options()) { + abort(); + } } bool schar_high(schar_T sc) @@ -137,15 +147,39 @@ bool schar_high(schar_T sc) # define schar_idx(sc) (sc >> 8) #endif -void schar_get(char *buf_out, schar_T sc) +/// sets final NUL +size_t schar_get(char *buf_out, schar_T sc) +{ + size_t len = schar_get_adv(&buf_out, sc); + *buf_out = NUL; + return len; +} + +/// advance buf_out. do NOT set final NUL +size_t schar_get_adv(char **buf_out, schar_T sc) +{ + size_t len; + if (schar_high(sc)) { + uint32_t idx = schar_idx(sc); + assert(idx < glyph_cache.h.n_keys); + len = strlen(&glyph_cache.keys[idx]); + memcpy(*buf_out, &glyph_cache.keys[idx], len); + } else { + len = strnlen((char *)&sc, 4); + memcpy(*buf_out, (char *)&sc, len); + } + *buf_out += len; + return len; +} + +size_t schar_len(schar_T sc) { if (schar_high(sc)) { uint32_t idx = schar_idx(sc); assert(idx < glyph_cache.h.n_keys); - xstrlcpy(buf_out, &glyph_cache.keys[idx], 32); + return strlen(&glyph_cache.keys[idx]); } else { - memcpy(buf_out, (char *)&sc, 4); - buf_out[4] = NUL; + return strnlen((char *)&sc, 4); } } @@ -243,7 +277,7 @@ void line_do_arabic_shape(schar_T *buf, int cols) // Too bigly, discard one code-point. // This should be enough as c0 cannot grow more than from 2 to 4 bytes // (base arabic to extended arabic) - rest -= (size_t)utf_cp_head_off(scbuf + off, scbuf + off + rest - 1) + 1; + rest -= (size_t)utf_cp_bounds(scbuf + off, scbuf + off + rest - 1).begin_off + 1; } memcpy(scbuf_new + len, scbuf + off, rest); buf[i] = schar_from_buf(scbuf_new, len + rest); @@ -263,13 +297,13 @@ void grid_clear_line(ScreenGrid *grid, size_t off, int width, bool valid) grid->chars[off + (size_t)col] = schar_from_ascii(' '); } int fill = valid ? 0 : -1; - (void)memset(grid->attrs + off, fill, (size_t)width * sizeof(sattr_T)); - (void)memset(grid->vcols + off, -1, (size_t)width * sizeof(colnr_T)); + memset(grid->attrs + off, fill, (size_t)width * sizeof(sattr_T)); + memset(grid->vcols + off, -1, (size_t)width * sizeof(colnr_T)); } void grid_invalidate(ScreenGrid *grid) { - (void)memset(grid->attrs, -1, sizeof(sattr_T) * (size_t)grid->rows * (size_t)grid->cols); + memset(grid->attrs, -1, sizeof(sattr_T) * (size_t)grid->rows * (size_t)grid->cols); } static bool grid_invalid_row(ScreenGrid *grid, int row) @@ -302,6 +336,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. /// @@ -310,14 +347,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); @@ -433,14 +474,13 @@ 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, int c, 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; } - schar_T sc = schar_from_char(c); for (int col = start_col; col < end_col; col++) { linebuf_char[col] = sc; linebuf_attr[col] = attr; @@ -449,6 +489,17 @@ void grid_line_fill(int start_col, int end_col, int c, 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. @@ -459,13 +510,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; @@ -498,8 +551,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. @@ -507,13 +561,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 @@ -532,89 +587,17 @@ void grid_line_flush_if_valid_row(void) grid_line_flush(); } -/// 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, int c1, - int c2, int attr) +void grid_clear(ScreenGrid *grid, int start_row, int end_row, int start_col, int end_col, int attr) { - int row_off = 0, 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 = schar_from_char(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 = schar_from_char(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(); } } @@ -650,8 +633,6 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol { bool redraw_next; // redraw_this for next character bool clear_next = false; - int char_cells; // 1: normal char - // 2: occupies two display cells assert(0 <= row && row < grid->rows); // TODO(bfredl): check all callsites and eliminate // Check for illegal col, just in case @@ -673,22 +654,16 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol // 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('>'); + linebuf_attr[col - 1] = grid->attrs[off_to + (size_t)col - 1]; 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_fill(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) { @@ -701,17 +676,19 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol } } - redraw_next = grid_char_needs_redraw(grid, col, (size_t)col + off_to, endcol - col); + redraw_next = grid_char_needs_redraw(grid, col, off_to + (size_t)col, endcol - col); - int start_dirty = -1, end_dirty = 0; + int start_dirty = -1; + int end_dirty = 0; while (col < endcol) { - char_cells = 1; + int char_cells = 1; // 1: normal char + // 2: occupies two display cells if (col + 1 < endcol && linebuf_char[col + 1] == 0) { char_cells = 2; } bool redraw_this = redraw_next; // Does character need redraw? - size_t off = (size_t)col + off_to; + size_t off = off_to + (size_t)col; redraw_next = grid_char_needs_redraw(grid, col + char_cells, off + (size_t)char_cells, endcol - col - char_cells); @@ -755,49 +732,59 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol 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[(size_t)col + off_to] = schar_from_ascii(' '); + grid->chars[off_to + (size_t)col] = schar_from_ascii(' '); 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; + // When clearing the left half of a double-wide char also clear the right half. + if (off_to + (size_t)clear_width < max_off_to + && grid->chars[off_to + (size_t)clear_width] == 0) { + clear_width++; + } + + 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 = off_to + (size_t)col; + 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 @@ -879,15 +866,20 @@ void grid_free(ScreenGrid *grid) grid->line_offset = NULL; } +#ifdef EXITFREE /// Doesn't allow reinit, so must only be called by free_all_mem! void grid_free_all_mem(void) { grid_free(&default_grid); + grid_free(&msg_grid); + XFREE_CLEAR(msg_grid.dirty_col); xfree(linebuf_char); xfree(linebuf_attr); xfree(linebuf_vcol); xfree(linebuf_scratch); + set_destroy(glyph, &glyph_cache); } +#endif /// (Re)allocates a window grid if size changed while in ext_multigrid mode. /// Updates size, offsets and handle for the grid regardless. @@ -913,14 +905,14 @@ void win_grid_alloc(win_T *wp) wp->w_lines = xcalloc((size_t)rows + 1, sizeof(wline_T)); } - int was_resized = false; + bool was_resized = false; if (want_allocation && (!has_allocation || grid_allocated->rows != total_rows || grid_allocated->cols != total_cols)) { grid_alloc(grid_allocated, total_rows, total_cols, wp->w_grid_alloc.valid, false); grid_allocated->valid = true; - if (wp->w_floating && wp->w_float_config.border) { + if (wp->w_floating && wp->w_config.border) { wp->w_redr_border = true; } was_resized = true; @@ -1082,3 +1074,15 @@ win_T *get_win_by_grid_handle(handle_T handle) } return NULL; } + +/// Put a unicode character in a screen cell. +schar_T schar_from_char(int c) +{ + schar_T sc = 0; + if (c >= 0x200000) { + // TODO(bfredl): this must NEVER happen, even if the file contained overlong sequences + c = 0xFFFD; + } + utf_char2bytes(c, (char *)&sc); + return sc; +} |