diff options
author | Jonathan de Boyne Pollard <postmaster@localhost> | 2017-05-22 21:53:36 +0100 |
---|---|---|
committer | Jonathan de Boyne Pollard <J.deBoynePollard-newsgroups@NTLWorld.com> | 2017-06-03 18:53:27 +0100 |
commit | dbc25f5a87cf3bcfe1caac1eb1ff8b3a6978a415 (patch) | |
tree | 797188b7770cfe6fd1c8b881a3bc193e4a3a3e5c /src | |
parent | d711bb84e6a78360e470d82f3671da64583089c2 (diff) | |
download | rneovim-dbc25f5a87cf3bcfe1caac1eb1ff8b3a6978a415.tar.gz rneovim-dbc25f5a87cf3bcfe1caac1eb1ff8b3a6978a415.tar.bz2 rneovim-dbc25f5a87cf3bcfe1caac1eb1ff8b3a6978a415.zip |
tui: Optimize cursor motions
Instead of emitting CUP in several places each with their own poor local
optimizations, funnel all cursor motion through a central place.
This central function performs the same optimization for every place that
needs to move the cursor, and implements a better set of optimizations:
* Emit CUU/CUD/CUF/CUB instad of CUP when they are likely shorter.
* Use BS and LF when they are shorter than CUB and CUD.
* Use CR for quick returns to column zero.
* If printing the next few characters is shorter than a rightwards motion,
then just write out the characters.
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/tui/tui.c | 164 |
1 files changed, 145 insertions, 19 deletions
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 84f23062ff..abe0e17d8a 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -199,7 +199,11 @@ static void terminfo_start(UI *ui) uv_loop_init(&data->write_loop); if (data->out_isatty) { uv_tty_init(&data->write_loop, &data->output_handle.tty, data->out_fd, 0); +#ifdef WIN32 uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_RAW); +#else + uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_IO); +#endif } else { uv_pipe_init(&data->write_loop, &data->output_handle.pipe, 0); uv_pipe_open(&data->output_handle.pipe, data->out_fd); @@ -401,10 +405,119 @@ static void print_cell(UI *ui, UCell *ptr) out(ui, ptr->data, strlen(ptr->data)); } +static bool cheap_to_print(UI *ui, int row, int col, int next) +{ + TUIData *data = ui->data; + UGrid *grid = &data->grid; + UCell *cell = grid->cells[row] + col; + while (next) { + --next; + if (attrs_differ(cell->attrs, data->print_attrs)) { + if (data->default_attr) { + return false; + } + } + if (strlen(cell->data) > 1) { + return false; + } + ++cell; + } + return true; +} + +/// This optimizes several cases where it is cheaper to do something other +/// than send a full cursor positioning control sequence. However, there are +/// some further optimizations that may seem obvious but that will not work. +/// +/// We cannot use VT (ASCII 0/11) for moving the cursor up, because VT means +/// move the cursor down on a DEC terminal. Similarly, on a DEC terminal FF +/// (ASCII 0/12) means the same thing and does not mean home. VT, CVT, and +/// TAB also stop at software-defined tabulation stops, not at a fixed set +/// of row/column positions. +static void cursor_goto(UI *ui, int row, int col) +{ + TUIData *data = ui->data; + UGrid *grid = &data->grid; + if (row == grid->row && col == grid->col) + return; + if (0 == row && 0 == col) { + unibi_out(ui, unibi_cursor_home); + ugrid_goto(&data->grid, row, col); + return; + } + if (0 == col && 0 != grid->col) { + unibi_out(ui, unibi_carriage_return); + ugrid_goto(&data->grid, grid->row, col); + } else if (col > grid->col) { + int n = col - grid->col; + if (n <= (row == grid->row ? 4 : 2) && cheap_to_print(ui, grid->row, grid->col, n)) { + UGRID_FOREACH_CELL(grid, grid->row, grid->row, + grid->col, col - 1, { + print_cell(ui, cell); + ++grid->col; + }); + } + } + if (row == grid->row) { + if (col < grid->col) { + int n = grid->col - col; + if (n <= 4) { // This might be just BS, so it is considered really cheap. + while (n--) + unibi_out(ui, unibi_cursor_left); + } else { + data->params[0].i = n; + unibi_out(ui, unibi_parm_left_cursor); + } + ugrid_goto(&data->grid, row, col); + return; + } else if (col > grid->col) { + int n = col - grid->col; + if (n <= 2) { + while (n--) + unibi_out(ui, unibi_cursor_right); + } else { + data->params[0].i = n; + unibi_out(ui, unibi_parm_right_cursor); + } + ugrid_goto(&data->grid, row, col); + return; + } + } + if (col == grid->col) { + if (row > grid->row) { + int n = row - grid->row; + if (n <= 4) { // This might be just LF, so it is considered really cheap. + while (n--) + unibi_out(ui, unibi_cursor_down); + } else { + data->params[0].i = n; + unibi_out(ui, unibi_parm_down_cursor); + } + ugrid_goto(&data->grid, row, col); + return; + } else if (row < grid->row) { + int n = grid->row - row; + if (n <= 2) { + while (n--) + unibi_out(ui, unibi_cursor_up); + } else { + data->params[0].i = n; + unibi_out(ui, unibi_parm_up_cursor); + } + ugrid_goto(&data->grid, row, col); + return; + } + } + unibi_goto(ui, row, col); + ugrid_goto(&data->grid, row, col); +} + static void clear_region(UI *ui, int top, int bot, int left, int right) { TUIData *data = ui->data; UGrid *grid = &data->grid; + int saved_row = grid->row; + int saved_col = grid->col; bool cleared = false; if (grid->bg == -1 && right == ui->width -1) { @@ -419,7 +532,7 @@ static void clear_region(UI *ui, int top, int bot, int left, int right) if (top == 0) { unibi_out(ui, unibi_clear_screen); } else { - unibi_goto(ui, top, 0); + cursor_goto(ui, top, 0); unibi_out(ui, unibi_clr_eos); } cleared = true; @@ -429,7 +542,7 @@ static void clear_region(UI *ui, int top, int bot, int left, int right) if (!cleared) { // iterate through each line and clear with clr_eol for (int row = top; row <= bot; ++row) { - unibi_goto(ui, row, left); + cursor_goto(ui, row, left); unibi_out(ui, unibi_clr_eol); } cleared = true; @@ -438,18 +551,15 @@ static void clear_region(UI *ui, int top, int bot, int left, int right) if (!cleared) { // could not clear using faster terminal codes, refresh the whole region - int currow = -1; UGRID_FOREACH_CELL(grid, top, bot, left, right, { - if (currow != row) { - unibi_goto(ui, row, col); - currow = row; - } + cursor_goto(ui, row, col); print_cell(ui, cell); + ++grid->col; }); } // restore cursor - unibi_goto(ui, grid->row, grid->col); + cursor_goto(ui, saved_row, saved_col); } static bool can_use_scroll(UI * ui) @@ -552,9 +662,7 @@ static void tui_eol_clear(UI *ui) static void tui_cursor_goto(UI *ui, Integer row, Integer col) { - TUIData *data = ui->data; - ugrid_goto(&data->grid, (int)row, (int)col); - unibi_goto(ui, (int)row, (int)col); + cursor_goto(ui, (int)row, (int)col); } CursorShape tui_cursor_decode_shape(const char *shape_str) @@ -728,6 +836,8 @@ static void tui_scroll(UI *ui, Integer count) ugrid_scroll(grid, (int)count, &clear_top, &clear_bot); if (can_use_scroll(ui)) { + int saved_row = grid->row; + int saved_col = grid->col; bool scroll_clears_to_current_colour = unibi_get_bool(data->ut, unibi_back_color_erase); @@ -735,7 +845,7 @@ static void tui_scroll(UI *ui, Integer count) if (!data->scroll_region_is_full_screen) { set_scroll_region(ui); } - unibi_goto(ui, grid->top, grid->left); + cursor_goto(ui, grid->top, grid->left); // also set default color attributes or some terminals can become funny if (scroll_clears_to_current_colour) { HlAttrs clear_attrs = EMPTY_ATTRS; @@ -764,7 +874,7 @@ static void tui_scroll(UI *ui, Integer count) if (!data->scroll_region_is_full_screen) { reset_scroll_region(ui); } - unibi_goto(ui, grid->row, grid->col); + cursor_goto(ui, saved_row, saved_col); if (!scroll_clears_to_current_colour) { // This is required because scrolling will leave wrong background in the @@ -830,19 +940,19 @@ static void tui_flush(UI *ui) tui_busy_stop(ui); // avoid hidden cursor } + int saved_row = grid->row; + int saved_col = grid->col; + while (kv_size(data->invalid_regions)) { Rect r = kv_pop(data->invalid_regions); - int currow = -1; UGRID_FOREACH_CELL(grid, r.top, r.bot, r.left, r.right, { - if (currow != row) { - unibi_goto(ui, row, col); - currow = row; - } + cursor_goto(ui, row, col); print_cell(ui, cell); + ++grid->col; }); } - unibi_goto(ui, grid->row, grid->col); + cursor_goto(ui, saved_row, saved_col); flush_buf(ui, true); } @@ -1190,6 +1300,22 @@ end: data->unibi_ext.set_rgb_background = (int)unibi_add_ext_str(ut, NULL, "\x1b[48;2;%p1%d;%p2%d;%p3%dm"); unibi_set_if_empty(ut, unibi_cursor_address, "\x1b[%i%p1%d;%p2%dH"); + unibi_set_if_empty(ut, unibi_cursor_home, "\x1b[H"); + unibi_set_if_empty(ut, unibi_parm_left_cursor, "\x1b[%p1%dD"); + unibi_set_if_empty(ut, unibi_parm_right_cursor, "\x1b[%p1%dC"); + unibi_set_if_empty(ut, unibi_parm_down_cursor, "\x1b[%p1%dB"); + unibi_set_if_empty(ut, unibi_parm_up_cursor, "\x1b[%p1%dA"); + unibi_set_if_empty(ut, unibi_cursor_left, "\x08"); + unibi_set_if_empty(ut, unibi_cursor_right, "\x1b[C"); +#if defined(WIN32) + unibi_set_if_empty(ut, unibi_cursor_down, "\x1b[B"); +#else + // N.B. This relies upon the terminal really being in cfmakeraw() mode, + // which libuv's RAW mode is in fact not. + unibi_set_if_empty(ut, unibi_cursor_down, "\x0a"); +#endif + unibi_set_if_empty(ut, unibi_cursor_up, "\x1b[A"); + unibi_set_if_empty(ut, unibi_carriage_return, "\x0d"); unibi_set_if_empty(ut, unibi_exit_attribute_mode, "\x1b[0;10m"); unibi_set_if_empty(ut, unibi_set_a_foreground, XTERM_SETAF_16); unibi_set_if_empty(ut, unibi_set_a_background, XTERM_SETAB_16); |