diff options
author | bfredl <bjorn.linse@gmail.com> | 2023-10-31 21:33:00 +0100 |
---|---|---|
committer | bfredl <bjorn.linse@gmail.com> | 2023-11-03 11:35:42 +0100 |
commit | 44f0480a22af8f87f8784dac430416cb9913adea (patch) | |
tree | aeb487ea271274f92c59c275bf6cb26e42ed2eac | |
parent | d4dc1355eda78ca2a8aece08d1ab6c6bc1e91505 (diff) | |
download | rneovim-44f0480a22af8f87f8784dac430416cb9913adea.tar.gz rneovim-44f0480a22af8f87f8784dac430416cb9913adea.tar.bz2 rneovim-44f0480a22af8f87f8784dac430416cb9913adea.zip |
refactor(grid): implement rightleftcmd as a post-processing step
Previously, 'rightleftcmd' was implemented by having all code which
would affect msg_col or output screen cells be conditional on `cmdmsg_rl`.
This change removes all that and instead implements rightleft as a
mirroring post-processing step.
-rw-r--r-- | src/nvim/ex_getln.c | 38 | ||||
-rw-r--r-- | src/nvim/grid.c | 42 | ||||
-rw-r--r-- | src/nvim/grid.h | 1 | ||||
-rw-r--r-- | src/nvim/grid_defs.h | 4 | ||||
-rw-r--r-- | src/nvim/message.c | 75 | ||||
-rw-r--r-- | src/nvim/spellsuggest.c | 12 | ||||
-rw-r--r-- | test/functional/ui/cmdline_spec.lua | 79 |
7 files changed, 168 insertions, 83 deletions
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 93fd62f107..80a1410dbc 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -715,12 +715,8 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear ExpandInit(&s->xpc); ccline.xpc = &s->xpc; - if (curwin->w_p_rl && *curwin->w_p_rlc == 's' - && (s->firstc == '/' || s->firstc == '?')) { - cmdmsg_rl = true; - } else { - cmdmsg_rl = false; - } + cmdmsg_rl = (curwin->w_p_rl && *curwin->w_p_rlc == 's' + && (s->firstc == '/' || s->firstc == '?')); msg_grid_validate(); @@ -1564,11 +1560,7 @@ static int command_line_erase_chars(CommandLineState *s) XFREE_CLEAR(ccline.cmdbuff); // no commandline to return if (!cmd_silent && !ui_has(kUICmdline)) { - if (cmdmsg_rl) { - msg_col = Columns; - } else { - msg_col = 0; - } + msg_col = 0; msg_putchar(' '); // delete ':' } s->is_state.search_start = s->is_state.save_cursor; @@ -2664,7 +2656,7 @@ static int command_line_changed(CommandLineState *s) } } - if (cmdmsg_rl || (p_arshape && !p_tbidi)) { + if (p_arshape && !p_tbidi) { // Always redraw the whole command line to fix shaping and // right-left typing. Not efficient, but it works. // Do it only when there are no characters left to read @@ -3863,18 +3855,10 @@ void cursorcmd(void) return; } - if (cmdmsg_rl) { - msg_row = cmdline_row + (ccline.cmdspos / (Columns - 1)); - msg_col = Columns - (ccline.cmdspos % (Columns - 1)) - 1; - if (msg_row <= 0) { - msg_row = Rows - 1; - } - } else { - msg_row = cmdline_row + (ccline.cmdspos / Columns); - msg_col = ccline.cmdspos % Columns; - if (msg_row >= Rows) { - msg_row = Rows - 1; - } + msg_row = cmdline_row + (ccline.cmdspos / Columns); + msg_col = ccline.cmdspos % Columns; + if (msg_row >= Rows) { + msg_row = Rows - 1; } msg_cursor_goto(msg_row, msg_col); @@ -3886,11 +3870,7 @@ void gotocmdline(bool clr) return; } msg_start(); - if (cmdmsg_rl) { - msg_col = Columns - 1; - } else { - msg_col = 0; // always start in column 0 - } + msg_col = 0; // always start in column 0 if (clr) { // clear the bottom line(s) msg_clr_eos(); // will reset clear_cmdline } diff --git a/src/nvim/grid.c b/src/nvim/grid.c index efc819c26f..d59c3b4803 100644 --- a/src/nvim/grid.c +++ b/src/nvim/grid.c @@ -472,6 +472,45 @@ 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; + } + + size_t n = (size_t)(grid_line_last - grid_line_first); + int mirror = grid_line_maxcol - 1; // Mirrors are more fun than television. + schar_T *scratch_char = (schar_T *)linebuf_scratch; + memcpy(scratch_char + grid_line_first, linebuf_char + grid_line_first, n * sizeof(schar_T)); + for (int col = grid_line_first; col < grid_line_last; col++) { + int rev = mirror - col; + if (col + 1 < grid_line_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]; + } + } + + // for attr and vcol: assumes doublewidth chars are self-consistent + sattr_T *scratch_attr = (sattr_T *)linebuf_scratch; + memcpy(scratch_attr + grid_line_first, linebuf_attr + grid_line_first, n * sizeof(sattr_T)); + for (int col = grid_line_first; col < grid_line_last; col++) { + linebuf_attr[mirror - col] = scratch_attr[col]; + } + + colnr_T *scratch_vcol = (colnr_T *)linebuf_scratch; + memcpy(scratch_vcol + grid_line_first, linebuf_vcol + grid_line_first, n * sizeof(colnr_T)); + for (int col = grid_line_first; col < grid_line_last; col++) { + linebuf_vcol[mirror - col] = scratch_vcol[col]; + } + + int grid_line_last_copy = grid_line_last; + grid_line_last = grid_line_maxcol - grid_line_first; + grid_line_first = grid_line_maxcol - grid_line_last_copy; +} + /// End a group of grid_line_puts calls and send the screen buffer to the UI layer. void grid_line_flush(void) { @@ -828,9 +867,11 @@ void grid_alloc(ScreenGrid *grid, int rows, int columns, bool copy, bool valid) 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; } } @@ -855,6 +896,7 @@ void grid_free_all_mem(void) 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. diff --git a/src/nvim/grid.h b/src/nvim/grid.h index 1b2c529a93..6e3c4fd770 100644 --- a/src/nvim/grid.h +++ b/src/nvim/grid.h @@ -29,6 +29,7 @@ EXTERN bool resizing_screen INIT( = 0); EXTERN schar_T *linebuf_char INIT( = NULL); EXTERN sattr_T *linebuf_attr INIT( = NULL); EXTERN colnr_T *linebuf_vcol INIT( = NULL); +EXTERN char *linebuf_scratch INIT( = NULL); // Low-level functions to manipulate individual character cells on the // screen grid. diff --git a/src/nvim/grid_defs.h b/src/nvim/grid_defs.h index c65c273d57..390b2a848a 100644 --- a/src/nvim/grid_defs.h +++ b/src/nvim/grid_defs.h @@ -16,7 +16,9 @@ // otherwise it must be a UTF-8 string of length maximum 4 (no NUL when n=4) typedef uint32_t schar_T; -typedef int sattr_T; +typedef int32_t sattr_T; +// must be at least as big as the biggest of schar_T, sattr_T, col_T +typedef int32_t sscratch_T; enum { kZIndexDefaultGrid = 0, diff --git a/src/nvim/message.c b/src/nvim/message.c index c1acef8b0d..3b33559906 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1231,9 +1231,7 @@ void wait_return(int redraw) } else { msg_didout = false; c = K_IGNORE; - msg_col = - cmdmsg_rl ? Columns - 1 : - 0; + msg_col = 0; } if (quit_more) { c = CAR; // just pretend CR was hit @@ -1435,7 +1433,7 @@ void msg_start(void) if (!msg_scroll && full_screen) { // overwrite last message msg_row = cmdline_row; - msg_col = cmdmsg_rl ? Columns - 1 : 0; + msg_col = 0; } else if (msg_didout || (p_ch == 0 && !ui_has(kUIMessages))) { // start message on next line msg_putchar('\n'); did_return = true; @@ -2132,7 +2130,7 @@ static void msg_puts_display(const char *str, int maxlen, int attr, int recurse) int msg_row_pending = -1; while (true) { - if (cmdmsg_rl ? msg_col <= 0 : msg_col >= Columns) { + if (msg_col >= Columns) { if (p_more && !recurse) { // Store text for scrolling back. store_sb_text(&sb_str, s, attr, &sb_col, true); @@ -2141,7 +2139,7 @@ static void msg_puts_display(const char *str, int maxlen, int attr, int recurse) break; } - msg_col = cmdmsg_rl ? Columns - 1 : 0; + msg_col = 0; msg_row++; msg_didout = false; } @@ -2156,7 +2154,7 @@ static void msg_puts_display(const char *str, int maxlen, int attr, int recurse) if (!recurse) { if (msg_row_pending >= 0) { - grid_line_flush_if_valid_row(); + msg_line_flush(); msg_row_pending = -1; } @@ -2196,7 +2194,7 @@ static void msg_puts_display(const char *str, int maxlen, int attr, int recurse) // TODO(bfredl): this logic is messier that it has to be. What // messages really want is its own private linebuf_char buffer. if (msg_row_pending >= 0) { - grid_line_flush_if_valid_row(); + msg_line_flush(); } grid_line_start(&msg_grid_adj, msg_row); msg_row_pending = msg_row; @@ -2207,7 +2205,7 @@ static void msg_puts_display(const char *str, int maxlen, int attr, int recurse) // avoid including composing chars after the end int l = (maxlen >= 0) ? utfc_ptr2len_len(s, (int)((str + maxlen) - s)) : utfc_ptr2len(s); - if (cw > 1 && (cmdmsg_rl ? msg_col <= 1 : msg_col == Columns - 1)) { + if (cw > 1 && (msg_col == Columns - 1)) { // Doesn't fit, print a highlighted '>' to fill it up. grid_line_puts(msg_col, ">", 1, HL_ATTR(HLF_AT)); cw = 1; @@ -2216,20 +2214,12 @@ static void msg_puts_display(const char *str, int maxlen, int attr, int recurse) s += l; } msg_didout = true; // remember that line is not empty - if (cmdmsg_rl) { - msg_col -= cw; - } else { - msg_col += cw; - } + msg_col += cw; } else { char c = *s++; if (c == '\n') { // go to next line msg_didout = false; // remember that line is empty - if (cmdmsg_rl) { - msg_col = Columns - 1; - } else { - msg_col = 0; - } + msg_col = 0; msg_row++; if (p_more && !recurse) { // Store text for scrolling back. @@ -2244,9 +2234,9 @@ static void msg_puts_display(const char *str, int maxlen, int attr, int recurse) } else if (c == TAB) { // translate Tab into spaces do { grid_line_puts(msg_col, " ", 1, print_attr); - msg_col += cmdmsg_rl ? -1 : 1; + msg_col += 1; - if (msg_col == (cmdmsg_rl ? 0 : Columns)) { + if (msg_col == Columns) { break; } } while (msg_col & 7); @@ -2257,7 +2247,7 @@ static void msg_puts_display(const char *str, int maxlen, int attr, int recurse) } if (msg_row_pending >= 0) { - grid_line_flush_if_valid_row(); + msg_line_flush(); } msg_cursor_goto(msg_row, msg_col); @@ -2268,9 +2258,20 @@ static void msg_puts_display(const char *str, int maxlen, int attr, int recurse) msg_check(); } +void msg_line_flush(void) +{ + if (cmdmsg_rl) { + grid_line_mirror(); + } + grid_line_flush_if_valid_row(); +} + void msg_cursor_goto(int row, int col) { ScreenGrid *grid = &msg_grid_adj; + if (cmdmsg_rl) { + col = Columns - 1 - col; + } grid_adjust(&grid, &row, &col); ui_grid_cursor_goto(grid->handle, row, col); } @@ -2656,18 +2657,10 @@ static void msg_puts_printf(const char *str, const ptrdiff_t maxlen) int cw = utf_char2cells(utf_ptr2char(s)); // primitive way to compute the current column - if (cmdmsg_rl) { - if (*s == '\r' || *s == '\n') { - msg_col = Columns - 1; - } else { - msg_col -= cw; - } + if (*s == '\r' || *s == '\n') { + msg_col = 0; } else { - if (*s == '\r' || *s == '\n') { - msg_col = 0; - } else { - msg_col += cw; - } + msg_col += cw; } s += len; } @@ -2915,8 +2908,6 @@ static int do_more_prompt(int typed_char) if (quit_more) { msg_row = Rows - 1; msg_col = 0; - } else if (cmdmsg_rl) { - msg_col = Columns - 1; } entered = false; @@ -3014,7 +3005,7 @@ void msg_clr_eos_force(void) return; } int msg_startcol = (cmdmsg_rl) ? 0 : msg_col; - int msg_endcol = (cmdmsg_rl) ? msg_col + 1 : Columns; + int msg_endcol = (cmdmsg_rl) ? Columns - msg_col : Columns; if (msg_grid.chars && msg_row < msg_grid_pos) { // TODO(bfredl): ugly, this state should already been validated at this @@ -3028,7 +3019,7 @@ void msg_clr_eos_force(void) ' ', ' ', HL_ATTR(HLF_MSG)); redraw_cmdline = true; // overwritten the command line - if (msg_row < Rows - 1 || msg_col == (cmdmsg_rl ? Columns : 0)) { + if (msg_row < Rows - 1 || msg_col == 0) { clear_cmdline = false; // command line has been cleared mode_displayed = false; // mode cleared or overwritten } @@ -3383,14 +3374,8 @@ void msg_advance(int col) if (col >= Columns) { // not enough room col = Columns - 1; } - if (cmdmsg_rl) { - while (msg_col > Columns - col) { - msg_putchar(' '); - } - } else { - while (msg_col < col) { - msg_putchar(' '); - } + while (msg_col < col) { + msg_putchar(' '); } } diff --git a/src/nvim/spellsuggest.c b/src/nvim/spellsuggest.c index 564c4ca12d..39265718c6 100644 --- a/src/nvim/spellsuggest.c +++ b/src/nvim/spellsuggest.c @@ -537,22 +537,18 @@ void spell_suggest(int count) } else { // When 'rightleft' is set the list is drawn right-left. cmdmsg_rl = curwin->w_p_rl; - if (cmdmsg_rl) { - msg_col = Columns - 1; - } // List the suggestions. msg_start(); msg_row = Rows - 1; // for when 'cmdheight' > 1 lines_left = Rows; // avoid more prompt - vim_snprintf(IObuff, IOSIZE, _("Change \"%.*s\" to:"), - sug.su_badlen, sug.su_badptr); - if (cmdmsg_rl && strncmp(IObuff, "Change", 6) == 0) { + char *fmt = _("Change \"%.*s\" to:"); + if (cmdmsg_rl && strncmp(fmt, "Change", 6) == 0) { // And now the rabbit from the high hat: Avoid showing the // untranslated message rightleft. - vim_snprintf(IObuff, IOSIZE, ":ot \"%.*s\" egnahC", - sug.su_badlen, sug.su_badptr); + fmt = ":ot \"%.*s\" egnahC"; } + vim_snprintf(IObuff, IOSIZE, fmt, sug.su_badlen, sug.su_badptr); msg_puts(IObuff); msg_clr_eos(); msg_putchar('\n'); diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua index 497b2e7f4c..188b9ee87b 100644 --- a/test/functional/ui/cmdline_spec.lua +++ b/test/functional/ui/cmdline_spec.lua @@ -24,6 +24,7 @@ local function new_screen(opt) [7] = {bold = true, foreground = Screen.colors.Brown}, [8] = {background = Screen.colors.LightGrey}, [9] = {bold = true}, + [10] = {background = Screen.colors.Yellow1}; }) return screen end @@ -771,6 +772,84 @@ describe('cmdline redraw', function() :^abc | ]]) end) + + it('with rightleftcmd', function() + command('set rightleft rightleftcmd=search shortmess+=s') + meths.buf_set_lines(0, 0, -1, true, {"let's rock!"}) + screen:expect{grid=[[ + !kcor s'te^l| + {1: ~}| + {1: ~}| + {1: ~}| + | + ]]} + + feed '/' + screen:expect{grid=[[ + !kcor s'tel| + {1: ~}| + {1: ~}| + {1: ~}| + ^ /| + ]]} + + feed "let's" + -- note: cursor looks off but looks alright in real use + -- when rendered as a block so it touches the end of the text + screen:expect{grid=[[ + !kcor {2:s'tel}| + {1: ~}| + {1: ~}| + {1: ~}| + ^ s'tel/| + ]]} + + -- cursor movement + feed "<space>" + screen:expect{grid=[[ + !kcor{2: s'tel}| + {1: ~}| + {1: ~}| + {1: ~}| + ^ s'tel/| + ]]} + + feed "rock" + screen:expect{grid=[[ + !{2:kcor s'tel}| + {1: ~}| + {1: ~}| + {1: ~}| + ^ kcor s'tel/| + ]]} + + feed "<right>" + screen:expect{grid=[[ + !{2:kcor s'tel}| + {1: ~}| + {1: ~}| + {1: ~}| + ^kcor s'tel/| + ]]} + + feed "<left>" + screen:expect{grid=[[ + !{2:kcor s'tel}| + {1: ~}| + {1: ~}| + {1: ~}| + ^ kcor s'tel/| + ]]} + + feed "<cr>" + screen:expect{grid=[[ + !{10:kcor s'te^l}| + {1: ~}| + {1: ~}| + {1: ~}| + kcor s'tel/ | + ]]} + end) end) describe('statusline is redrawn on entering cmdline', function() |