aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJonathan de Boyne Pollard <postmaster@localhost>2017-05-22 21:53:36 +0100
committerJonathan de Boyne Pollard <J.deBoynePollard-newsgroups@NTLWorld.com>2017-06-03 18:53:27 +0100
commitdbc25f5a87cf3bcfe1caac1eb1ff8b3a6978a415 (patch)
tree797188b7770cfe6fd1c8b881a3bc193e4a3a3e5c /src
parentd711bb84e6a78360e470d82f3671da64583089c2 (diff)
downloadrneovim-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.c164
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);