diff options
Diffstat (limited to 'src/nvim/grid.c')
-rw-r--r-- | src/nvim/grid.c | 154 |
1 files changed, 116 insertions, 38 deletions
diff --git a/src/nvim/grid.c b/src/nvim/grid.c index 2eab158bc4..7c8823e0d4 100644 --- a/src/nvim/grid.c +++ b/src/nvim/grid.c @@ -152,17 +152,17 @@ bool schar_high(schar_T sc) #endif } -void schar_get(char *buf_out, schar_T sc) -{ - if (schar_high(sc)) { #ifdef ORDER_BIG_ENDIAN - uint32_t idx = sc & (0x00FFFFFF); +# define schar_idx(sc) (sc & (0x00FFFFFF)) #else - uint32_t idx = sc >> 8; +# define schar_idx(sc) (sc >> 8) #endif - if (idx >= glyph_cache.h.n_keys) { - abort(); - } + +void schar_get(char *buf_out, 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); } else { memcpy(buf_out, (char *)&sc, 4); @@ -170,6 +170,13 @@ void schar_get(char *buf_out, schar_T sc) } } +/// gets first raw UTF-8 byte of an schar +static char schar_get_first_byte(schar_T sc) +{ + assert(!(schar_high(sc) && schar_idx(sc) >= glyph_cache.h.n_keys)); + return schar_high(sc) ? glyph_cache.keys[schar_idx(sc)] : *(char *)≻ +} + /// @return ascii char or NUL if not ascii char schar_get_ascii(schar_T sc) { @@ -179,6 +186,90 @@ char schar_get_ascii(schar_T sc) return (sc < 0x80) ? (char)sc : NUL; #endif } + +static bool schar_in_arabic_block(schar_T sc) +{ + char first_byte = schar_get_first_byte(sc); + return ((uint8_t)first_byte & 0xFE) == 0xD8; +} + +/// 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 sc_buf[MAX_SCHAR_SIZE]; + schar_get(sc_buf, sc); + + *c0 = utf_ptr2char(sc_buf); + int len = utf_ptr2len(sc_buf); + if (*c0 == NUL) { + *c1 = NUL; + } else { + *c1 = utf_ptr2char(sc_buf + len); + } +} + +void line_do_arabic_shape(schar_T *buf, int cols) +{ + int i = 0; + + for (i = 0; i < cols; i++) { + // quickly skip over non-arabic text + if (schar_in_arabic_block(buf[i])) { + break; + } + } + + if (i == cols) { + return; + } + + 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]; + int len = utf_char2bytes(c0new, scbuf_new); + if (c1new) { + len += utf_char2bytes(c1new, scbuf_new + len); + } + + int off = utf_char2len(c0) + (c1 ? utf_char2len(c1) : 0); + size_t rest = strlen(scbuf + off); + if (rest + (size_t)off + 1 > MAX_SCHAR_SIZE) { + // TODO(bfredl): this cannot happen just yet, as we only construct + // schar_T values with up to MAX_MCO+1 composing codepoints. When code + // is improved so that MAX_SCHAR_SIZE becomes the only/sharp limit, + // we need be able to peel off a composing char which doesn't fit anymore. + abort(); + } + memcpy(scbuf_new + len, scbuf + off, rest); + buf[i] = schar_from_buf(scbuf_new, (size_t)len + rest); + +next: + c0prev = c0; + c0 = c0next; + c1 = c1next; + } +} + /// 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) @@ -242,6 +333,15 @@ void grid_line_start(ScreenGrid *grid, int row) 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); + } } /// Get present char from current rendered screen line @@ -287,11 +387,7 @@ int grid_line_puts(int col, const char *text, int textlen, int attr) { const char *ptr = text; int len = textlen; - int c; int u8cc[MAX_MCO]; - int prev_c = 0; // previous Arabic character - int pc, nc, nc1; - int pcc[MAX_MCO]; assert(grid_line_grid); @@ -301,7 +397,6 @@ int grid_line_puts(int col, const char *text, int textlen, int attr) while (col < max_col && (len < 0 || (int)(ptr - text) < len) && *ptr != NUL) { - c = (unsigned char)(*ptr); // check if this is the first byte of a multibyte int mbyte_blen = len > 0 ? utfc_ptr2len_len(ptr, (int)((text + len) - ptr)) @@ -316,37 +411,16 @@ int grid_line_puts(int col, const char *text, int textlen, int attr) u8cc[0] = 0; } - 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; - } 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; mbyte_cells = 1; } schar_T buf; - // TODO(bfredl): why not just keep the original byte sequence. arabshape is - // an edge case, treat it as such.. + // TODO(bfredl): why not just keep the original byte sequence. buf = schar_from_cc(u8c, u8cc); // When at the start of the text and overwriting the right half of a @@ -545,14 +619,12 @@ static int grid_char_needs_redraw(ScreenGrid *grid, int col, size_t off_to, int /// 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 col, int endcol, int clear_width, - int rl, int bg_attr, bool wrap, bool invalid_row) + bool rl, int bg_attr, bool wrap, bool invalid_row) { 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 col, just in case @@ -591,6 +663,10 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol endcol = (clear_width > 0 ? clear_width : -clear_width); } + if (p_arshape && !p_tbidi) { + line_do_arabic_shape(linebuf_char + col, endcol - col); + } + if (bg_attr) { for (int c = col; c < endcol; c++) { linebuf_attr[c] = hl_combine_attr(bg_attr, linebuf_attr[c]); @@ -599,6 +675,8 @@ 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); + int start_dirty = -1, end_dirty = 0; + while (col < endcol) { char_cells = 1; if (col + 1 < endcol && linebuf_char[col + 1] == 0) { |