aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/grid.c
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2023-11-30 20:35:25 +0000
committerJosh Rahm <joshuarahm@gmail.com>2023-11-30 20:35:25 +0000
commit1b7b916b7631ddf73c38e3a0070d64e4636cb2f3 (patch)
treecd08258054db80bb9a11b1061bb091c70b76926a /src/nvim/grid.c
parenteaa89c11d0f8aefbb512de769c6c82f61a8baca3 (diff)
parent4a8bf24ac690004aedf5540fa440e788459e5e34 (diff)
downloadrneovim-1b7b916b7631ddf73c38e3a0070d64e4636cb2f3.tar.gz
rneovim-1b7b916b7631ddf73c38e3a0070d64e4636cb2f3.tar.bz2
rneovim-1b7b916b7631ddf73c38e3a0070d64e4636cb2f3.zip
Merge remote-tracking branch 'upstream/master' into aucmd_textputpostaucmd_textputpost
Diffstat (limited to 'src/nvim/grid.c')
-rw-r--r--src/nvim/grid.c885
1 files changed, 523 insertions, 362 deletions
diff --git a/src/nvim/grid.c b/src/nvim/grid.c
index 46f8a59710..2ef89b778e 100644
--- a/src/nvim/grid.c
+++ b/src/nvim/grid.c
@@ -1,6 +1,3 @@
-// This is an open source non-commercial project. Dear PVS-Studio, please check
-// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-
// Most of the routines in this file perform screen (grid) manipulations. The
// given operation is performed physically on the screen. The corresponding
// change is also made to the internal screen image. In this way, the editor
@@ -13,19 +10,25 @@
#include <assert.h>
#include <limits.h>
+#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
+#include "nvim/api/private/defs.h"
#include "nvim/arabic.h"
+#include "nvim/ascii_defs.h"
#include "nvim/buffer_defs.h"
+#include "nvim/decoration.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
#include "nvim/highlight.h"
#include "nvim/log.h"
+#include "nvim/map_defs.h"
+#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/option_defs.h"
-#include "nvim/types.h"
+#include "nvim/option_vars.h"
+#include "nvim/types_defs.h"
#include "nvim/ui.h"
-#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "grid.c.generated.h"
@@ -36,6 +39,15 @@
// Per-cell attributes
static size_t linebuf_size = 0;
+// Used to cache glyphs which doesn't fit an a sizeof(schar_T) length UTF-8 string.
+// Then it instead stores an index into glyph_cache.keys[] which is a flat char array.
+// The hash part is used by schar_from_buf() to quickly lookup glyphs which already
+// has been interned. schar_get() should used to convert a schar_T value
+// back to a string buffer.
+//
+// The maximum byte size of a glyph is MAX_SCHAR_SIZE (including the final NUL).
+static Set(glyph) glyph_cache = SET_INIT;
+
/// Determine if dedicated window grid should be used or the default_grid
///
/// If UI did not request multigrid support, draw all windows on the
@@ -55,320 +67,469 @@ void grid_adjust(ScreenGrid **grid, int *row_off, int *col_off)
}
}
-/// Put a unicode char, and up to MAX_MCO composing chars, in a screen cell.
-int schar_from_cc(char *p, int c, int u8cc[MAX_MCO])
+schar_T schar_from_str(char *str)
{
- int len = utf_char2bytes(c, p);
- for (int i = 0; i < MAX_MCO; i++) {
- if (u8cc[i] == 0) {
- break;
- }
- len += utf_char2bytes(u8cc[i], p + len);
+ if (str == NULL) {
+ return 0;
}
- p[len] = 0;
- return len;
+ return schar_from_buf(str, strlen(str));
}
-/// clear a line in the grid starting at "off" until "width" characters
-/// are cleared.
-void grid_clear_line(ScreenGrid *grid, size_t off, int width, bool valid)
+/// @param buf need not be NUL terminated, but may not contain embedded NULs.
+///
+/// caller must ensure len < MAX_SCHAR_SIZE (not =, as NUL needs a byte)
+schar_T schar_from_buf(const char *buf, size_t len)
{
- for (int col = 0; col < width; col++) {
- schar_from_ascii(grid->chars[off + (size_t)col], ' ');
+ assert(len < MAX_SCHAR_SIZE);
+ if (len <= 4) {
+ schar_T sc = 0;
+ memcpy((char *)&sc, buf, len);
+ return sc;
+ } else {
+ String str = { .data = (char *)buf, .size = len };
+
+ MHPutStatus status;
+ uint32_t idx = set_put_idx(glyph, &glyph_cache, str, &status);
+ assert(idx < 0xFFFFFF);
+#ifdef ORDER_BIG_ENDIAN
+ return idx + ((uint32_t)0xFF << 24);
+#else
+ return 0xFF + (idx << 8);
+#endif
}
- int fill = valid ? 0 : -1;
- (void)memset(grid->attrs + off, fill, (size_t)width * sizeof(sattr_T));
}
-void grid_invalidate(ScreenGrid *grid)
+/// Check if cache is full, and if it is, clear it.
+///
+/// This should normally only be called in update_screen()
+///
+/// @return true if cache was clered, and all your screen buffers now are hosed
+/// and you need to use UPD_CLEAR
+bool schar_cache_clear_if_full(void)
{
- (void)memset(grid->attrs, -1, sizeof(sattr_T) * (size_t)grid->rows * (size_t)grid->cols);
+ // note: critical max is really (1<<24)-1. This gives us some marginal
+ // until next time update_screen() is called
+ if (glyph_cache.h.n_keys > (1<<21)) {
+ schar_cache_clear();
+ return true;
+ }
+ return false;
}
-bool grid_invalid_row(ScreenGrid *grid, int row)
+void schar_cache_clear(void)
{
- return grid->attrs[grid->line_offset[row]] < 0;
+ decor_check_invalid_glyphs();
+ set_clear(glyph, &glyph_cache);
}
-static int line_off2cells(schar_T *line, size_t off, size_t max_off)
+bool schar_high(schar_T sc)
{
- return (off + 1 < max_off && line[off + 1][0] == 0) ? 2 : 1;
+#ifdef ORDER_BIG_ENDIAN
+ return ((sc & 0xFF000000) == 0xFF000000);
+#else
+ return ((sc & 0xFF) == 0xFF);
+#endif
}
-/// 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)
+#ifdef ORDER_BIG_ENDIAN
+# define schar_idx(sc) (sc & (0x00FFFFFF))
+#else
+# define schar_idx(sc) (sc >> 8)
+#endif
+
+void schar_get(char *buf_out, schar_T sc)
{
- return line_off2cells(grid->chars, off, max_off);
+ 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);
+ } else {
+ memcpy(buf_out, (char *)&sc, 4);
+ buf_out[4] = NUL;
+ }
}
-/// 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)
+/// gets first raw UTF-8 byte of an schar
+static char schar_get_first_byte(schar_T sc)
{
- grid_adjust(&grid, &row, &col);
+ assert(!(schar_high(sc) && schar_idx(sc) >= glyph_cache.h.n_keys));
+ return schar_high(sc) ? glyph_cache.keys[schar_idx(sc)] : *(char *)&sc;
+}
- return grid_off2cells(grid, grid->line_offset[row] + (size_t)col,
- grid->line_offset[row] + (size_t)grid->cols) > 1;
+int schar_get_first_codepoint(schar_T sc)
+{
+ char sc_buf[MAX_SCHAR_SIZE];
+ schar_get(sc_buf, sc);
+ return utf_ptr2char(sc_buf);
}
-/// 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)
+/// @return ascii char or NUL if not ascii
+char schar_get_ascii(schar_T sc)
{
- int coloff = 0;
- grid_adjust(&grid, &row, &coloff);
+#ifdef ORDER_BIG_ENDIAN
+ return (!(sc & 0x80FFFFFF)) ? *(char *)&sc : NUL;
+#else
+ return (sc < 0x80) ? (char)sc : NUL;
+#endif
+}
- col += coloff;
- if (grid->chars != NULL && col > 0
- && grid->chars[grid->line_offset[row] + (size_t)col][0] == 0) {
- return col - 1 - coloff;
- }
- return col - coloff;
+static bool schar_in_arabic_block(schar_T sc)
+{
+ char first_byte = schar_get_first_byte(sc);
+ return ((uint8_t)first_byte & 0xFE) == 0xD8;
}
-/// output a single character directly to the grid
-void grid_putchar(ScreenGrid *grid, int c, int row, int col, int attr)
+/// Get the first two codepoints of an schar, or NUL when not available
+static void schar_get_first_two_codepoints(schar_T sc, int *c0, int *c1)
{
- char buf[MB_MAXBYTES + 1];
+ char sc_buf[MAX_SCHAR_SIZE];
+ schar_get(sc_buf, sc);
- buf[utf_char2bytes(c, buf)] = NUL;
- grid_puts(grid, buf, row, col, attr);
+ *c0 = utf_ptr2char(sc_buf);
+ int len = utf_ptr2len(sc_buf);
+ if (*c0 == NUL) {
+ *c1 = NUL;
+ } else {
+ *c1 = utf_ptr2char(sc_buf + len);
+ }
}
-/// get a single character directly from grid.chars into "bytes[]".
-/// Also return its attribute in *attrp;
-void grid_getbytes(ScreenGrid *grid, int row, int col, char *bytes, int *attrp)
+void line_do_arabic_shape(schar_T *buf, int cols)
{
- grid_adjust(&grid, &row, &col);
+ int i = 0;
- // safety check
- if (grid->chars == NULL || row >= grid->rows || col >= grid->cols) {
+ for (i = 0; i < cols; i++) {
+ // quickly skip over non-arabic text
+ if (schar_in_arabic_block(buf[i])) {
+ break;
+ }
+ }
+
+ if (i == cols) {
return;
}
- size_t off = grid->line_offset[row] + (size_t)col;
- *attrp = grid->attrs[off];
- schar_copy(bytes, grid->chars[off]);
+ int c0prev = 0;
+ int c0, c1;
+ schar_get_first_two_codepoints(buf[i], &c0, &c1);
+
+ for (; i < cols; i++) {
+ int c0next, c1next;
+ schar_get_first_two_codepoints(i + 1 < cols ? buf[i + 1] : 0, &c0next, &c1next);
+
+ if (!ARABIC_CHAR(c0)) {
+ goto next;
+ }
+
+ int c1new = c1;
+ int c0new = arabic_shape(c0, &c1new, c0next, c1next, c0prev);
+
+ if (c0new == c0 && c1new == c1) {
+ goto next; // unchanged
+ }
+
+ char scbuf[MAX_SCHAR_SIZE];
+ schar_get(scbuf, buf[i]);
+
+ char scbuf_new[MAX_SCHAR_SIZE];
+ size_t len = (size_t)utf_char2bytes(c0new, scbuf_new);
+ if (c1new) {
+ len += (size_t)utf_char2bytes(c1new, scbuf_new + len);
+ }
+
+ int off = utf_char2len(c0) + (c1 ? utf_char2len(c1) : 0);
+ size_t rest = strlen(scbuf + off);
+ if (rest + len + 1 > MAX_SCHAR_SIZE) {
+ // 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;
+ }
+ memcpy(scbuf_new + len, scbuf + off, rest);
+ buf[i] = schar_from_buf(scbuf_new, len + rest);
+
+next:
+ c0prev = c0;
+ c0 = c0next;
+ c1 = c1next;
+ }
}
-/// put string '*text' on the window grid at position 'row' and 'col', with
-/// attributes 'attr', and update chars[] and attrs[].
-/// Note: only outputs within one row, message is truncated at grid boundary!
-/// Note: if grid, row and/or col is invalid, nothing is done.
-void grid_puts(ScreenGrid *grid, char *text, int row, int col, int attr)
+/// clear a line in the grid starting at "off" until "width" characters
+/// are cleared.
+void grid_clear_line(ScreenGrid *grid, size_t off, int width, bool valid)
{
- grid_puts_len(grid, text, -1, row, col, attr);
+ for (int col = 0; col < width; col++) {
+ 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));
}
-static ScreenGrid *put_dirty_grid = NULL;
-static int put_dirty_row = -1;
-static int put_dirty_first = INT_MAX;
-static int put_dirty_last = 0;
+void grid_invalidate(ScreenGrid *grid)
+{
+ (void)memset(grid->attrs, -1, sizeof(sattr_T) * (size_t)grid->rows * (size_t)grid->cols);
+}
-/// Start a group of grid_puts_len calls that builds a single grid line.
-///
-/// Must be matched with a grid_puts_line_flush call before moving to
-/// another line.
-void grid_puts_line_start(ScreenGrid *grid, int row)
+static bool grid_invalid_row(ScreenGrid *grid, int row)
{
- int col = 0; // unused
- grid_adjust(&grid, &row, &col);
- assert(put_dirty_row == -1);
- put_dirty_row = row;
- put_dirty_grid = grid;
+ return grid->attrs[grid->line_offset[row]] < 0;
}
-void grid_put_schar(ScreenGrid *grid, int row, int col, char *schar, int attr)
+/// Get a single character directly from grid.chars
+///
+/// @param[out] attrp set to the character's attribute (optional)
+schar_T grid_getchar(ScreenGrid *grid, int row, int col, int *attrp)
{
- assert(put_dirty_row == row);
- size_t off = grid->line_offset[row] + (size_t)col;
- if (grid->attrs[off] != attr || schar_cmp(grid->chars[off], schar) || rdb_flags & RDB_NODELTA) {
- schar_copy(grid->chars[off], schar);
- grid->attrs[off] = attr;
+ grid_adjust(&grid, &row, &col);
+
+ // safety check
+ if (grid->chars == NULL || row >= grid->rows || col >= grid->cols) {
+ return NUL;
+ }
- put_dirty_first = MIN(put_dirty_first, col);
- // TODO(bfredl): Y U NO DOUBLEWIDTH?
- put_dirty_last = MAX(put_dirty_last, col + 1);
+ size_t off = grid->line_offset[row] + (size_t)col;
+ if (attrp != NULL) {
+ *attrp = grid->attrs[off];
}
+ return grid->chars[off];
}
-/// like grid_puts(), but output "text[len]". When "len" is -1 output up to
-/// a NUL.
-void grid_puts_len(ScreenGrid *grid, char *text, int textlen, int row, int col, int attr)
-{
- size_t off;
- char *ptr = text;
- int len = textlen;
- int c;
- size_t max_off;
- int mbyte_blen = 1;
- int mbyte_cells = 1;
- int u8c = 0;
- int u8cc[MAX_MCO];
- bool clear_next_cell = false;
- int prev_c = 0; // previous Arabic character
- int pc, nc, nc1;
- int pcc[MAX_MCO];
- int need_redraw;
- bool do_flush = false;
+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;
+/// 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
+/// another line.
+void grid_line_start(ScreenGrid *grid, int row)
+{
+ int col = 0;
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;
+ 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_last = 0;
+
+ assert((size_t)grid_line_maxcol <= linebuf_size);
+
+ if (rdb_flags & RDB_INVALID) {
+ // Current batch must not depend on previous contents of linebuf_char.
+ // Set invalid values which will cause assertion failures later if they are used.
+ memset(linebuf_char, 0xFF, sizeof(schar_T) * linebuf_size);
+ memset(linebuf_attr, 0xFF, sizeof(sattr_T) * linebuf_size);
}
+}
- if (put_dirty_row == -1) {
- grid_puts_line_start(grid, row);
- do_flush = true;
- } else {
- if (grid != put_dirty_grid || row != put_dirty_row) {
- abort();
+/// 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) {
+ col += grid_line_coloff;
+ 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(' ');
}
- off = grid->line_offset[row] + (size_t)col;
+}
- // When drawing over the right half of a double-wide char clear out the
- // left half. Only needed in a terminal.
- if (grid != &default_grid && col == 0 && grid_invalid_row(grid, row)) {
- // redraw the previous cell, make it empty
- put_dirty_first = -1;
- put_dirty_last = MAX(put_dirty_last, 1);
+void grid_line_put_schar(int col, schar_T schar, int attr)
+{
+ assert(grid_line_grid);
+ if (col >= grid_line_maxcol) {
+ return;
}
- max_off = grid->line_offset[row] + (size_t)grid->cols;
- while (col < grid->cols
- && (len < 0 || (int)(ptr - text) < len)
- && *ptr != NUL) {
- c = (unsigned char)(*ptr);
+ 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);
+ linebuf_vcol[col] = -1;
+}
+
+/// 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;
+ int len = textlen;
+
+ assert(grid_line_grid);
+
+ int start_col = col;
+
+ const int max_col = grid_line_maxcol;
+ while (col < max_col && (len < 0 || (int)(ptr - text) < len) && *ptr != NUL) {
// check if this is the first byte of a multibyte
- mbyte_blen = len > 0
- ? utfc_ptr2len_len(ptr, (int)((text + len) - ptr))
- : utfc_ptr2len(ptr);
- u8c = len >= 0
- ? utfc_ptr2char_len(ptr, u8cc, (int)((text + len) - ptr))
- : utfc_ptr2char(ptr, u8cc);
- mbyte_cells = utf_char2cells(u8c);
- if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) {
- // Do Arabic shaping.
- if (len >= 0 && (int)(ptr - text) + mbyte_blen >= len) {
- // Past end of string to be displayed.
- nc = NUL;
- nc1 = NUL;
- } else {
- nc = len >= 0
- ? utfc_ptr2char_len(ptr + mbyte_blen, pcc,
- (int)((text + len) - ptr - mbyte_blen))
- : utfc_ptr2char(ptr + mbyte_blen, pcc);
- nc1 = pcc[0];
- }
- pc = prev_c;
- prev_c = u8c;
- u8c = arabic_shape(u8c, &c, &u8cc[0], nc, nc1, pc);
- } else {
- prev_c = u8c;
+ int mbyte_blen = len > 0
+ ? utfc_ptr2len_len(ptr, (int)((text + len) - ptr))
+ : utfc_ptr2len(ptr);
+ int firstc;
+ schar_T schar = len >= 0
+ ? utfc_ptr2schar_len(ptr, (int)((text + len) - ptr), &firstc)
+ : utfc_ptr2schar(ptr, &firstc);
+ int mbyte_cells = utf_char2cells(firstc);
+ if (mbyte_cells > 2) {
+ mbyte_cells = 1;
+
+ schar = schar_from_char(0xFFFD);
}
- 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 = '>';
- u8c = '>';
- u8cc[0] = 0;
+ schar = schar_from_ascii('>');
mbyte_cells = 1;
}
- schar_T buf;
- schar_from_cc(buf, u8c, u8cc);
-
- need_redraw = schar_cmp(grid->chars[off], buf)
- || (mbyte_cells == 2 && grid->chars[off + 1][0] != 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] == 0) {
- grid->chars[off - 1][0] = '>';
- grid->chars[off - 1][1] = 0;
- }
+ // 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('>');
+ }
- schar_copy(grid->chars[off], buf);
- grid->attrs[off] = attr;
- if (mbyte_cells == 2) {
- grid->chars[off + 1][0] = 0;
- grid->attrs[off + 1] = attr;
- }
- put_dirty_first = MIN(put_dirty_first, col);
- put_dirty_last = MAX(put_dirty_last, col + mbyte_cells);
+ linebuf_char[col] = schar;
+ 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;
+}
+
+void grid_line_fill(int start_col, int end_col, int c, int attr)
+{
+ end_col = MIN(end_col, grid_line_maxcol);
+ if (start_col >= end_col) {
+ return;
+ }
+
+ schar_T sc = schar_from_char(c);
+ for (int col = start_col; col < end_col; col++) {
+ 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);
+}
+
+/// 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);
+}
+
+void grid_line_mirror(void)
+{
+ if (grid_line_first >= grid_line_last) {
+ return;
+ }
+ linebuf_mirror(&grid_line_first, &grid_line_last, grid_line_maxcol);
+}
+
+void linebuf_mirror(int *firstp, int *lastp, int maxcol)
+{
+ int first = *firstp;
+ int last = *lastp;
+
+ size_t n = (size_t)(last - first);
+ int mirror = maxcol - 1; // Mirrors are more fun than television.
+ schar_T *scratch_char = (schar_T *)linebuf_scratch;
+ memcpy(scratch_char + first, linebuf_char + first, n * sizeof(schar_T));
+ for (int col = first; col < last; col++) {
+ int rev = mirror - col;
+ if (col + 1 < last && scratch_char[col + 1] == 0) {
+ linebuf_char[rev - 1] = scratch_char[col];
+ linebuf_char[rev] = 0;
+ col++;
+ } else {
+ linebuf_char[rev] = scratch_char[col];
}
}
- if (do_flush) {
- grid_puts_line_flush(true);
+ // for attr and vcol: assumes doublewidth chars are self-consistent
+ sattr_T *scratch_attr = (sattr_T *)linebuf_scratch;
+ memcpy(scratch_attr + first, linebuf_attr + first, n * sizeof(sattr_T));
+ for (int col = first; col < last; col++) {
+ linebuf_attr[mirror - col] = scratch_attr[col];
+ }
+
+ colnr_T *scratch_vcol = (colnr_T *)linebuf_scratch;
+ memcpy(scratch_vcol + first, linebuf_vcol + first, n * sizeof(colnr_T));
+ for (int col = first; col < last; col++) {
+ linebuf_vcol[mirror - col] = scratch_vcol[col];
+ }
+
+ *lastp = maxcol - first;
+ *firstp = maxcol - last;
+}
+
+/// End a group of grid_line_puts calls and send the screen buffer to the UI layer.
+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) {
+ return;
}
+
+ grid_put_linebuf(grid, grid_line_row, grid_line_coloff, grid_line_first, grid_line_last,
+ grid_line_last, false, 0, false);
}
-/// End a group of grid_puts_len calls and send the screen buffer to the UI
-/// layer.
+/// flush grid line but only if on a valid row
///
-/// @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_puts_line_flush(bool set_cursor)
-{
- assert(put_dirty_row != -1);
- if (put_dirty_first < put_dirty_last) {
- if (set_cursor) {
- ui_grid_cursor_goto(put_dirty_grid->handle, put_dirty_row,
- MIN(put_dirty_last, put_dirty_grid->cols - 1));
- }
- if (!put_dirty_grid->throttled) {
- ui_line(put_dirty_grid, put_dirty_row, put_dirty_first, put_dirty_last,
- put_dirty_last, 0, false);
- } else if (put_dirty_grid->dirty_col) {
- if (put_dirty_last > put_dirty_grid->dirty_col[put_dirty_row]) {
- put_dirty_grid->dirty_col[put_dirty_row] = put_dirty_last;
- }
+/// This is a stopgap until message.c has been refactored to behave
+void grid_line_flush_if_valid_row(void)
+{
+ if (grid_line_row < 0 || grid_line_row >= grid_line_grid->rows) {
+ if (rdb_flags & RDB_INVALID) {
+ abort();
+ } else {
+ grid_line_grid = NULL;
+ return;
}
- put_dirty_first = INT_MAX;
- put_dirty_last = 0;
}
- put_dirty_row = -1;
- put_dirty_grid = NULL;
+ grid_line_flush();
}
/// Fill the grid from "start_row" to "end_row" (exclusive), from "start_col"
@@ -377,8 +538,6 @@ void grid_puts_line_flush(bool set_cursor)
void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int end_col, int c1,
int c2, int attr)
{
- schar_T sc;
-
int row_off = 0, col_off = 0;
grid_adjust(&grid, &row_off, &col_off);
start_row += row_off;
@@ -400,47 +559,46 @@ 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_len(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_len(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;
- schar_from_char(sc, c1);
- size_t lineoff = grid->line_offset[row];
+ schar_T sc = schar_from_char(c1);
for (col = start_col; col < end_col; col++) {
size_t off = lineoff + (size_t)col;
- if (schar_cmp(grid->chars[off], sc) || grid->attrs[off] != attr || rdb_flags & RDB_NODELTA) {
- schar_copy(grid->chars[off], sc);
+ 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) {
- schar_from_char(sc, c2);
+ sc = schar_from_char(c2);
}
}
if (dirty_last > dirty_first) {
- // TODO(bfredl): support a cleared suffix even with a batched line?
- if (put_dirty_row == row) {
- put_dirty_first = MIN(put_dirty_first, dirty_first);
- put_dirty_last = MAX(put_dirty_last, dirty_last);
- } else if (grid->throttled) {
+ if (grid->throttled) {
// Note: assumes msg_grid is the only throttled grid
assert(grid == &msg_grid);
int dirty = 0;
@@ -457,10 +615,6 @@ void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int
ui_line(grid, row, dirty_first, last, dirty_last, attr, false);
}
}
-
- if (end_col == grid->cols) {
- grid->line_wraps[row] = false;
- }
}
}
@@ -469,14 +623,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
- && ((schar_cmp(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
- && schar_cmp(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));
}
@@ -486,53 +640,47 @@ 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,
+ bool rl, int bg_attr, bool wrap)
{
- size_t max_off_from;
- size_t max_off_to;
- int col = 0;
- bool redraw_this; // Does character need redraw?
bool redraw_next; // redraw_this for next character
bool clear_next = false;
int char_cells; // 1: normal char
// 2: occupies two display cells
- int start_dirty = -1, end_dirty = 0;
-
+ assert(0 <= row && row < grid->rows);
// TODO(bfredl): check all callsites and eliminate
- // Check for illegal row and col, just in case
- if (row >= grid->rows) {
- row = grid->rows - 1;
- }
+ // Check for illegal col, just in case
if (endcol > grid->cols) {
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");
return;
}
- size_t off_from = 0;
+ bool invalid_row = grid != &default_grid && grid_invalid_row(grid, row) && col == 0;
size_t off_to = grid->line_offset[row] + (size_t)coloff;
- max_off_from = linebuf_size;
- max_off_to = grid->line_offset[row] + (size_t)grid->cols;
+ const size_t max_off_to = grid->line_offset[row] + (size_t)grid->cols;
+
+ // 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][0] == ' '
- && grid->chars[off_to][1] == NUL
- && 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) {
@@ -540,28 +688,32 @@ 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);
+ endcol = clear_width;
+ }
+
+ if (p_arshape && !p_tbidi && endcol > col) {
+ line_do_arabic_shape(linebuf_char + col, endcol - col);
}
if (bg_attr) {
for (int c = col; c < endcol; c++) {
- linebuf_attr[off_from + (size_t)c] =
- hl_combine_attr(bg_attr, linebuf_attr[off_from + (size_t)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);
+
+ int start_dirty = -1, end_dirty = 0;
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;
}
- redraw_this = redraw_next;
- redraw_next = grid_char_needs_redraw(grid, off_from + (size_t)char_cells,
- off_to + (size_t)char_cells,
+ bool redraw_this = redraw_next; // Does character need redraw?
+ 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) {
@@ -574,52 +726,50 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle
// 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_to, 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))) {
+ if (col + char_cells == endcol && off + (size_t)char_cells < max_off_to
+ && grid->chars[off + (size_t)char_cells] == NUL) {
clear_next = true;
}
- schar_copy(grid->chars[off_to], linebuf_char[off_from]);
+ grid->chars[off] = linebuf_char[col];
if (char_cells == 2) {
- schar_copy(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];
}
}
- off_to += (size_t)char_cells;
- off_from += (size_t)char_cells;
+ grid->vcols[off] = linebuf_vcol[col];
+ if (char_cells == 2) {
+ grid->vcols[off + 1] = linebuf_vcol[col + 1];
+ }
+
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.
- schar_from_ascii(grid->chars[off_to], ' ');
+ 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][0] != ' '
- || grid->chars[off_to][1] != NUL
- || 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][0] = ' ';
- grid->chars[off_to][1] = NUL;
- 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;
@@ -628,17 +778,11 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle
}
clear_end = col + 1;
}
+ grid->vcols[off] = MAXCOL;
col++;
- off_to++;
}
}
- if (clear_width > 0 || wp->w_width != grid->cols) {
- // If we cleared after the end of the line, it did not wrap.
- // For vsplit, line wrapping is not possible.
- grid->line_wraps[row] = false;
- }
-
if (clear_end < end_dirty) {
clear_end = end_dirty;
}
@@ -646,30 +790,44 @@ 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 pseudo 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;
+ }
+ }
}
}
void grid_alloc(ScreenGrid *grid, int rows, int columns, bool copy, bool valid)
{
int new_row;
- ScreenGrid new = *grid;
+ ScreenGrid ngrid = *grid;
assert(rows >= 0 && columns >= 0);
size_t ncells = (size_t)rows * (size_t)columns;
- new.chars = xmalloc(ncells * sizeof(schar_T));
- new.attrs = xmalloc(ncells * sizeof(sattr_T));
- new.line_offset = xmalloc((size_t)rows * sizeof(*new.line_offset));
- new.line_wraps = xmalloc((size_t)rows * sizeof(*new.line_wraps));
+ ngrid.chars = xmalloc(ncells * sizeof(schar_T));
+ ngrid.attrs = xmalloc(ncells * sizeof(sattr_T));
+ ngrid.vcols = xmalloc(ncells * sizeof(colnr_T));
+ memset(ngrid.vcols, -1, ncells * sizeof(colnr_T));
+ ngrid.line_offset = xmalloc((size_t)rows * sizeof(*ngrid.line_offset));
- new.rows = rows;
- new.cols = columns;
+ ngrid.rows = rows;
+ ngrid.cols = columns;
- for (new_row = 0; new_row < new.rows; new_row++) {
- new.line_offset[new_row] = (size_t)new_row * (size_t)new.cols;
- new.line_wraps[new_row] = false;
+ for (new_row = 0; new_row < ngrid.rows; new_row++) {
+ ngrid.line_offset[new_row] = (size_t)new_row * (size_t)ngrid.cols;
- grid_clear_line(&new, new.line_offset[new_row], columns, valid);
+ grid_clear_line(&ngrid, ngrid.line_offset[new_row], columns, valid);
if (copy) {
// If the screen is not going to be cleared, copy as much as
@@ -677,26 +835,33 @@ void grid_alloc(ScreenGrid *grid, int rows, int columns, bool copy, bool valid)
// (used when resizing the window at the "--more--" prompt or when
// executing an external command, for the GUI).
if (new_row < grid->rows && grid->chars != NULL) {
- int len = MIN(grid->cols, new.cols);
- memmove(new.chars + new.line_offset[new_row],
+ int len = MIN(grid->cols, ngrid.cols);
+ memmove(ngrid.chars + ngrid.line_offset[new_row],
grid->chars + grid->line_offset[new_row],
(size_t)len * sizeof(schar_T));
- memmove(new.attrs + new.line_offset[new_row],
+ memmove(ngrid.attrs + ngrid.line_offset[new_row],
grid->attrs + grid->line_offset[new_row],
(size_t)len * sizeof(sattr_T));
+ memmove(ngrid.vcols + ngrid.line_offset[new_row],
+ grid->vcols + grid->line_offset[new_row],
+ (size_t)len * sizeof(colnr_T));
}
}
}
grid_free(grid);
- *grid = new;
+ *grid = ngrid;
// Share a single scratch buffer for all grids, by
// ensuring it is as wide as the widest grid.
if (linebuf_size < (size_t)columns) {
xfree(linebuf_char);
xfree(linebuf_attr);
+ xfree(linebuf_vcol);
+ xfree(linebuf_scratch);
linebuf_char = xmalloc((size_t)columns * sizeof(schar_T));
linebuf_attr = xmalloc((size_t)columns * sizeof(sattr_T));
+ linebuf_vcol = xmalloc((size_t)columns * sizeof(colnr_T));
+ linebuf_scratch = xmalloc((size_t)columns * sizeof(sscratch_T));
linebuf_size = (size_t)columns;
}
}
@@ -705,13 +870,13 @@ void grid_free(ScreenGrid *grid)
{
xfree(grid->chars);
xfree(grid->attrs);
+ xfree(grid->vcols);
xfree(grid->line_offset);
- xfree(grid->line_wraps);
grid->chars = NULL;
grid->attrs = NULL;
+ grid->vcols = NULL;
grid->line_offset = NULL;
- grid->line_wraps = NULL;
}
/// Doesn't allow reinit, so must only be called by free_all_mem!
@@ -720,6 +885,8 @@ void grid_free_all_mem(void)
grid_free(&default_grid);
xfree(linebuf_char);
xfree(linebuf_attr);
+ xfree(linebuf_vcol);
+ xfree(linebuf_scratch);
}
/// (Re)allocates a window grid if size changed while in ext_multigrid mode.
@@ -788,6 +955,7 @@ void win_grid_alloc(win_T *wp)
if ((resizing_screen || was_resized) && want_allocation) {
ui_call_grid_resize(grid_allocated->handle,
grid_allocated->cols, grid_allocated->rows);
+ ui_check_cursor_grid(grid_allocated->handle);
}
}
@@ -810,7 +978,6 @@ void grid_assign_handle(ScreenGrid *grid)
/// 'row', 'col' and 'end' are relative to the start of the region.
void grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col, int width)
{
- int i;
int j;
unsigned temp;
@@ -825,7 +992,7 @@ void grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
// Shift line_offset[] line_count down to reflect the inserted lines.
// Clear the inserted lines.
- for (i = 0; i < line_count; i++) {
+ for (int i = 0; i < line_count; i++) {
if (width != grid->cols) {
// need to copy part of a line
j = end - 1 - i;
@@ -834,16 +1001,13 @@ void grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
}
j += line_count;
grid_clear_line(grid, grid->line_offset[j] + (size_t)col, width, false);
- grid->line_wraps[j] = false;
} else {
j = end - 1 - i;
temp = (unsigned)grid->line_offset[j];
while ((j -= line_count) >= row) {
grid->line_offset[j + line_count] = grid->line_offset[j];
- grid->line_wraps[j + line_count] = grid->line_wraps[j];
}
grid->line_offset[j + line_count] = temp;
- grid->line_wraps[j + line_count] = false;
grid_clear_line(grid, temp, grid->cols, false);
}
}
@@ -860,7 +1024,6 @@ void grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col, int width)
{
int j;
- int i;
unsigned temp;
int row_off = 0;
@@ -874,7 +1037,7 @@ void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
// Now shift line_offset[] line_count up to reflect the deleted lines.
// Clear the inserted lines.
- for (i = 0; i < line_count; i++) {
+ for (int i = 0; i < line_count; i++) {
if (width != grid->cols) {
// need to copy part of a line
j = row + i;
@@ -883,17 +1046,14 @@ void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
}
j -= line_count;
grid_clear_line(grid, grid->line_offset[j] + (size_t)col, width, false);
- grid->line_wraps[j] = false;
} else {
// whole width, moving the line pointers is faster
j = row + i;
temp = (unsigned)grid->line_offset[j];
while ((j += line_count) <= end - 1) {
grid->line_offset[j - line_count] = grid->line_offset[j];
- grid->line_wraps[j - line_count] = grid->line_wraps[j];
}
grid->line_offset[j - line_count] = temp;
- grid->line_wraps[j - line_count] = false;
grid_clear_line(grid, temp, grid->cols, false);
}
}
@@ -910,6 +1070,7 @@ static void linecopy(ScreenGrid *grid, int to, int from, int col, int width)
memmove(grid->chars + off_to, grid->chars + off_from, (size_t)width * sizeof(schar_T));
memmove(grid->attrs + off_to, grid->attrs + off_from, (size_t)width * sizeof(sattr_T));
+ memmove(grid->vcols + off_to, grid->vcols + off_from, (size_t)width * sizeof(colnr_T));
}
win_T *get_win_by_grid_handle(handle_T handle)