aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjörn Linse <bjorn.linse@gmail.com>2018-07-06 14:39:50 +0200
committerBjörn Linse <bjorn.linse@gmail.com>2018-07-21 13:21:58 +0200
commit1adb01c120d04bdbf25cd4ea6151ecd5f2de3a72 (patch)
tree1f906f5f77b77bc0beecb33320ceb4c15ff5c58e
parent2134396074d86c344aaf43c3b839fd38c499fb69 (diff)
downloadrneovim-1adb01c120d04bdbf25cd4ea6151ecd5f2de3a72.tar.gz
rneovim-1adb01c120d04bdbf25cd4ea6151ecd5f2de3a72.tar.bz2
rneovim-1adb01c120d04bdbf25cd4ea6151ecd5f2de3a72.zip
ui: use line-based rather than char-based updates in screen.c
Add ext_newgrid and ext_hlstate extensions. These use predefined highlights and line-segment based updates, for efficiency and simplicity.. The ext_hlstate extension in addition allows semantic identification of builtin and syntax highlights. Reimplement the old char-based updates in the remote UI layer, for compatibility. For the moment, this is still the default. The bulitin TUI uses the new line-based protocol. cmdline uses curwin cursor position when ext_cmdline is active.
-rw-r--r--src/nvim/api/ui.c345
-rw-r--r--src/nvim/api/ui_events.in.h68
-rw-r--r--src/nvim/event/defs.h2
-rw-r--r--src/nvim/ex_docmd.c4
-rw-r--r--src/nvim/ex_getln.c22
-rw-r--r--src/nvim/generators/gen_api_ui_events.lua26
-rw-r--r--src/nvim/highlight.c19
-rw-r--r--src/nvim/highlight_defs.h13
-rw-r--r--src/nvim/lib/kvec.h8
-rw-r--r--src/nvim/main.c3
-rw-r--r--src/nvim/message.c12
-rw-r--r--src/nvim/misc1.c2
-rw-r--r--src/nvim/popupmnu.c3
-rw-r--r--src/nvim/screen.c377
-rw-r--r--src/nvim/syntax.c14
-rw-r--r--src/nvim/tui/tui.c174
-rw-r--r--src/nvim/ugrid.c17
-rw-r--r--src/nvim/ugrid.h3
-rw-r--r--src/nvim/ui.c193
-rw-r--r--src/nvim/ui.h19
-rw-r--r--src/nvim/ui_bridge.c50
-rw-r--r--test/functional/api/version_spec.lua2
-rw-r--r--test/functional/api/vim_spec.lua17
-rw-r--r--test/functional/terminal/tui_spec.lua12
-rw-r--r--test/functional/ui/cmdline_spec.lua28
-rw-r--r--test/functional/ui/options_spec.lua7
-rw-r--r--test/functional/ui/screen.lua79
-rw-r--r--test/functional/ui/screen_basic_spec.lua12
28 files changed, 967 insertions, 564 deletions
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index 01e6e0ca03..63c2c4a1b9 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -26,6 +26,12 @@
typedef struct {
uint64_t channel_id;
Array buffer;
+
+ int hl_id; // current higlight for legacy put event
+ Integer cursor_row, cursor_col; // Intended visibule cursor position
+
+ // Position of legacy cursor, used both for drawing and visible user cursor.
+ Integer client_row, client_col;
} UIData;
static PMap(uint64_t) *connected_uis = NULL;
@@ -71,10 +77,9 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height,
ui->width = (int)width;
ui->height = (int)height;
ui->rgb = true;
- ui->resize = remote_ui_resize;
- ui->clear = remote_ui_clear;
- ui->eol_clear = remote_ui_eol_clear;
- ui->cursor_goto = remote_ui_cursor_goto;
+ ui->grid_resize = remote_ui_grid_resize;
+ ui->grid_clear = remote_ui_grid_clear;
+ ui->grid_cursor_goto = remote_ui_grid_cursor_goto;
ui->mode_info_set = remote_ui_mode_info_set;
ui->update_menu = remote_ui_update_menu;
ui->busy_start = remote_ui_busy_start;
@@ -82,16 +87,12 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height,
ui->mouse_on = remote_ui_mouse_on;
ui->mouse_off = remote_ui_mouse_off;
ui->mode_change = remote_ui_mode_change;
- ui->set_scroll_region = remote_ui_set_scroll_region;
- ui->scroll = remote_ui_scroll;
- ui->highlight_set = remote_ui_highlight_set;
- ui->put = remote_ui_put;
+ ui->grid_scroll = remote_ui_grid_scroll;
+ ui->hl_attr_define = remote_ui_hl_attr_define;
+ ui->raw_line = remote_ui_raw_line;
ui->bell = remote_ui_bell;
ui->visual_bell = remote_ui_visual_bell;
ui->default_colors_set = remote_ui_default_colors_set;
- ui->update_fg = remote_ui_update_fg;
- ui->update_bg = remote_ui_update_bg;
- ui->update_sp = remote_ui_update_sp;
ui->flush = remote_ui_flush;
ui->suspend = remote_ui_suspend;
ui->set_title = remote_ui_set_title;
@@ -103,16 +104,22 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height,
memset(ui->ui_ext, 0, sizeof(ui->ui_ext));
for (size_t i = 0; i < options.size; i++) {
- ui_set_option(ui, options.items[i].key, options.items[i].value, err);
+ ui_set_option(ui, true, options.items[i].key, options.items[i].value, err);
if (ERROR_SET(err)) {
xfree(ui);
return;
}
}
+ if (ui->ui_ext[kUIHlState]) {
+ ui->ui_ext[kUINewgrid] = true;
+ }
+
UIData *data = xmalloc(sizeof(UIData));
data->channel_id = channel_id;
data->buffer = (Array)ARRAY_DICT_INIT;
+ data->hl_id = 0;
+ data->client_col = -1;
ui->data = data;
pmap_put(uint64_t)(connected_uis, channel_id, ui);
@@ -174,13 +181,11 @@ void nvim_ui_set_option(uint64_t channel_id, String name,
}
UI *ui = pmap_get(uint64_t)(connected_uis, channel_id);
- ui_set_option(ui, name, value, error);
- if (!ERROR_SET(error)) {
- ui_refresh();
- }
+ ui_set_option(ui, false, name, value, error);
}
-static void ui_set_option(UI *ui, String name, Object value, Error *error)
+static void ui_set_option(UI *ui, bool init, String name, Object value,
+ Error *error)
{
if (strequal(name.data, "rgb")) {
if (value.type != kObjectTypeBoolean) {
@@ -188,40 +193,46 @@ static void ui_set_option(UI *ui, String name, Object value, Error *error)
return;
}
ui->rgb = value.data.boolean;
+ // A little drastic, but only legacy uis need to use this option
+ if (!init) {
+ ui_refresh();
+ }
return;
}
+ // LEGACY: Deprecated option, use `ext_cmdline` instead.
+ bool is_popupmenu = strequal(name.data, "popupmenu_external");
+
for (UIExtension i = 0; i < kUIExtCount; i++) {
- if (strequal(name.data, ui_ext_names[i])) {
+ if (strequal(name.data, ui_ext_names[i])
+ || (i == kUIPopupmenu && is_popupmenu)) {
if (value.type != kObjectTypeBoolean) {
snprintf((char *)IObuff, IOSIZE, "%s must be a Boolean",
- ui_ext_names[i]);
+ name.data);
api_set_error(error, kErrorTypeValidation, (char *)IObuff);
return;
}
- ui->ui_ext[i] = value.data.boolean;
- return;
- }
- }
-
- if (strequal(name.data, "popupmenu_external")) {
- // LEGACY: Deprecated option, use `ext_cmdline` instead.
- if (value.type != kObjectTypeBoolean) {
- api_set_error(error, kErrorTypeValidation,
- "popupmenu_external must be a Boolean");
+ bool boolval = value.data.boolean;
+ if (!init && i == kUINewgrid && boolval != ui->ui_ext[i]) {
+ // There shouldn't be a reason for an UI to do this ever
+ // so explicitly don't support this.
+ api_set_error(error, kErrorTypeValidation,
+ "ext_newgrid option cannot be changed");
+ }
+ ui->ui_ext[i] = boolval;
+ if (!init) {
+ ui_set_ext_option(ui, i, boolval);
+ }
return;
}
- ui->ui_ext[kUIPopupmenu] = value.data.boolean;
- return;
}
api_set_error(error, kErrorTypeValidation, "No such UI option: %s",
name.data);
-#undef UI_EXT_OPTION
}
/// Pushes data into UI.UIData, to be consumed later by remote_ui_flush().
-static void push_call(UI *ui, char *name, Array args)
+static void push_call(UI *ui, const char *name, Array args)
{
Array call = ARRAY_DICT_INIT;
UIData *data = ui->data;
@@ -243,27 +254,293 @@ static void push_call(UI *ui, char *name, Array args)
kv_A(data->buffer, kv_size(data->buffer) - 1).data.array = call;
}
+static void remote_ui_grid_clear(UI *ui, Integer grid)
+{
+ Array args = ARRAY_DICT_INIT;
+ if (ui->ui_ext[kUINewgrid]) {
+ ADD(args, INTEGER_OBJ(grid));
+ }
+ const char *name = ui->ui_ext[kUINewgrid] ? "grid_clear" : "clear";
+ push_call(ui, name, args);
+}
-static void remote_ui_highlight_set(UI *ui, HlAttrs attrs)
+static void remote_ui_grid_resize(UI *ui, Integer grid,
+ Integer width, Integer height)
{
Array args = ARRAY_DICT_INIT;
+ if (ui->ui_ext[kUINewgrid]) {
+ ADD(args, INTEGER_OBJ(grid));
+ }
+ ADD(args, INTEGER_OBJ(width));
+ ADD(args, INTEGER_OBJ(height));
+ const char *name = ui->ui_ext[kUINewgrid] ? "grid_resize" : "resize";
+ push_call(ui, name, args);
+}
+
+static void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top,
+ Integer bot, Integer left, Integer right,
+ Integer rows, Integer cols)
+{
+ if (ui->ui_ext[kUINewgrid]) {
+ Array args = ARRAY_DICT_INIT;
+ ADD(args, INTEGER_OBJ(grid));
+ ADD(args, INTEGER_OBJ(top));
+ ADD(args, INTEGER_OBJ(bot));
+ ADD(args, INTEGER_OBJ(left));
+ ADD(args, INTEGER_OBJ(right));
+ ADD(args, INTEGER_OBJ(rows));
+ ADD(args, INTEGER_OBJ(cols));
+ push_call(ui, "grid_scroll", args);
+ } else {
+ Array args = ARRAY_DICT_INIT;
+ ADD(args, INTEGER_OBJ(top));
+ ADD(args, INTEGER_OBJ(bot-1));
+ ADD(args, INTEGER_OBJ(left));
+ ADD(args, INTEGER_OBJ(right-1));
+ push_call(ui, "set_scroll_region", args);
+
+ args = (Array)ARRAY_DICT_INIT;
+ ADD(args, INTEGER_OBJ(rows));
+ push_call(ui, "scroll", args);
+ }
+}
+
+static void remote_ui_default_colors_set(UI *ui, Integer rgb_fg,
+ Integer rgb_bg, Integer rgb_sp,
+ Integer cterm_fg, Integer cterm_bg)
+{
+ Array args = ARRAY_DICT_INIT;
+ ADD(args, INTEGER_OBJ(rgb_fg));
+ ADD(args, INTEGER_OBJ(rgb_bg));
+ ADD(args, INTEGER_OBJ(rgb_sp));
+ ADD(args, INTEGER_OBJ(cterm_fg));
+ ADD(args, INTEGER_OBJ(cterm_bg));
+ push_call(ui, "default_colors_set", args);
+
+ // Deprecated
+ if (!ui->ui_ext[kUINewgrid]) {
+ args = (Array)ARRAY_DICT_INIT;
+ ADD(args, INTEGER_OBJ(ui->rgb ? rgb_fg : cterm_fg - 1));
+ push_call(ui, "update_fg", args);
+
+ args = (Array)ARRAY_DICT_INIT;
+ ADD(args, INTEGER_OBJ(ui->rgb ? rgb_bg : cterm_bg - 1));
+ push_call(ui, "update_bg", args);
+
+ args = (Array)ARRAY_DICT_INIT;
+ ADD(args, INTEGER_OBJ(ui->rgb ? rgb_sp : -1));
+ push_call(ui, "update_sp", args);
+ }
+}
+
+static void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs,
+ HlAttrs cterm_attrs, Array info)
+{
+ if (!ui->ui_ext[kUINewgrid]) {
+ return;
+ }
+ Array args = ARRAY_DICT_INIT;
+
+ ADD(args, INTEGER_OBJ(id));
+
+ Dictionary rgb_hl = hlattrs2dict(&rgb_attrs, true);
+ ADD(args, DICTIONARY_OBJ(rgb_hl));
+
+ Dictionary cterm_hl = hlattrs2dict(&cterm_attrs, false);
+ ADD(args, DICTIONARY_OBJ(cterm_hl));
+
+ if (ui->ui_ext[kUIHlState]) {
+ ADD(args, ARRAY_OBJ(copy_array(info)));
+ } else {
+ ADD(args, ARRAY_OBJ((Array)ARRAY_DICT_INIT));
+ }
+
+ push_call(ui, "hl_attr_define", args);
+}
+
+static void remote_ui_highlight_set(UI *ui, int id)
+{
+ Array args = ARRAY_DICT_INIT;
+ UIData *data = ui->data;
+
+ HlAttrs attrs = HLATTRS_INIT;
+
+ if (data->hl_id == id) {
+ return;
+ }
+ data->hl_id = id;
+
+ if (id != 0) {
+ HlAttrs *aep = syn_attr2entry(id);
+ if (aep) {
+ attrs = *aep;
+ }
+ }
+
Dictionary hl = hlattrs2dict(&attrs, ui->rgb);
ADD(args, DICTIONARY_OBJ(hl));
push_call(ui, "highlight_set", args);
}
+/// "true" cursor used only for input focus
+static void remote_ui_grid_cursor_goto(UI *ui, Integer grid, Integer row,
+ Integer col)
+{
+ if (ui->ui_ext[kUINewgrid]) {
+ Array args = ARRAY_DICT_INIT;
+ ADD(args, INTEGER_OBJ(grid));
+ ADD(args, INTEGER_OBJ(row));
+ ADD(args, INTEGER_OBJ(col));
+ push_call(ui, "grid_cursor_goto", args);
+ } else {
+ UIData *data = ui->data;
+ data->cursor_row = row;
+ data->cursor_col = col;
+ remote_ui_cursor_goto(ui, row, col);
+ }
+}
+
+/// emulated cursor used both for drawing and for input focus
+static void remote_ui_cursor_goto(UI *ui, Integer row, Integer col)
+{
+ UIData *data = ui->data;
+ if (data->client_row == row && data->client_col == col) {
+ return;
+ }
+ data->client_row = row;
+ data->client_col = col;
+ Array args = ARRAY_DICT_INIT;
+ ADD(args, INTEGER_OBJ(row));
+ ADD(args, INTEGER_OBJ(col));
+ push_call(ui, "cursor_goto", args);
+}
+
+static void remote_ui_put(UI *ui, const char *cell)
+{
+ UIData *data = ui->data;
+ data->client_col++;
+ Array args = ARRAY_DICT_INIT;
+ ADD(args, STRING_OBJ(cstr_to_string(cell)));
+ push_call(ui, "put", args);
+}
+
+static void remote_ui_raw_line(UI *ui, Integer grid, Integer row,
+ Integer startcol, Integer endcol,
+ Integer clearcol, Integer clearattr,
+ const schar_T *chunk, const sattr_T *attrs)
+{
+ UIData *data = ui->data;
+ if (ui->ui_ext[kUINewgrid]) {
+ Array args = ARRAY_DICT_INIT;
+ ADD(args, INTEGER_OBJ(grid));
+ ADD(args, INTEGER_OBJ(row));
+ ADD(args, INTEGER_OBJ(startcol));
+ Array cells = ARRAY_DICT_INIT;
+ int repeat = 0;
+ size_t ncells = (size_t)(endcol-startcol);
+ int last_hl = -1;
+ for (size_t i = 0; i < ncells; i++) {
+ repeat++;
+ if (i == ncells-1 || attrs[i] != attrs[i+1]
+ || STRCMP(chunk[i], chunk[i+1])) {
+ Array cell = ARRAY_DICT_INIT;
+ ADD(cell, STRING_OBJ(cstr_to_string((const char *)chunk[i])));
+ if (attrs[i] != last_hl || repeat > 1) {
+ ADD(cell, INTEGER_OBJ(attrs[i]));
+ last_hl = attrs[i];
+ }
+ if (repeat > 1) {
+ ADD(cell, INTEGER_OBJ(repeat));
+ }
+ ADD(cells, ARRAY_OBJ(cell));
+ repeat = 0;
+ }
+ }
+ if (endcol < clearcol) {
+ Array cell = ARRAY_DICT_INIT;
+ ADD(cell, STRING_OBJ(cstr_to_string(" ")));
+ ADD(cell, INTEGER_OBJ(clearattr));
+ ADD(cell, INTEGER_OBJ(clearcol-endcol));
+ ADD(cells, ARRAY_OBJ(cell));
+ }
+ ADD(args, ARRAY_OBJ(cells));
+
+ push_call(ui, "grid_line", args);
+ } else {
+ for (int i = 0; i < endcol-startcol; i++) {
+ remote_ui_cursor_goto(ui, row, startcol+i);
+ remote_ui_highlight_set(ui, attrs[i]);
+ remote_ui_put(ui, (const char *)chunk[i]);
+ if (utf_ambiguous_width(utf_ptr2char(chunk[i]))) {
+ data->client_col = -1; // force cursor update
+ }
+ }
+ if (endcol < clearcol) {
+ remote_ui_cursor_goto(ui, row, endcol);
+ remote_ui_highlight_set(ui, (int)clearattr);
+ // legacy eol_clear was only ever used with cleared attributes
+ // so be on the safe side
+ if (clearattr == 0 && clearcol == Columns) {
+ Array args = ARRAY_DICT_INIT;
+ push_call(ui, "eol_clear", args);
+ } else {
+ for (Integer c = endcol; c < clearcol; c++) {
+ remote_ui_put(ui, " ");
+ }
+ }
+ }
+ }
+}
+
static void remote_ui_flush(UI *ui)
{
UIData *data = ui->data;
if (data->buffer.size > 0) {
+ if (!ui->ui_ext[kUINewgrid]) {
+ remote_ui_cursor_goto(ui, data->cursor_row, data->cursor_col);
+ }
rpc_send_event(data->channel_id, "redraw", data->buffer);
data->buffer = (Array)ARRAY_DICT_INIT;
}
}
+static void remote_ui_cmdline_show(UI *ui, Array args)
+{
+ Array new_args = ARRAY_DICT_INIT;
+ Array contents = args.items[0].data.array;
+ Array new_contents = ARRAY_DICT_INIT;
+ for (size_t i = 0; i < contents.size; i++) {
+ Array item = contents.items[i].data.array;
+ Array new_item = ARRAY_DICT_INIT;
+ int attr = (int)item.items[0].data.integer;
+ if (attr) {
+ HlAttrs *aep = syn_attr2entry(attr);
+ Dictionary rgb_attrs = hlattrs2dict(aep, ui->rgb ? kTrue : kFalse);
+ ADD(new_item, DICTIONARY_OBJ(rgb_attrs));
+ } else {
+ ADD(new_item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT));
+ }
+ ADD(new_item, copy_object(item.items[1]));
+ ADD(new_contents, ARRAY_OBJ(new_item));
+ }
+ ADD(new_args, ARRAY_OBJ(new_contents));
+ for (size_t i = 1; i < args.size; i++) {
+ ADD(new_args, copy_object(args.items[i]));
+ }
+ push_call(ui, "cmdline_show", new_args);
+}
+
static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed)
{
+ if (!ui->ui_ext[kUINewgrid]) {
+ // the representation of cmdline_show changed, translate back
+ if (strequal(name, "cmdline_show")) {
+ remote_ui_cmdline_show(ui, args);
+ // never consumes args
+ return;
+ }
+ }
Array my_args = ARRAY_DICT_INIT;
// Objects are currently single-reference
// make a copy, but only if necessary
diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h
index 3ef16a7ac3..456ad0c8cc 100644
--- a/src/nvim/api/ui_events.in.h
+++ b/src/nvim/api/ui_events.in.h
@@ -10,14 +10,6 @@
#include "nvim/func_attr.h"
#include "nvim/ui.h"
-void resize(Integer width, Integer height)
- FUNC_API_SINCE(3);
-void clear(void)
- FUNC_API_SINCE(3);
-void eol_clear(void)
- FUNC_API_SINCE(3);
-void cursor_goto(Integer row, Integer col)
- FUNC_API_SINCE(3);
void mode_info_set(Boolean enabled, Array cursor_styles)
FUNC_API_SINCE(3);
void update_menu(void)
@@ -32,29 +24,12 @@ void mouse_off(void)
FUNC_API_SINCE(3);
void mode_change(String mode, Integer mode_idx)
FUNC_API_SINCE(3);
-void set_scroll_region(Integer top, Integer bot, Integer left, Integer right)
- FUNC_API_SINCE(3);
-void scroll(Integer count)
- FUNC_API_SINCE(3);
-void highlight_set(HlAttrs attrs)
- FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL FUNC_API_BRIDGE_IMPL;
-void put(String str)
- FUNC_API_SINCE(3);
void bell(void)
FUNC_API_SINCE(3);
void visual_bell(void)
FUNC_API_SINCE(3);
void flush(void)
FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL;
-void update_fg(Integer fg)
- FUNC_API_SINCE(3) FUNC_API_BRIDGE_IMPL;
-void update_bg(Integer bg)
- FUNC_API_SINCE(3) FUNC_API_BRIDGE_IMPL;
-void update_sp(Integer sp)
- FUNC_API_SINCE(3) FUNC_API_BRIDGE_IMPL;
-void default_colors_set(Integer rgb_fg, Integer rgb_bg, Integer rgb_sp,
- Integer cterm_fg, Integer cterm_bg)
- FUNC_API_SINCE(4);
void suspend(void)
FUNC_API_SINCE(3) FUNC_API_BRIDGE_IMPL;
void set_title(String title)
@@ -64,6 +39,49 @@ void set_icon(String icon)
void option_set(String name, Object value)
FUNC_API_SINCE(4) FUNC_API_BRIDGE_IMPL;
+// First revison of the grid protocol, used by default
+void update_fg(Integer fg)
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+void update_bg(Integer bg)
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+void update_sp(Integer sp)
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+void resize(Integer width, Integer height)
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+void clear(void)
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+void eol_clear(void)
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+void cursor_goto(Integer row, Integer col)
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+void highlight_set(HlAttrs attrs)
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY FUNC_API_REMOTE_IMPL;
+void put(String str)
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+void set_scroll_region(Integer top, Integer bot, Integer left, Integer right)
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+void scroll(Integer count)
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+
+// Second revison of the grid protocol, used with ext_newgrid ui option
+void default_colors_set(Integer rgb_fg, Integer rgb_bg, Integer rgb_sp,
+ Integer cterm_fg, Integer cterm_bg)
+ FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL;
+void hl_attr_define(Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs,
+ Array info)
+ FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_BRIDGE_IMPL;
+void grid_resize(Integer grid, Integer width, Integer height)
+ FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL;
+void grid_clear(Integer grid)
+ FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL;
+void grid_cursor_goto(Integer grid, Integer row, Integer col)
+ FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL;
+void grid_line(Integer grid, Integer row, Integer col_start, Array data)
+ FUNC_API_SINCE(5) FUNC_API_REMOTE_ONLY;
+void grid_scroll(Integer grid, Integer top, Integer bot,
+ Integer left, Integer right, Integer rows, Integer cols)
+ FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL;
+
void popupmenu_show(Array items, Integer selected, Integer row, Integer col)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void popupmenu_hide(void)
diff --git a/src/nvim/event/defs.h b/src/nvim/event/defs.h
index cc875d74b9..55b2d277bb 100644
--- a/src/nvim/event/defs.h
+++ b/src/nvim/event/defs.h
@@ -4,7 +4,7 @@
#include <assert.h>
#include <stdarg.h>
-#define EVENT_HANDLER_MAX_ARGC 6
+#define EVENT_HANDLER_MAX_ARGC 9
typedef void (*argv_callback)(void **argv);
typedef struct message {
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index c1b9eff697..b077aefa1e 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -6320,8 +6320,10 @@ static void ex_stop(exarg_T *eap)
autowrite_all();
}
apply_autocmds(EVENT_VIMSUSPEND, NULL, NULL, false, NULL);
+
+ // TODO(bfredl): the TUI should do this on suspend
ui_cursor_goto((int)Rows - 1, 0);
- ui_linefeed();
+ ui_call_grid_scroll(1, 0, Rows, 0, Columns, 1, 0);
ui_flush();
ui_call_suspend(); // call machine specific function
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 6b52ce7c80..775d002e58 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -215,6 +215,8 @@ static int hislen = 0; /* actual length of history tables */
/// user interrupting highlight function to not interrupt command-line.
static bool getln_interrupted_highlight = false;
+static bool need_cursor_update = false;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ex_getln.c.generated.h"
@@ -2944,30 +2946,22 @@ static void ui_ext_cmdline_show(CmdlineInfo *line)
char *buf = xmallocz(len);
memset(buf, '*', len);
Array item = ARRAY_DICT_INIT;
- ADD(item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT));
+ ADD(item, INTEGER_OBJ(0));
ADD(item, STRING_OBJ(((String) { .data = buf, .size = len })));
ADD(content, ARRAY_OBJ(item));
} else if (kv_size(line->last_colors.colors)) {
for (size_t i = 0; i < kv_size(line->last_colors.colors); i++) {
CmdlineColorChunk chunk = kv_A(line->last_colors.colors, i);
Array item = ARRAY_DICT_INIT;
+ ADD(item, INTEGER_OBJ(chunk.attr));
- if (chunk.attr) {
- HlAttrs *aep = syn_attr2entry(chunk.attr);
- // TODO(bfredl): this desicion could be delayed by making attr_code a
- // recognized type
- Dictionary rgb_attrs = hlattrs2dict(aep, true);
- ADD(item, DICTIONARY_OBJ(rgb_attrs));
- } else {
- ADD(item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT));
- }
ADD(item, STRING_OBJ(cbuf_to_string((char *)line->cmdbuff + chunk.start,
chunk.end-chunk.start)));
ADD(content, ARRAY_OBJ(item));
}
} else {
Array item = ARRAY_DICT_INIT;
- ADD(item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT));
+ ADD(item, INTEGER_OBJ(0));
ADD(item, STRING_OBJ(cstr_to_string((char *)(line->cmdbuff))));
ADD(content, ARRAY_OBJ(item));
}
@@ -3033,6 +3027,8 @@ void cmdline_screen_cleared(void)
}
prev_ccline = prev_ccline->prev_ccline;
}
+
+ need_cursor_update = true;
}
/// called by ui_flush, do what redraws neccessary to keep cmdline updated.
@@ -3501,6 +3497,10 @@ static void cursorcmd(void)
if (ccline.redraw_state < kCmdRedrawPos) {
ccline.redraw_state = kCmdRedrawPos;
}
+ if (need_cursor_update) {
+ need_cursor_update = false;
+ setcursor();
+ }
return;
}
diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua
index 2666ca6e6f..e76b601d8a 100644
--- a/src/nvim/generators/gen_api_ui_events.lua
+++ b/src/nvim/generators/gen_api_ui_events.lua
@@ -132,19 +132,21 @@ for i = 1, #events do
end
end
- call_output:write('void ui_call_'..ev.name)
- write_signature(call_output, ev, '')
- call_output:write('\n{\n')
- if ev.remote_only then
- write_arglist(call_output, ev, false)
- call_output:write(' UI_LOG('..ev.name..', 0);\n')
- call_output:write(' ui_event("'..ev.name..'", args);\n')
- else
- call_output:write(' UI_CALL')
- write_signature(call_output, ev, ev.name, true)
- call_output:write(";\n")
+ if not (ev.remote_only and ev.remote_impl) then
+ call_output:write('void ui_call_'..ev.name)
+ write_signature(call_output, ev, '')
+ call_output:write('\n{\n')
+ if ev.remote_only then
+ write_arglist(call_output, ev, false)
+ call_output:write(' UI_LOG('..ev.name..', 0);\n')
+ call_output:write(' ui_event("'..ev.name..'", args);\n')
+ else
+ call_output:write(' UI_CALL')
+ write_signature(call_output, ev, ev.name, true)
+ call_output:write(";\n")
+ end
+ call_output:write("}\n\n")
end
- call_output:write("}\n\n")
end
diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c
index 5efbafa128..0b39ba442e 100644
--- a/src/nvim/highlight.c
+++ b/src/nvim/highlight.c
@@ -38,7 +38,7 @@ void highlight_init(void)
bool highlight_use_hlstate(void)
{
if (hlstate_active) {
- return false;
+ return false;
}
hlstate_active = true;
// hl tables must now be rebuilt.
@@ -89,9 +89,26 @@ static int get_attr_entry(HlEntry entry)
map_put(HlEntry, int)(attr_entry_ids, entry, id);
+ Array inspect = hl_inspect(id);
+
+ // Note: internally we don't distinguish between cterm and rgb attributes,
+ // remote_ui_hl_attr_define will however.
+ ui_call_hl_attr_define(id, entry.attr, entry.attr, inspect);
+ api_free_array(inspect);
return id;
}
+/// When a UI connects, we need to send it the table of higlights used so far.
+void ui_send_all_hls(UI *ui)
+{
+ for (size_t i = 1; i < kv_size(attr_entries); i++) {
+ Array inspect = hl_inspect((int)i);
+ ui->hl_attr_define(ui, (Integer)i, kv_A(attr_entries, i).attr,
+ kv_A(attr_entries, i).attr, inspect);
+ api_free_array(inspect);
+ }
+}
+
/// Get attribute code for a syntax group.
int hl_get_syn_attr(int idx, HlAttrs at_en)
{
diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h
index 5f637228ba..09d20c75ea 100644
--- a/src/nvim/highlight_defs.h
+++ b/src/nvim/highlight_defs.h
@@ -8,6 +8,8 @@
typedef int32_t RgbValue;
/// Highlighting attribute bits.
+///
+/// sign bit should not be used here, as it identifies invalid highlight
typedef enum {
HL_INVERSE = 0x01,
HL_BOLD = 0x02,
@@ -35,6 +37,17 @@ typedef struct attr_entry {
.cterm_bg_color = 0, \
}
+// sentinel value that compares unequal to any valid highlight
+#define HLATTRS_INVALID (HlAttrs) { \
+ .rgb_ae_attr = -1, \
+ .cterm_ae_attr = -1, \
+ .rgb_fg_color = -1, \
+ .rgb_bg_color = -1, \
+ .rgb_sp_color = -1, \
+ .cterm_fg_color = 0, \
+ .cterm_bg_color = 0, \
+}
+
/// Values for index in highlight_attr[].
/// When making changes, also update hlf_names below!
typedef enum {
diff --git a/src/nvim/lib/kvec.h b/src/nvim/lib/kvec.h
index 6d54c7f78d..93b2f053bc 100644
--- a/src/nvim/lib/kvec.h
+++ b/src/nvim/lib/kvec.h
@@ -98,14 +98,14 @@
(*kv_pushp(v) = (x))
#define kv_a(v, i) \
- (((v).capacity <= (size_t) (i) \
+ (*(((v).capacity <= (size_t) (i) \
? ((v).capacity = (v).size = (i) + 1, \
kv_roundup32((v).capacity), \
- kv_resize((v), (v).capacity), 0) \
+ kv_resize((v), (v).capacity), 0UL) \
: ((v).size <= (size_t) (i) \
? (v).size = (i) + 1 \
- : 0)), \
- (v).items[(i)])
+ : 0UL)), \
+ &(v).items[(i)]))
/// Type of a vector with a few first members allocated on stack
///
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 528fd921bc..96c2168bca 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -454,7 +454,6 @@ int main(int argc, char **argv)
}
setmouse(); // may start using the mouse
- ui_reset_scroll_region(); // In case Rows changed
if (exmode_active) {
must_redraw = CLEAR; // Don't clear the screen when starting in Ex mode.
@@ -1374,7 +1373,7 @@ static void handle_quickfix(mparm_T *paramp)
paramp->use_ef, OPT_FREE, SID_CARG);
vim_snprintf((char *)IObuff, IOSIZE, "cfile %s", p_ef);
if (qf_init(NULL, p_ef, p_efm, true, IObuff, p_menc) < 0) {
- ui_linefeed();
+ msg_putchar('\n');
mch_exit(3);
}
TIME_MSG("reading errorfile");
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 9d4d421941..46fc9115b4 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -1888,11 +1888,9 @@ static void msg_scroll_up(void)
fill_msgsep, fill_msgsep, HL_ATTR(HLF_MSGSEP));
}
int nscroll = MIN(msg_scrollsize()+1, Rows);
- ui_call_set_scroll_region(Rows-nscroll, Rows-1, 0, Columns-1);
- screen_del_lines(Rows-nscroll, 0, 1, nscroll, NULL);
- ui_reset_scroll_region();
+ screen_del_lines(Rows-nscroll, 1, Rows, 0, Columns);
} else {
- screen_del_lines(0, 0, 1, (int)Rows, NULL);
+ screen_del_lines(0, 1, (int)Rows, 0, Columns);
}
}
@@ -2307,9 +2305,9 @@ static int do_more_prompt(int typed_char)
mp_last = msg_sb_start(mp_last->sb_prev);
}
- if (toscroll == -1 && screen_ins_lines(0, 0, 1,
- (int)Rows, NULL) == OK) {
- /* display line at top */
+ if (toscroll == -1
+ && screen_ins_lines(0, 1, (int)Rows, 0, (int)Columns) == OK) {
+ // display line at top
(void)disp_sb_line(0, mp);
} else {
/* redisplay all lines */
diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c
index 3d7399f151..684f486c04 100644
--- a/src/nvim/misc1.c
+++ b/src/nvim/misc1.c
@@ -2714,7 +2714,7 @@ int call_shell(char_u *cmd, ShellOpts opts, char_u *extra_shell_arg)
if (p_verbose > 3) {
verbose_enter();
smsg(_("Calling shell to execute: \"%s\""), cmd == NULL ? p_sh : cmd);
- ui_linefeed();
+ msg_putchar('\n');
verbose_leave();
}
diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c
index 1182d3d902..5bd4b4ddff 100644
--- a/src/nvim/popupmnu.c
+++ b/src/nvim/popupmnu.c
@@ -341,6 +341,8 @@ void pum_redraw(void)
idx = i + pum_first;
attr = (idx == pum_selected) ? attr_select : attr_norm;
+ screen_puts_line_start(row);
+
// prepend a space if there is room
if (curwin->w_p_rl) {
if (pum_col < curwin->w_wincol + curwin->w_width - 1) {
@@ -488,6 +490,7 @@ void pum_redraw(void)
? attr_thumb : attr_scroll);
}
}
+ screen_puts_line_flush(false);
row++;
}
}
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 41a430bb8b..65a3c17286 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -300,7 +300,8 @@ void update_screen(int type)
type = CLEAR;
} else if (type != CLEAR) {
check_for_delay(false);
- if (screen_ins_lines(0, 0, msg_scrolled, (int)Rows, NULL) == FAIL) {
+ if (screen_ins_lines(0, msg_scrolled, (int)Rows, 0, (int)Columns)
+ == FAIL) {
type = CLEAR;
}
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
@@ -4314,13 +4315,14 @@ static void screen_line(int row, int coloff, int endcol,
/* 2: occupies two display cells */
# define CHAR_CELLS char_cells
+ int start_dirty = -1, end_dirty = 0;
+
/* Check for illegal row and col, just in case. */
if (row >= Rows)
row = Rows - 1;
if (endcol > Columns)
endcol = Columns;
-
off_from = (unsigned)(current_ScreenLine - ScreenLines);
off_to = LineOffset[row] + coloff;
max_off_from = off_from + screen_Columns;
@@ -4368,6 +4370,10 @@ static void screen_line(int row, int coloff, int endcol,
if (redraw_this) {
+ if (start_dirty == -1) {
+ start_dirty = col;
+ }
+ end_dirty = col + char_cells;
// When writing a single-width character over a double-width
// character and at the end of the redrawn text, need to clear out
// the right halve of the old character.
@@ -4388,12 +4394,11 @@ static void screen_line(int row, int coloff, int endcol,
}
ScreenAttrs[off_to] = ScreenAttrs[off_from];
- /* For simplicity set the attributes of second half of a
- * double-wide character equal to the first half. */
- if (char_cells == 2)
+ // For simplicity set the attributes of second half of a
+ // double-wide character equal to the first half.
+ if (char_cells == 2) {
ScreenAttrs[off_to + 1] = ScreenAttrs[off_from];
-
- screen_char(off_to, row, col + coloff);
+ }
}
off_to += CHAR_CELLS;
@@ -4405,23 +4410,29 @@ static void screen_line(int row, int coloff, int endcol,
/* Clear the second half of a double-wide character of which the left
* half was overwritten with a single-wide character. */
schar_from_ascii(ScreenLines[off_to], ' ');
- screen_char(off_to, row, col + coloff);
+ end_dirty++;
}
+ int clear_end = -1;
if (clear_width > 0 && !rlflag) {
// blank out the rest of the line
- while (col < clear_width && ScreenLines[off_to][0] == ' '
- && ScreenLines[off_to][1] == NUL
- && ScreenAttrs[off_to] == bg_attr
- ) {
- ++off_to;
- ++col;
- }
- if (col < clear_width) {
- screen_fill(row, row + 1, col + coloff, clear_width + coloff, ' ', ' ',
- bg_attr);
- off_to += clear_width - col;
- col = clear_width;
+ // TODO(bfredl): we could cache winline widths
+ while (col < clear_width) {
+ if (ScreenLines[off_to][0] != ' ' || ScreenLines[off_to][1] != NUL
+ || ScreenAttrs[off_to] != bg_attr) {
+ ScreenLines[off_to][0] = ' ';
+ ScreenLines[off_to][1] = NUL;
+ ScreenAttrs[off_to] = bg_attr;
+ if (start_dirty == -1) {
+ start_dirty = col;
+ end_dirty = col;
+ } else if (clear_end == -1) {
+ end_dirty = endcol;
+ }
+ clear_end = col+1;
+ }
+ col++;
+ off_to++;
}
}
@@ -4436,11 +4447,25 @@ static void screen_line(int row, int coloff, int endcol,
|| ScreenAttrs[off_to] != hl) {
schar_copy(ScreenLines[off_to], sc);
ScreenAttrs[off_to] = hl;
- screen_char(off_to, row, col + coloff);
+ if (start_dirty == -1) {
+ start_dirty = col;
+ }
+ end_dirty = col+1;
}
} else
LineWraps[row] = FALSE;
}
+
+ if (clear_end < end_dirty) {
+ clear_end = end_dirty;
+ }
+ if (start_dirty == -1) {
+ start_dirty = end_dirty;
+ }
+ if (clear_end > start_dirty) {
+ ui_line(row, coloff+start_dirty, coloff+end_dirty, coloff+clear_end,
+ bg_attr);
+ }
}
/*
@@ -4722,11 +4747,11 @@ win_redr_status_matches (
/* Put the wildmenu just above the command line. If there is
* no room, scroll the screen one line up. */
if (cmdline_row == Rows - 1) {
- screen_del_lines(0, 0, 1, (int)Rows, NULL);
- ++msg_scrolled;
+ screen_del_lines(0, 1, (int)Rows, 0, (int)Columns);
+ msg_scrolled++;
} else {
- ++cmdline_row;
- ++row;
+ cmdline_row++;
+ row++;
}
wild_menu_showing = WM_SCROLLED;
} else {
@@ -5090,6 +5115,8 @@ win_redr_custom (
/*
* Draw each snippet with the specified highlighting.
*/
+ screen_puts_line_start(row);
+
curattr = attr;
p = buf;
for (n = 0; hltab[n].start != NULL; n++) {
@@ -5110,6 +5137,8 @@ win_redr_custom (
// Make sure to use an empty string instead of p, if p is beyond buf + len.
screen_puts(p >= buf + len ? (char_u *)"" : p, row, col, curattr);
+ screen_puts_line_flush(false);
+
if (wp == NULL) {
// Fill the tab_page_click_defs array for clicking in the tab pages line.
col = 0;
@@ -5207,7 +5236,6 @@ void screen_getbytes(int row, int col, char_u *bytes, int *attrp)
}
}
-
/*
* Put string '*text' on the screen at position 'row' and 'col', with
* attributes 'attr', and update ScreenLines[] and ScreenAttrs[].
@@ -5219,6 +5247,20 @@ void screen_puts(char_u *text, int row, int col, int attr)
screen_puts_len(text, -1, row, col, attr);
}
+static int put_dirty_row = -1;
+static int put_dirty_first = -1;
+static int put_dirty_last = 0;
+
+/// Start a group of screen_puts_len calls that builds a single screen line.
+///
+/// Must be matched with a screen_puts_line_flush call before moving to
+/// another line.
+void screen_puts_line_start(int row)
+{
+ assert(put_dirty_row == -1);
+ put_dirty_row = row;
+}
+
/*
* Like screen_puts(), but output "text[len]". When "len" is -1 output up to
* a NUL.
@@ -5242,6 +5284,16 @@ void screen_puts_len(char_u *text, int textlen, int row, int col, int attr)
int force_redraw_next = FALSE;
int need_redraw;
+ bool do_flush = false;
+ if (put_dirty_row == -1) {
+ screen_puts_line_start(row);
+ do_flush = true;
+ } else {
+ if (row != put_dirty_row) {
+ abort();
+ }
+ }
+
if (ScreenLines == NULL || row >= screen_Rows) /* safety check */
return;
off = LineOffset[row] + col;
@@ -5252,9 +5304,12 @@ void screen_puts_len(char_u *text, int textlen, int row, int col, int attr)
schar_from_ascii(ScreenLines[off - 1], ' ');
ScreenAttrs[off - 1] = 0;
// redraw the previous cell, make it empty
- screen_char(off - 1, row, col - 1);
- /* force the cell at "col" to be redrawn */
- force_redraw_next = TRUE;
+ if (put_dirty_first == -1) {
+ put_dirty_first = col-1;
+ }
+ put_dirty_last = col+1;
+ // force the cell at "col" to be redrawn
+ force_redraw_next = true;
}
max_off = LineOffset[row] + screen_Columns;
@@ -5333,8 +5388,12 @@ void screen_puts_len(char_u *text, int textlen, int row, int col, int attr)
ScreenLines[off + 1][0] = 0;
ScreenAttrs[off + 1] = attr;
}
- screen_char(off, row, col);
+ if (put_dirty_first == -1) {
+ put_dirty_first = col;
+ }
+ put_dirty_last = col+mbyte_cells;
}
+
off += mbyte_cells;
col += mbyte_cells;
ptr += mbyte_blen;
@@ -5345,11 +5404,29 @@ void screen_puts_len(char_u *text, int textlen, int row, int col, int attr)
}
}
- /* If we detected the next character needs to be redrawn, but the text
- * doesn't extend up to there, update the character here. */
- if (force_redraw_next && col < screen_Columns) {
- screen_char(off, row, col);
+ if (do_flush) {
+ screen_puts_line_flush(true);
+ }
+}
+
+/// End a group of screen_puts_len calls and send the screen buffer to the UI
+/// layer.
+///
+/// @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 screen_puts_line_flush(bool set_cursor)
+{
+ assert(put_dirty_row != -1);
+ if (put_dirty_first != -1) {
+ if (set_cursor) {
+ ui_cursor_goto(put_dirty_row, put_dirty_last);
+ }
+ ui_line(put_dirty_row, put_dirty_first, put_dirty_last, put_dirty_last, 0);
+ put_dirty_first = -1;
+ put_dirty_last = 0;
}
+ put_dirty_row = -1;
}
/*
@@ -5641,32 +5718,6 @@ next_search_hl_pos(
return 0;
}
-/*
- * Put character ScreenLines["off"] on the screen at position "row" and "col",
- * using the attributes from ScreenAttrs["off"].
- */
-static void screen_char(unsigned off, int row, int col)
-{
- // Check for illegal values, just in case (could happen just after resizing).
- if (row >= screen_Rows || col >= screen_Columns) {
- return;
- }
-
- // Outputting the last character on the screen may scrollup the screen.
- // Don't to it! Mark the character invalid (update it when scrolled up)
- // FIXME: The premise here is not actually true (cf. deferred wrap).
- if (row == screen_Rows - 1 && col == screen_Columns - 1
- // account for first command-line character in rightleft mode
- && !cmdmsg_rl) {
- ScreenAttrs[off] = (sattr_T)-1;
- return;
- }
-
- ui_cursor_goto(row, col);
- ui_set_highlight(ScreenAttrs[off]);
-
- ui_puts(ScreenLines[off]);
-}
/*
* Fill the screen from 'start_row' to 'end_row', from 'start_col' to 'end_col'
@@ -5675,12 +5726,6 @@ static void screen_char(unsigned off, int row, int col)
*/
void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1, int c2, int attr)
{
- int row;
- int col;
- int off;
- int end_off;
- int did_delete;
- int c;
schar_T sc;
if (end_row > screen_Rows) /* safety check */
@@ -5692,8 +5737,7 @@ void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1,
|| start_col >= end_col) /* nothing to do */
return;
- /* it's a "normal" terminal when not in a GUI or cterm */
- for (row = start_row; row < end_row; ++row) {
+ for (int row = start_row; row < end_row; row++) {
if (has_mbyte) {
// When drawing over the right halve of a double-wide char clear
// out the left halve. When drawing over the left halve of a
@@ -5706,71 +5750,52 @@ void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1,
screen_puts_len((char_u *)" ", 1, row, end_col, 0);
}
}
- /*
- * Try to use delete-line termcap code, when no attributes or in a
- * "normal" terminal, where a bold/italic space is just a
- * space.
- */
- did_delete = FALSE;
- if (c2 == ' '
- && end_col == Columns
- && attr == 0) {
- /*
- * check if we really need to clear something
- */
- col = start_col;
- if (c1 != ' ') /* don't clear first char */
- ++col;
-
- off = LineOffset[row] + col;
- end_off = LineOffset[row] + end_col;
-
- // skip blanks (used often, keep it fast!)
- while (off < end_off && ScreenLines[off][0] == ' '
- && ScreenLines[off][1] == 0 && ScreenAttrs[off] == 0) {
- off++;
- }
- if (off < end_off) { // something to be cleared
- col = off - LineOffset[row];
- ui_clear_highlight();
- ui_cursor_goto(row, col); // clear rest of this screen line
- ui_call_eol_clear();
- col = end_col - col;
- while (col--) { // clear chars in ScreenLines
- schar_from_ascii(ScreenLines[off], ' ');
- ScreenAttrs[off] = 0;
- ++off;
- }
- }
- did_delete = TRUE; /* the chars are cleared now */
- }
- off = LineOffset[row] + start_col;
- c = c1;
- schar_from_char(sc, c);
+ int dirty_first = INT_MAX;
+ int dirty_last = 0;
+ int col = start_col;
+ schar_from_char(sc, c1);
+ int lineoff = LineOffset[row];
for (col = start_col; col < end_col; col++) {
+ int off = lineoff + col;
if (schar_cmp(ScreenLines[off], sc) || ScreenAttrs[off] != attr) {
schar_copy(ScreenLines[off], sc);
ScreenAttrs[off] = attr;
- if (!did_delete || c != ' ')
- screen_char(off, row, col);
+ if (dirty_first == INT_MAX) {
+ dirty_first = col;
+ }
+ dirty_last = col+1;
}
- ++off;
if (col == start_col) {
- if (did_delete)
- break;
- c = c2;
- schar_from_char(sc, c);
+ schar_from_char(sc, c2);
}
}
- if (end_col == Columns)
- LineWraps[row] = FALSE;
- if (row == Rows - 1) { /* overwritten the command line */
- redraw_cmdline = TRUE;
- if (c1 == ' ' && c2 == ' ')
- clear_cmdline = FALSE; /* command line has been cleared */
- if (start_col == 0)
- mode_displayed = FALSE; /* mode cleared or overwritten */
+ if (dirty_last > dirty_first) {
+ // TODO(bfredl): support a cleared suffix even with a batched line?
+ if (put_dirty_row == row) {
+ if (put_dirty_first == -1) {
+ put_dirty_first = dirty_first;
+ }
+ put_dirty_last = MAX(put_dirty_last, dirty_last);
+ } else {
+ int last = c2 != ' ' ? dirty_last : dirty_first + (c1 != ' ');
+ ui_line(row, dirty_first, last, dirty_last, attr);
+ }
+ }
+
+ if (end_col == Columns) {
+ LineWraps[row] = false;
+ }
+
+ // TODO(bfredl): The relevant caller should do this
+ if (row == Rows - 1) { // overwritten the command line
+ redraw_cmdline = true;
+ if (c1 == ' ' && c2 == ' ') {
+ clear_cmdline = false; // command line has been cleared
+ }
+ if (start_col == 0) {
+ mode_displayed = false; // mode cleared or overwritten
+ }
}
}
}
@@ -6027,15 +6052,13 @@ static void screenclear2(void)
return;
}
- ui_clear_highlight(); // don't want highlighting here
-
/* blank out ScreenLines */
for (i = 0; i < Rows; ++i) {
lineclear(LineOffset[i], (int)Columns);
LineWraps[i] = FALSE;
}
- ui_call_clear(); // clear the display
+ ui_call_grid_clear(1); // clear the display
clear_cmdline = false;
mode_displayed = false;
screen_cleared = true; // can use contents of ScreenLines now
@@ -6064,18 +6087,16 @@ static void lineclear(unsigned off, int width)
(void)memset(ScreenAttrs + off, 0, (size_t)width * sizeof(sattr_T));
}
-/*
- * Copy part of a Screenline for vertically split window "wp".
- */
-static void linecopy(int to, int from, win_T *wp)
+/// Copy part of a Screenline for vertically split window.
+static void linecopy(int to, int from, int col, int width)
{
- const unsigned off_to = LineOffset[to] + wp->w_wincol;
- const unsigned off_from = LineOffset[from] + wp->w_wincol;
+ unsigned off_to = LineOffset[to] + col;
+ unsigned off_from = LineOffset[from] + col;
memmove(ScreenLines + off_to, ScreenLines + off_from,
- wp->w_width * sizeof(schar_T));
+ width * sizeof(schar_T));
memmove(ScreenAttrs + off_to, ScreenAttrs + off_from,
- wp->w_width * sizeof(ScreenAttrs[0]));
+ width * sizeof(sattr_T));
}
/*
@@ -6153,15 +6174,16 @@ static int win_do_lines(win_T *wp, int row, int line_count,
// otherwise it will stay there forever.
clear_cmdline = TRUE;
int retval;
- ui_set_scroll_region(wp, row);
+
if (del) {
- retval = screen_del_lines(wp->w_winrow + row, 0, line_count,
- wp->w_height - row, wp);
+ retval = screen_del_lines(wp->w_winrow + row, line_count,
+ wp->w_winrow + wp->w_height,
+ wp->w_wincol, wp->w_width);
} else {
- retval = screen_ins_lines(wp->w_winrow + row, 0, line_count,
- wp->w_height - row, wp);
+ retval = screen_ins_lines(wp->w_winrow + row, line_count,
+ wp->w_winrow + wp->w_height,
+ wp->w_wincol, wp->w_width);
}
- ui_reset_scroll_region();
return retval;
}
@@ -6189,19 +6211,13 @@ static void win_rest_invalid(win_T *wp)
*/
-// insert lines on the screen and update ScreenLines[]
-// 'end' is the line after the scrolled part. Normally it is Rows.
-// When scrolling region used 'off' is the offset from the top for the region.
-// 'row' and 'end' are relative to the start of the region.
-//
-// return FAIL for failure, OK for success.
-int screen_ins_lines (
- int off,
- int row,
- int line_count,
- int end,
- win_T *wp /* NULL or window to use width from */
-)
+/// insert lines on the screen and update ScreenLines[]
+/// 'end' is the line after the scrolled part. Normally it is Rows.
+/// When scrolling region used 'off' is the offset from the top for the region.
+/// 'row' and 'end' are relative to the start of the region.
+///
+/// @return FAIL for failure, OK for success.
+int screen_ins_lines(int row, int line_count, int end, int col, int width)
{
int i;
int j;
@@ -6213,18 +6229,16 @@ int screen_ins_lines (
// Shift LineOffset[] line_count down to reflect the inserted lines.
// Clear the inserted lines in ScreenLines[].
- row += off;
- end += off;
- for (i = 0; i < line_count; ++i) {
- if (wp != NULL && wp->w_width != Columns) {
+ for (i = 0; i < line_count; i++) {
+ if (width != Columns) {
// need to copy part of a line
j = end - 1 - i;
while ((j -= line_count) >= row) {
- linecopy(j + line_count, j, wp);
+ linecopy(j + line_count, j, col, width);
}
j += line_count;
- lineclear(LineOffset[j] + wp->w_wincol, wp->w_width);
- LineWraps[j] = FALSE;
+ lineclear(LineOffset[j] + col, width);
+ LineWraps[j] = false;
} else {
j = end - 1 - i;
temp = LineOffset[j];
@@ -6233,29 +6247,23 @@ int screen_ins_lines (
LineWraps[j + line_count] = LineWraps[j];
}
LineOffset[j + line_count] = temp;
- LineWraps[j + line_count] = FALSE;
+ LineWraps[j + line_count] = false;
lineclear(temp, (int)Columns);
}
}
- ui_call_scroll(-line_count);
+ ui_call_grid_scroll(1, row, end, col, col+width, -line_count, 0);
return OK;
}
-// delete lines on the screen and update ScreenLines[]
-// 'end' is the line after the scrolled part. Normally it is Rows.
-// When scrolling region used 'off' is the offset from the top for the region.
-// 'row' and 'end' are relative to the start of the region.
-//
-// Return OK for success, FAIL if the lines are not deleted.
-int screen_del_lines (
- int off,
- int row,
- int line_count,
- int end,
- win_T *wp /* NULL or window to use width from */
-)
+/// delete lines on the screen and update ScreenLines[]
+/// 'end' is the line after the scrolled part. Normally it is Rows.
+/// When scrolling region used 'off' is the offset from the top for the region.
+/// 'row' and 'end' are relative to the start of the region.
+///
+/// Return OK for success, FAIL if the lines are not deleted.
+int screen_del_lines(int row, int line_count, int end, int col, int width)
{
int j;
int i;
@@ -6267,18 +6275,16 @@ int screen_del_lines (
// Now shift LineOffset[] line_count up to reflect the deleted lines.
// Clear the inserted lines in ScreenLines[].
- row += off;
- end += off;
- for (i = 0; i < line_count; ++i) {
- if (wp != NULL && wp->w_width != Columns) {
+ for (i = 0; i < line_count; i++) {
+ if (width != Columns) {
// need to copy part of a line
j = row + i;
while ((j += line_count) <= end - 1) {
- linecopy(j - line_count, j, wp);
+ linecopy(j - line_count, j, col, width);
}
j -= line_count;
- lineclear(LineOffset[j] + wp->w_wincol, wp->w_width);
- LineWraps[j] = FALSE;
+ lineclear(LineOffset[j] + col, width);
+ LineWraps[j] = false;
} else {
// whole width, moving the line pointers is faster
j = row + i;
@@ -6288,16 +6294,17 @@ int screen_del_lines (
LineWraps[j - line_count] = LineWraps[j];
}
LineOffset[j - line_count] = temp;
- LineWraps[j - line_count] = FALSE;
+ LineWraps[j - line_count] = false;
lineclear(temp, (int)Columns);
}
}
- ui_call_scroll(line_count);
+ ui_call_grid_scroll(1, row, end, col, col+width, line_count, 0);
return OK;
}
+
/*
* show the current mode and ruler
*
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index ff9619574b..ff47e443f9 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -6798,14 +6798,12 @@ void do_highlight(const char *line, const bool forceit, const bool init)
HL_TABLE()[idx].sg_cterm_fg = color + 1;
if (is_normal_group) {
cterm_normal_fg_color = color + 1;
- must_redraw = CLEAR;
}
} else {
HL_TABLE()[idx].sg_cterm_bg = color + 1;
if (is_normal_group) {
cterm_normal_bg_color = color + 1;
if (!ui_rgb_attached()) {
- must_redraw = CLEAR;
if (color >= 0) {
int dark = -1;
@@ -6909,8 +6907,16 @@ void do_highlight(const char *line, const bool forceit, const bool init)
// Need to update all groups, because they might be using "bg" and/or
// "fg", which have been changed now.
highlight_attr_set_all();
- // If the normal group has changed, it is simpler to refresh every UI
- ui_refresh();
+
+ if (!ui_is_external(kUINewgrid)) {
+ // Older UIs assume that we clear the screen after normal group is
+ // changed
+ ui_refresh();
+ } else {
+ // TUI and newer UIs will repaint the screen themselves. NOT_VALID
+ // redraw below will still handle usages of guibg=fg etc.
+ ui_default_colors_set();
+ }
} else {
set_hl_attr(idx);
}
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index 03288a3325..56c47ed6cc 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -88,6 +88,7 @@ typedef struct {
bool cont_received;
UGrid grid;
kvec_t(Rect) invalid_regions;
+ int row, col;
int out_fd;
bool scroll_region_is_full_screen;
bool can_change_scroll_region;
@@ -98,6 +99,8 @@ typedef struct {
bool busy, is_invisible;
bool cork, overflow;
cursorentry_T cursor_shapes[SHAPE_IDX_COUNT];
+ HlAttrs clear_attrs;
+ kvec_t(HlAttrs) attrs;
HlAttrs print_attrs;
bool default_attr;
ModeShape showing_mode;
@@ -126,10 +129,9 @@ UI *tui_start(void)
{
UI *ui = xcalloc(1, sizeof(UI)); // Freed by ui_bridge_stop().
ui->stop = tui_stop;
- ui->resize = tui_resize;
- ui->clear = tui_clear;
- ui->eol_clear = tui_eol_clear;
- ui->cursor_goto = tui_cursor_goto;
+ ui->grid_resize = tui_grid_resize;
+ ui->grid_clear = tui_grid_clear;
+ ui->grid_cursor_goto = tui_grid_cursor_goto;
ui->mode_info_set = tui_mode_info_set;
ui->update_menu = tui_update_menu;
ui->busy_start = tui_busy_start;
@@ -137,10 +139,8 @@ UI *tui_start(void)
ui->mouse_on = tui_mouse_on;
ui->mouse_off = tui_mouse_off;
ui->mode_change = tui_mode_change;
- ui->set_scroll_region = tui_set_scroll_region;
- ui->scroll = tui_scroll;
- ui->highlight_set = tui_highlight_set;
- ui->put = tui_put;
+ ui->grid_scroll = tui_grid_scroll;
+ ui->hl_attr_define = tui_hl_attr_define;
ui->bell = tui_bell;
ui->visual_bell = tui_visual_bell;
ui->default_colors_set = tui_default_colors_set;
@@ -149,8 +149,10 @@ UI *tui_start(void)
ui->set_title = tui_set_title;
ui->set_icon = tui_set_icon;
ui->option_set= tui_option_set;
+ ui->raw_line = tui_raw_line;
memset(ui->ui_ext, 0, sizeof(ui->ui_ext));
+ ui->ui_ext[kUINewgrid] = true;
return ui_bridge_attach(ui, tui_main, tui_scheduler);
}
@@ -290,7 +292,7 @@ static void terminfo_stop(UI *ui)
static void tui_terminal_start(UI *ui)
{
TUIData *data = ui->data;
- data->print_attrs = HLATTRS_INIT;
+ data->print_attrs = HLATTRS_INVALID;
ugrid_init(&data->grid);
terminfo_start(ui);
update_size(ui);
@@ -346,6 +348,9 @@ static void tui_main(UIBridgeData *bridge, UI *ui)
signal_watcher_start(&data->cont_handle, sigcont_cb, SIGCONT);
#endif
+ // TODO(bfredl): zero hl is empty, send this explicitly?
+ kv_push(data->attrs, HLATTRS_INIT);
+
#if TERMKEY_VERSION_MAJOR > 0 || TERMKEY_VERSION_MINOR > 18
data->input.tk_ti_hook_fn = tui_tk_ti_getstr;
#endif
@@ -380,6 +385,7 @@ static void tui_main(UIBridgeData *bridge, UI *ui)
signal_watcher_close(&data->winch_handle, NULL);
loop_close(&tui_loop, false);
kv_destroy(data->invalid_regions);
+ kv_destroy(data->attrs);
xfree(data);
}
@@ -438,18 +444,17 @@ static void update_attrs(UI *ui, HlAttrs attrs)
}
data->print_attrs = attrs;
- UGrid *grid = &data->grid;
int fg = ui->rgb ? attrs.rgb_fg_color : (attrs.cterm_fg_color - 1);
if (fg == -1) {
- fg = ui->rgb ? grid->clear_attrs.rgb_fg_color
- : (grid->clear_attrs.cterm_fg_color - 1);
+ fg = ui->rgb ? data->clear_attrs.rgb_fg_color
+ : (data->clear_attrs.cterm_fg_color - 1);
}
int bg = ui->rgb ? attrs.rgb_bg_color : (attrs.cterm_bg_color - 1);
if (bg == -1) {
- bg = ui->rgb ? grid->clear_attrs.rgb_bg_color
- : (grid->clear_attrs.cterm_bg_color - 1);
+ bg = ui->rgb ? data->clear_attrs.rgb_bg_color
+ : (data->clear_attrs.cterm_bg_color - 1);
}
int attr = ui->rgb ? attrs.rgb_ae_attr : attrs.cterm_ae_attr;
@@ -592,6 +597,8 @@ static void cursor_goto(UI *ui, int row, int col)
if (row == grid->row && col == grid->col) {
return;
}
+ grid->row = row;
+ grid->col = col;
if (0 == row && 0 == col) {
unibi_out(ui, unibi_cursor_home);
ugrid_goto(grid, row, col);
@@ -679,20 +686,20 @@ static void cursor_goto(UI *ui, int row, int col)
ugrid_goto(grid, row, col);
}
-static void clear_region(UI *ui, int top, int bot, int left, int right)
+static void clear_region(UI *ui, int top, int bot, int left, int right,
+ HlAttrs attrs)
{
TUIData *data = ui->data;
UGrid *grid = &data->grid;
- int saved_row = grid->row;
- int saved_col = grid->col;
bool cleared = false;
- bool nobg = ui->rgb ? grid->clear_attrs.rgb_bg_color == -1
- : grid->clear_attrs.cterm_bg_color == 0;
+ // TODO(bfredl): support BCE for non-default background
+ bool nobg = ui->rgb ? attrs.rgb_bg_color == -1
+ : attrs.cterm_bg_color == 0;
if (nobg && right == ui->width -1) {
// Background is set to the default color and the right edge matches the
// screen end, try to use terminal codes for clearing the requested area.
- update_attrs(ui, grid->clear_attrs);
+ update_attrs(ui, attrs);
if (left == 0) {
if (bot == ui->height - 1) {
if (top == 0) {
@@ -725,7 +732,7 @@ static void clear_region(UI *ui, int top, int bot, int left, int right)
}
// restore cursor
- cursor_goto(ui, saved_row, saved_col);
+ cursor_goto(ui, data->row, data->col);
}
static bool can_use_scroll(UI * ui)
@@ -792,7 +799,7 @@ static void reset_scroll_region(UI *ui)
unibi_goto(ui, grid->row, grid->col);
}
-static void tui_resize(UI *ui, Integer width, Integer height)
+static void tui_grid_resize(UI *ui, Integer g, Integer width, Integer height)
{
TUIData *data = ui->data;
ugrid_resize(&data->grid, (int)width, (int)height);
@@ -810,25 +817,21 @@ static void tui_resize(UI *ui, Integer width, Integer height)
}
}
-static void tui_clear(UI *ui)
+static void tui_grid_clear(UI *ui, Integer g)
{
TUIData *data = ui->data;
UGrid *grid = &data->grid;
ugrid_clear(grid);
kv_size(data->invalid_regions) = 0;
- clear_region(ui, grid->top, grid->bot, grid->left, grid->right);
+ clear_region(ui, grid->top, grid->bot, grid->left, grid->right,
+ data->clear_attrs);
}
-static void tui_eol_clear(UI *ui)
+static void tui_grid_cursor_goto(UI *ui, Integer grid, Integer row, Integer col)
{
TUIData *data = ui->data;
- UGrid *grid = &data->grid;
- ugrid_eol_clear(grid);
- clear_region(ui, grid->row, grid->row, grid->col, grid->right);
-}
-
-static void tui_cursor_goto(UI *ui, Integer row, Integer col)
-{
+ data->row = (int)row;
+ data->col = (int)col;
cursor_goto(ui, (int)row, (int)col);
}
@@ -958,27 +961,23 @@ static void tui_mode_change(UI *ui, String mode, Integer mode_idx)
data->showing_mode = (ModeShape)mode_idx;
}
-static void tui_set_scroll_region(UI *ui, Integer top, Integer bot,
- Integer left, Integer right)
+static void tui_grid_scroll(UI *ui, Integer g, Integer top, Integer bot,
+ Integer left, Integer right,
+ Integer rows, Integer cols)
{
TUIData *data = ui->data;
- ugrid_set_scroll_region(&data->grid, (int)top, (int)bot,
- (int)left, (int)right);
+ UGrid *grid = &data->grid;
+ ugrid_set_scroll_region(&data->grid, (int)top, (int)bot-1,
+ (int)left, (int)right-1);
+
data->scroll_region_is_full_screen =
- left == 0 && right == ui->width - 1
- && top == 0 && bot == ui->height - 1;
-}
+ left == 0 && right == ui->width
+ && top == 0 && bot == ui->height;
-static void tui_scroll(UI *ui, Integer count)
-{
- TUIData *data = ui->data;
- UGrid *grid = &data->grid;
int clear_top, clear_bot;
- ugrid_scroll(grid, (int)count, &clear_top, &clear_bot);
+ ugrid_scroll(grid, (int)rows, &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);
@@ -989,21 +988,21 @@ static void tui_scroll(UI *ui, Integer count)
cursor_goto(ui, grid->top, grid->left);
// also set default color attributes or some terminals can become funny
if (scroll_clears_to_current_colour) {
- update_attrs(ui, grid->clear_attrs);
+ update_attrs(ui, data->clear_attrs);
}
- if (count > 0) {
- if (count == 1) {
+ if (rows > 0) {
+ if (rows == 1) {
unibi_out(ui, unibi_delete_line);
} else {
- UNIBI_SET_NUM_VAR(data->params[0], (int)count);
+ UNIBI_SET_NUM_VAR(data->params[0], (int)rows);
unibi_out(ui, unibi_parm_delete_line);
}
} else {
- if (count == -1) {
+ if (rows == -1) {
unibi_out(ui, unibi_insert_line);
} else {
- UNIBI_SET_NUM_VAR(data->params[0], -(int)count);
+ UNIBI_SET_NUM_VAR(data->params[0], -(int)rows);
unibi_out(ui, unibi_parm_insert_line);
}
}
@@ -1012,12 +1011,13 @@ static void tui_scroll(UI *ui, Integer count)
if (!data->scroll_region_is_full_screen) {
reset_scroll_region(ui);
}
- cursor_goto(ui, saved_row, saved_col);
+ cursor_goto(ui, data->row, data->col);
if (!scroll_clears_to_current_colour) {
// Scrolling will leave wrong background in the cleared area on non-BCE
// terminals. Update the cleared area.
- clear_region(ui, clear_top, clear_bot, grid->left, grid->right);
+ clear_region(ui, clear_top, clear_bot, grid->left, grid->right,
+ data->clear_attrs);
}
} else {
// Mark the entire scroll region as invalid for redrawing later
@@ -1025,23 +1025,11 @@ static void tui_scroll(UI *ui, Integer count)
}
}
-static void tui_highlight_set(UI *ui, HlAttrs attrs)
-{
- ((TUIData *)ui->data)->grid.attrs = attrs;
-}
-
-static void tui_put(UI *ui, String text)
+static void tui_hl_attr_define(UI *ui, Integer id, HlAttrs attrs,
+ HlAttrs cterm_attrs, Array info)
{
TUIData *data = ui->data;
- UGrid *grid = &data->grid;
- UCell *cell;
-
- cell = ugrid_put(&data->grid, (uint8_t *)text.data, text.size);
- // ugrid_put does not advance the cursor correctly, as the actual terminal
- // will when we print. Its cursor motion model is simplistic and wrong. So
- // we have to undo what it has just done before doing it right.
- grid->col--;
- print_cell(ui, cell);
+ kv_a(data->attrs, (size_t)id) = attrs;
}
static void tui_bell(UI *ui)
@@ -1058,12 +1046,16 @@ static void tui_default_colors_set(UI *ui, Integer rgb_fg, Integer rgb_bg,
Integer rgb_sp,
Integer cterm_fg, Integer cterm_bg)
{
- UGrid *grid = &((TUIData *)ui->data)->grid;
- grid->clear_attrs.rgb_fg_color = (int)rgb_fg;
- grid->clear_attrs.rgb_bg_color = (int)rgb_bg;
- grid->clear_attrs.rgb_sp_color = (int)rgb_sp;
- grid->clear_attrs.cterm_fg_color = (int)cterm_fg;
- grid->clear_attrs.cterm_bg_color = (int)cterm_bg;
+ TUIData *data = ui->data;
+
+ data->clear_attrs.rgb_fg_color = (int)rgb_fg;
+ data->clear_attrs.rgb_bg_color = (int)rgb_bg;
+ data->clear_attrs.rgb_sp_color = (int)rgb_sp;
+ data->clear_attrs.cterm_fg_color = (int)cterm_fg;
+ data->clear_attrs.cterm_bg_color = (int)cterm_bg;
+
+ data->print_attrs = HLATTRS_INVALID;
+ invalidate(ui, 0, data->grid.height-1, 0, data->grid.width-1);
}
static void tui_flush(UI *ui)
@@ -1083,9 +1075,6 @@ 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);
assert(r.bot < grid->height && r.right < grid->width);
@@ -1095,7 +1084,7 @@ static void tui_flush(UI *ui)
});
}
- cursor_goto(ui, saved_row, saved_col);
+ cursor_goto(ui, data->row, data->col);
flush_buf(ui);
}
@@ -1176,10 +1165,37 @@ static void tui_option_set(UI *ui, String name, Object value)
TUIData *data = ui->data;
if (strequal(name.data, "termguicolors")) {
ui->rgb = value.data.boolean;
+
+ data->print_attrs = HLATTRS_INVALID;
invalidate(ui, 0, data->grid.height-1, 0, data->grid.width-1);
}
}
+static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol,
+ Integer endcol, Integer clearcol, Integer clearattr,
+ const schar_T *chunk, const sattr_T *attrs)
+{
+ TUIData *data = ui->data;
+ UGrid *grid = &data->grid;
+ for (Integer c = startcol; c < endcol; c++) {
+ memcpy(grid->cells[linerow][c].data, chunk[c-startcol], sizeof(schar_T));
+ grid->cells[linerow][c].attrs = kv_A(data->attrs, attrs[c-startcol]);
+ }
+ UGRID_FOREACH_CELL(grid, (int)linerow, (int)linerow, (int)startcol,
+ (int)endcol-1, {
+ cursor_goto(ui, row, col);
+ print_cell(ui, cell);
+ });
+
+ if (clearcol > endcol) {
+ HlAttrs cl_attrs = kv_A(data->attrs, (size_t)clearattr);
+ ugrid_clear_chunk(grid, (int)linerow, (int)endcol, (int)clearcol,
+ cl_attrs);
+ clear_region(ui, (int)linerow, (int)linerow, (int)endcol, (int)clearcol-1,
+ cl_attrs);
+ }
+}
+
static void invalidate(UI *ui, int top, int bot, int left, int right)
{
TUIData *data = ui->data;
diff --git a/src/nvim/ugrid.c b/src/nvim/ugrid.c
index 6d420ef2f8..48f3cff2d7 100644
--- a/src/nvim/ugrid.c
+++ b/src/nvim/ugrid.c
@@ -17,7 +17,6 @@
void ugrid_init(UGrid *grid)
{
grid->attrs = HLATTRS_INIT;
- grid->clear_attrs = HLATTRS_INIT;
grid->cells = NULL;
}
@@ -45,12 +44,13 @@ void ugrid_resize(UGrid *grid, int width, int height)
void ugrid_clear(UGrid *grid)
{
- clear_region(grid, grid->top, grid->bot, grid->left, grid->right);
+ clear_region(grid, grid->top, grid->bot, grid->left, grid->right,
+ HLATTRS_INIT);
}
-void ugrid_eol_clear(UGrid *grid)
+void ugrid_clear_chunk(UGrid *grid, int row, int col, int endcol, HlAttrs attrs)
{
- clear_region(grid, grid->row, grid->row, grid->col, grid->right);
+ clear_region(grid, row, row, col, endcol-1, attrs);
}
void ugrid_goto(UGrid *grid, int row, int col)
@@ -99,7 +99,8 @@ void ugrid_scroll(UGrid *grid, int count, int *clear_top, int *clear_bot)
*clear_bot = stop;
*clear_top = stop + count + 1;
}
- clear_region(grid, *clear_top, *clear_bot, grid->left, grid->right);
+ clear_region(grid, *clear_top, *clear_bot, grid->left, grid->right,
+ HLATTRS_INIT);
}
UCell *ugrid_put(UGrid *grid, uint8_t *text, size_t size)
@@ -117,13 +118,13 @@ UCell *ugrid_put(UGrid *grid, uint8_t *text, size_t size)
return cell;
}
-static void clear_region(UGrid *grid, int top, int bot, int left, int right)
+static void clear_region(UGrid *grid, int top, int bot, int left, int right,
+ HlAttrs attrs)
{
- HlAttrs clear_attrs = grid->clear_attrs;
UGRID_FOREACH_CELL(grid, top, bot, left, right, {
cell->data[0] = ' ';
cell->data[1] = 0;
- cell->attrs = clear_attrs;
+ cell->attrs = attrs;
});
}
diff --git a/src/nvim/ugrid.h b/src/nvim/ugrid.h
index 035074846e..04e027bd46 100644
--- a/src/nvim/ugrid.h
+++ b/src/nvim/ugrid.h
@@ -7,7 +7,7 @@
typedef struct ucell UCell;
typedef struct ugrid UGrid;
-#define CELLBYTES (4 * (MAX_MCO+1))
+#define CELLBYTES (sizeof(schar_T))
struct ucell {
char data[CELLBYTES + 1];
@@ -17,7 +17,6 @@ struct ucell {
struct ugrid {
int top, bot, left, right;
int row, col;
- HlAttrs clear_attrs;
int width, height;
HlAttrs attrs;
UCell **cells;
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index 8419951079..ef68b804ba 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -52,14 +52,10 @@ static UI *uis[MAX_UI_COUNT];
static bool ui_ext[kUIExtCount] = { 0 };
static size_t ui_count = 0;
static int row = 0, col = 0;
-static struct {
- int top, bot, left, right;
-} sr;
-static int current_attr_code = -1;
static bool pending_cursor_update = false;
static int busy = 0;
-static int height, width;
-static int old_mode_idx = -1;
+static int mode_idx = SHAPE_IDX_N;
+static bool pending_mode_update = false;
#if MIN_LOG_LEVEL > DEBUG_LOG_LEVEL
# define UI_LOG(funname, ...)
@@ -89,7 +85,6 @@ static char uilog_last_event[1024] = { 0 };
#ifdef _MSC_VER
# define UI_CALL(funname, ...) \
do { \
- flush_cursor_update(); \
UI_LOG(funname, 0); \
for (size_t i = 0; i < ui_count; i++) { \
UI *ui = uis[i]; \
@@ -99,7 +94,6 @@ static char uilog_last_event[1024] = { 0 };
#else
# define UI_CALL(...) \
do { \
- flush_cursor_update(); \
UI_LOG(__VA_ARGS__, 0); \
for (size_t i = 0; i < ui_count; i++) { \
UI *ui = uis[i]; \
@@ -108,8 +102,8 @@ static char uilog_last_event[1024] = { 0 };
} while (0)
#endif
#define CNT(...) SELECT_NTH(__VA_ARGS__, MORE, MORE, MORE, \
- MORE, MORE, ZERO, ignore)
-#define SELECT_NTH(a1, a2, a3, a4, a5, a6, a7, ...) a7
+ MORE, MORE, MORE, MORE, MORE, ZERO, ignore)
+#define SELECT_NTH(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, ...) a10
#define UI_CALL_HELPER(c, ...) UI_CALL_HELPER2(c, __VA_ARGS__)
// Resolves to UI_CALL_MORE or UI_CALL_ZERO.
#define UI_CALL_HELPER2(c, ...) UI_CALL_##c(__VA_ARGS__)
@@ -199,6 +193,9 @@ void ui_refresh(void)
}
row = col = 0;
+ pending_cursor_update = true;
+
+ ui_default_colors_set();
int save_p_lz = p_lz;
p_lz = false; // convince redrawing() to return true ...
@@ -207,13 +204,14 @@ void ui_refresh(void)
for (UIExtension i = 0; (int)i < kUIExtCount; i++) {
ui_ext[i] = ext_widgets[i];
- ui_call_option_set(cstr_as_string((char *)ui_ext_names[i]),
- BOOLEAN_OBJ(ext_widgets[i]));
+ if (i < kUIGlobalCount) {
+ ui_call_option_set(cstr_as_string((char *)ui_ext_names[i]),
+ BOOLEAN_OBJ(ext_widgets[i]));
+ }
}
ui_mode_info_set();
- old_mode_idx = -1;
+ pending_mode_update = true;
ui_cursor_shape();
- current_attr_code = -1;
}
static void ui_refresh_event(void **argv)
@@ -226,25 +224,15 @@ void ui_schedule_refresh(void)
loop_schedule(&main_loop, event_create(ui_refresh_event, 0));
}
-void ui_resize(int new_width, int new_height)
+void ui_resize(int width, int height)
{
- width = new_width;
- height = new_height;
+ ui_call_grid_resize(1, width, height);
+}
- // TODO(bfredl): update default colors when they changed, NOT on resize.
+void ui_default_colors_set(void)
+{
ui_call_default_colors_set(normal_fg, normal_bg, normal_sp,
cterm_normal_fg_color, cterm_normal_bg_color);
-
- // Deprecated:
- UI_CALL(update_fg, (ui->rgb ? normal_fg : cterm_normal_fg_color - 1));
- UI_CALL(update_bg, (ui->rgb ? normal_bg : cterm_normal_bg_color - 1));
- UI_CALL(update_sp, (ui->rgb ? normal_sp : -1));
-
- sr.top = 0;
- sr.bot = height - 1;
- sr.left = 0;
- sr.right = width - 1;
- ui_call_resize(width, height);
}
void ui_busy_start(void)
@@ -269,6 +257,18 @@ void ui_attach_impl(UI *ui)
uis[ui_count++] = ui;
ui_refresh_options();
+
+ for (UIExtension i = kUIGlobalCount; (int)i < kUIExtCount; i++) {
+ ui_set_ext_option(ui, i, ui->ui_ext[i]);
+ }
+
+ bool sent = false;
+ if (ui->ui_ext[kUIHlState]) {
+ sent = highlight_use_hlstate();
+ }
+ if (!sent) {
+ ui_send_all_hls(ui);
+ }
ui_refresh();
}
@@ -302,97 +302,34 @@ void ui_detach_impl(UI *ui)
}
}
-// Set scrolling region for window 'wp'.
-// The region starts 'off' lines from the start of the window.
-// Also set the vertical scroll region for a vertically split window. Always
-// the full width of the window, excluding the vertical separator.
-void ui_set_scroll_region(win_T *wp, int off)
-{
- sr.top = wp->w_winrow + off;
- sr.bot = wp->w_winrow + wp->w_height - 1;
-
- if (wp->w_width != Columns) {
- sr.left = wp->w_wincol;
- sr.right = wp->w_wincol + wp->w_width - 1;
- }
-
- ui_call_set_scroll_region(sr.top, sr.bot, sr.left, sr.right);
-}
-
-// Reset scrolling region to the whole screen.
-void ui_reset_scroll_region(void)
-{
- sr.top = 0;
- sr.bot = (int)Rows - 1;
- sr.left = 0;
- sr.right = (int)Columns - 1;
- ui_call_set_scroll_region(sr.top, sr.bot, sr.left, sr.right);
-}
-
-void ui_set_highlight(int attr_code)
+void ui_set_ext_option(UI *ui, UIExtension ext, bool active)
{
- if (current_attr_code == attr_code) {
+ if (ext < kUIGlobalCount) {
+ ui_refresh();
return;
}
- current_attr_code = attr_code;
-
- HlAttrs attrs = HLATTRS_INIT;
-
- if (attr_code != 0) {
- HlAttrs *aep = syn_attr2entry(attr_code);
- if (aep) {
- attrs = *aep;
- }
+ if (ui->option_set) {
+ ui->option_set(ui, cstr_as_string((char *)ui_ext_names[ext]),
+ BOOLEAN_OBJ(active));
}
-
- UI_CALL(highlight_set, attrs);
-}
-
-void ui_clear_highlight(void)
-{
- ui_set_highlight(0);
}
-void ui_puts(uint8_t *str)
+void ui_line(int row, int startcol, int endcol, int clearcol, int clearattr)
{
- uint8_t *p = str;
- uint8_t c;
-
- while ((c = *p)) {
- if (c < 0x20) {
- abort();
- }
-
- size_t clen = (size_t)mb_ptr2len(p);
- ui_call_put((String){ .data = (char *)p, .size = clen });
- col++;
- if (mb_ptr2cells(p) > 1) {
- // double cell character, blank the next cell
- ui_call_put((String)STRING_INIT);
- col++;
- }
- if (utf_ambiguous_width(utf_ptr2char(p))) {
- pending_cursor_update = true;
- }
- if (col >= width) {
- ui_linefeed();
- }
- p += clen;
-
- if (p_wd) { // 'writedelay': flush & delay each time.
- ui_flush();
- uint64_t wd = (uint64_t)labs(p_wd);
- os_microdelay(wd * 1000u, true);
- }
+ size_t off = LineOffset[row]+(size_t)startcol;
+ UI_CALL(raw_line, 1, row, startcol, endcol, clearcol, clearattr,
+ (const schar_T *)ScreenLines+off, (const sattr_T *)ScreenAttrs+off);
+ if (p_wd) { // 'writedelay': flush & delay each time.
+ int old_row = row, old_col = col;
+ // If'writedelay is active, we set the cursor to highlight what was drawn
+ ui_cursor_goto(row, MIN(clearcol, (int)Columns-1));
+ ui_flush();
+ uint64_t wd = (uint64_t)labs(p_wd);
+ os_microdelay(wd * 1000u, true);
+ ui_cursor_goto(old_row, old_col);
}
}
-void ui_putc(uint8_t c)
-{
- uint8_t buf[2] = {c, 0};
- ui_puts(buf);
-}
-
void ui_cursor_goto(int new_row, int new_col)
{
if (new_row == row && new_col == col) {
@@ -450,30 +387,19 @@ int ui_current_col(void)
void ui_flush(void)
{
cmdline_ui_flush();
- ui_call_flush();
-}
-
-
-void ui_linefeed(void)
-{
- int new_col = 0;
- int new_row = row;
- if (new_row < sr.bot) {
- new_row++;
- } else {
- ui_call_scroll(1);
- }
- ui_cursor_goto(new_row, new_col);
-}
-
-static void flush_cursor_update(void)
-{
if (pending_cursor_update) {
+ ui_call_grid_cursor_goto(1, row, col);
pending_cursor_update = false;
- ui_call_cursor_goto(row, col);
}
+ if (pending_mode_update) {
+ char *full_name = shape_table[mode_idx].full_name;
+ ui_call_mode_change(cstr_as_string(full_name), mode_idx);
+ pending_mode_update = false;
+ }
+ ui_call_flush();
}
+
/// Check if current mode has changed.
/// May update the shape of the cursor.
void ui_cursor_shape(void)
@@ -481,12 +407,11 @@ void ui_cursor_shape(void)
if (!full_screen) {
return;
}
- int mode_idx = cursor_get_mode_idx();
+ int new_mode_idx = cursor_get_mode_idx();
- if (old_mode_idx != mode_idx) {
- old_mode_idx = mode_idx;
- char *full_name = shape_table[mode_idx].full_name;
- ui_call_mode_change(cstr_as_string(full_name), mode_idx);
+ if (new_mode_idx != mode_idx) {
+ mode_idx = new_mode_idx;
+ pending_mode_update = true;
}
conceal_check_cursur_line();
}
diff --git a/src/nvim/ui.h b/src/nvim/ui.h
index 9e98ff9f15..584d8a77c6 100644
--- a/src/nvim/ui.h
+++ b/src/nvim/ui.h
@@ -5,13 +5,18 @@
#include <stdbool.h>
#include <stdint.h>
-#include "api/private/defs.h"
+#include "nvim/globals.h"
+#include "nvim/api/private/defs.h"
+#include "nvim/highlight_defs.h"
typedef enum {
kUICmdline = 0,
kUIPopupmenu,
kUITabline,
kUIWildmenu,
+#define kUIGlobalCount (kUIWildmenu+1)
+ kUINewgrid,
+ kUIHlState,
kUIExtCount,
} UIExtension;
@@ -19,7 +24,9 @@ EXTERN const char *ui_ext_names[] INIT(= {
"ext_cmdline",
"ext_popupmenu",
"ext_tabline",
- "ext_wildmenu"
+ "ext_wildmenu",
+ "ext_newgrid",
+ "ext_hlstate",
});
@@ -30,9 +37,17 @@ struct ui_t {
bool ui_ext[kUIExtCount]; ///< Externalized widgets
int width, height;
void *data;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ui_events.generated.h"
#endif
+
+ // For perfomance and simplicity, we use the dense screen representation
+ // in the bridge and the TUI. The remote_ui module will translate this
+ // in to the public grid_line format.
+ void (*raw_line)(UI *ui, Integer grid, Integer row, Integer startcol,
+ Integer endcol, Integer clearcol, Integer clearattr,
+ const schar_T *chunk, const sattr_T *attrs);
void (*event)(UI *ui, char *name, Array args, bool *args_consumed);
void (*stop)(UI *ui);
void (*inspect)(UI *ui, Dictionary *info);
diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c
index 56db124a46..a96a24bde7 100644
--- a/src/nvim/ui_bridge.c
+++ b/src/nvim/ui_bridge.c
@@ -42,10 +42,9 @@ UI *ui_bridge_attach(UI *ui, ui_main_fn ui_main, event_scheduler scheduler)
rv->ui = ui;
rv->bridge.rgb = ui->rgb;
rv->bridge.stop = ui_bridge_stop;
- rv->bridge.resize = ui_bridge_resize;
- rv->bridge.clear = ui_bridge_clear;
- rv->bridge.eol_clear = ui_bridge_eol_clear;
- rv->bridge.cursor_goto = ui_bridge_cursor_goto;
+ rv->bridge.grid_resize = ui_bridge_grid_resize;
+ rv->bridge.grid_clear = ui_bridge_grid_clear;
+ rv->bridge.grid_cursor_goto = ui_bridge_grid_cursor_goto;
rv->bridge.mode_info_set = ui_bridge_mode_info_set;
rv->bridge.update_menu = ui_bridge_update_menu;
rv->bridge.busy_start = ui_bridge_busy_start;
@@ -53,10 +52,8 @@ UI *ui_bridge_attach(UI *ui, ui_main_fn ui_main, event_scheduler scheduler)
rv->bridge.mouse_on = ui_bridge_mouse_on;
rv->bridge.mouse_off = ui_bridge_mouse_off;
rv->bridge.mode_change = ui_bridge_mode_change;
- rv->bridge.set_scroll_region = ui_bridge_set_scroll_region;
- rv->bridge.scroll = ui_bridge_scroll;
- rv->bridge.highlight_set = ui_bridge_highlight_set;
- rv->bridge.put = ui_bridge_put;
+ rv->bridge.grid_scroll = ui_bridge_grid_scroll;
+ rv->bridge.hl_attr_define = ui_bridge_hl_attr_define;
rv->bridge.bell = ui_bridge_bell;
rv->bridge.visual_bell = ui_bridge_visual_bell;
rv->bridge.default_colors_set = ui_bridge_default_colors_set;
@@ -65,6 +62,7 @@ UI *ui_bridge_attach(UI *ui, ui_main_fn ui_main, event_scheduler scheduler)
rv->bridge.set_title = ui_bridge_set_title;
rv->bridge.set_icon = ui_bridge_set_icon;
rv->bridge.option_set = ui_bridge_option_set;
+ rv->bridge.raw_line = ui_bridge_raw_line;
rv->scheduler = scheduler;
for (UIExtension i = 0; (int)i < kUIExtCount; i++) {
@@ -133,19 +131,45 @@ static void ui_bridge_stop_event(void **argv)
ui->stop(ui);
}
-static void ui_bridge_highlight_set(UI *b, HlAttrs attrs)
+static void ui_bridge_hl_attr_define(UI *ui, Integer id, HlAttrs attrs,
+ HlAttrs cterm_attrs, Array info)
{
HlAttrs *a = xmalloc(sizeof(HlAttrs));
*a = attrs;
- UI_BRIDGE_CALL(b, highlight_set, 2, b, a);
+ UI_BRIDGE_CALL(ui, hl_attr_define, 3, ui, INT2PTR(id), a);
}
-static void ui_bridge_highlight_set_event(void **argv)
+static void ui_bridge_hl_attr_define_event(void **argv)
{
UI *ui = UI(argv[0]);
- ui->highlight_set(ui, *((HlAttrs *)argv[1]));
- xfree(argv[1]);
+ Array info = ARRAY_DICT_INIT;
+ ui->hl_attr_define(ui, PTR2INT(argv[1]), *((HlAttrs *)argv[2]),
+ *((HlAttrs *)argv[2]), info);
+ xfree(argv[2]);
}
+static void ui_bridge_raw_line_event(void **argv)
+{
+ UI *ui = UI(argv[0]);
+ ui->raw_line(ui, PTR2INT(argv[1]), PTR2INT(argv[2]), PTR2INT(argv[3]),
+ PTR2INT(argv[4]), PTR2INT(argv[5]), PTR2INT(argv[6]),
+ argv[7], argv[8]);
+ xfree(argv[7]);
+ xfree(argv[8]);
+}
+static void ui_bridge_raw_line(UI *ui, Integer grid, Integer row,
+ Integer startcol, Integer endcol,
+ Integer clearcol, Integer clearattr,
+ const schar_T *chunk, const sattr_T *attrs)
+{
+ size_t ncol = (size_t)(endcol-startcol);
+ schar_T *c = xmemdup(chunk, ncol * sizeof(schar_T));
+ sattr_T *hl = xmemdup(attrs, ncol * sizeof(sattr_T));
+ UI_BRIDGE_CALL(ui, raw_line, 9, ui, INT2PTR(grid), INT2PTR(row),
+ INT2PTR(startcol), INT2PTR(endcol), INT2PTR(clearcol),
+ INT2PTR(clearattr), c, hl);
+}
+
+
static void ui_bridge_suspend(UI *b)
{
UIBridgeData *data = (UIBridgeData *)b;
diff --git a/test/functional/api/version_spec.lua b/test/functional/api/version_spec.lua
index 7bf54c0d1e..e0472977cc 100644
--- a/test/functional/api/version_spec.lua
+++ b/test/functional/api/version_spec.lua
@@ -156,6 +156,6 @@ describe("ui_options in metadata", function()
local api = helpers.call('api_info')
local options = api.ui_options
eq({'rgb', 'ext_cmdline', 'ext_popupmenu',
- 'ext_tabline', 'ext_wildmenu'}, options)
+ 'ext_tabline', 'ext_wildmenu', 'ext_newgrid', 'ext_hlstate'}, options)
end)
end)
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index e4b343c123..ae8a8488d4 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -1242,6 +1242,8 @@ describe('API', function()
ext_popupmenu = false,
ext_tabline = false,
ext_wildmenu = false,
+ ext_newgrid = screen._options.ext_newgrid or false,
+ ext_hlstate=false,
height = 4,
rgb = true,
width = 20,
@@ -1252,18 +1254,9 @@ describe('API', function()
screen:detach()
screen = Screen.new(44, 99)
screen:attach({ rgb = false })
- expected = {
- {
- chan = 1,
- ext_cmdline = false,
- ext_popupmenu = false,
- ext_tabline = false,
- ext_wildmenu = false,
- height = 99,
- rgb = false,
- width = 44,
- }
- }
+ expected[1].rgb = false
+ expected[1].width = 44
+ expected[1].height = 99
eq(expected, nvim("list_uis"))
end)
end)
diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua
index 5603224975..c0404ff463 100644
--- a/test/functional/terminal/tui_spec.lua
+++ b/test/functional/terminal/tui_spec.lua
@@ -207,7 +207,7 @@ describe('tui', function()
screen:set_default_attr_ids({
[1] = {reverse = true},
[2] = {foreground = 13, special = Screen.colors.Grey0},
- [3] = {special = Screen.colors.Grey0, bold = true, reverse = true},
+ [3] = {bold = true, reverse = true, special = Screen.colors.Grey0},
[4] = {bold = true},
[5] = {special = Screen.colors.Grey0, reverse = true, foreground = 4},
[6] = {foreground = 4, special = Screen.colors.Grey0},
@@ -257,11 +257,11 @@ describe('tui', function()
it('shows up in nvim_list_uis', function()
feed_data(':echo map(nvim_list_uis(), {k,v -> sort(items(v))})\013')
screen:expect([=[
- {5: }|
- [[['ext_cmdline', v:false], ['ext_popupmenu', v:fa|
- lse], ['ext_tabline', v:false], ['ext_wildmenu', v|
- :false], ['height', 6], ['rgb', v:false], ['width'|
- , 50]]] |
+ [[['ext_cmdline', v:false], ['ext_hlstate', v:fals|
+ e], ['ext_newgrid', v:true], ['ext_popupmenu', v:f|
+ alse], ['ext_tabline', v:false], ['ext_wildmenu', |
+ v:false], ['height', 6], ['rgb', v:false], ['width|
+ ', 50]]] |
{10:Press ENTER or type command to continue}{1: } |
{3:-- TERMINAL --} |
]=])
diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua
index 5ce49822e5..b2fc008dba 100644
--- a/test/functional/ui/cmdline_spec.lua
+++ b/test/functional/ui/cmdline_spec.lua
@@ -29,6 +29,9 @@ describe('external cmdline', function()
if name == "cmdline_show" then
local content, pos, firstc, prompt, indent, level = unpack(data)
ok(level > 0)
+ for _,item in ipairs(content) do
+ item[1] = screen:get_hl(item[1])
+ end
cmdline[level] = {content=content, pos=pos, firstc=firstc,
prompt=prompt, indent=indent}
last_level = level
@@ -87,6 +90,7 @@ describe('external cmdline', function()
|
]], nil, nil, function()
eq(1, last_level)
+ --print(require('inspect')(cmdline))
eq({{
content = { { {}, "" } },
firstc = ":",
@@ -168,10 +172,10 @@ describe('external cmdline', function()
it('from normal mode', function()
feed(':')
screen:expect([[
- |
+ ^ |
{1:~ }|
{1:~ }|
- {3:c^ }|
+ {3:c }|
|
]], nil, nil, function()
eq({{
@@ -351,11 +355,11 @@ describe('external cmdline', function()
-- redraw! forgets cursor position. Be OK with that, as UI should indicate
-- focus is at external cmdline anyway.
screen:expect([[
- |
+ ^ |
{1:~ }|
{1:~ }|
{1:~ }|
- ^ |
+ |
]], nil, nil, function()
eq(expectation, cmdline)
end)
@@ -363,11 +367,11 @@ describe('external cmdline', function()
feed('<cr>')
screen:expect([[
- |
+ ^ |
{1:~ }|
{1:~ }|
{1:~ }|
- ^ |
+ |
]], nil, nil, function()
eq({{
content = { { {}, "xx3" } },
@@ -424,11 +428,11 @@ describe('external cmdline', function()
block = {}
command("redraw!")
screen:expect([[
- |
+ ^ |
{1:~ }|
{1:~ }|
{1:~ }|
- ^ |
+ |
]], nil, nil, function()
eq({ { { {}, 'function Foo()'} },
{ { {}, ' line1'} } }, block)
@@ -528,9 +532,9 @@ describe('external cmdline', function()
screen:expect([[
|
{2:[No Name] }|
- {1::}make |
+ {1::}make^ |
{3:[Command Line] }|
- ^ |
+ |
]], nil, nil, function()
eq({nil, {
content = { { {}, "yank" } },
@@ -572,11 +576,11 @@ describe('external cmdline', function()
cmdline = {}
command("redraw!")
screen:expect([[
- |
+ ^ |
{1:~ }|
{1:~ }|
{1:~ }|
- ^ |
+ |
]], nil, nil, function()
eq({{
content = { { {}, "make" } },
diff --git a/test/functional/ui/options_spec.lua b/test/functional/ui/options_spec.lua
index 62b08c0967..322a94763f 100644
--- a/test/functional/ui/options_spec.lua
+++ b/test/functional/ui/options_spec.lua
@@ -30,10 +30,15 @@ describe('ui receives option updates', function()
ext_popupmenu=false,
ext_tabline=false,
ext_wildmenu=false,
+ ext_newgrid=false,
+ ext_hlstate=false,
}
it("for defaults", function()
screen:attach()
+ -- NB: UI test suite can be run in both "newgrid" and legacy grid mode.
+ -- In both cases check that the received value is the one requested.
+ defaults.ext_newgrid = screen._options.ext_newgrid or false
screen:expect(function()
eq(defaults, screen.options)
end)
@@ -41,6 +46,7 @@ describe('ui receives option updates', function()
it("when setting options", function()
screen:attach()
+ defaults.ext_newgrid = screen._options.ext_newgrid or false
local changed = {}
for k,v in pairs(defaults) do
changed[k] = v
@@ -89,6 +95,7 @@ describe('ui receives option updates', function()
end
screen:attach({ext_cmdline=true, ext_wildmenu=true})
+ defaults.ext_newgrid = screen._options.ext_newgrid or false
changed.ext_cmdline = true
changed.ext_wildmenu = true
screen:expect(function()
diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua
index 7607131e9b..d96aae1aa7 100644
--- a/test/functional/ui/screen.lua
+++ b/test/functional/ui/screen.lua
@@ -142,6 +142,8 @@ function Screen.new(width, height)
_default_attr_ignore = nil,
_mouse_enabled = true,
_attrs = {},
+ _attr_table = {[0]={{},{}}},
+ _clear_attrs = {},
_cursor = {
row = 1, col = 1
},
@@ -163,6 +165,11 @@ function Screen:attach(options)
if options == nil then
options = {rgb=true}
end
+ if options.ext_newgrid == nil then
+ options.ext_newgrid = true
+ end
+ self._options = options
+ self._clear_attrs = (options.ext_newgrid and {{},{}}) or {}
uimeths.attach(self._width, self._height, options)
end
@@ -176,6 +183,7 @@ end
function Screen:set_option(option, value)
uimeths.set_option(option, value)
+ self._options[option] = value
end
-- Asserts that `expected` eventually matches the screen state.
@@ -339,7 +347,7 @@ function Screen:_handle_resize(width, height)
for _ = 1, height do
local cols = {}
for _ = 1, width do
- table.insert(cols, {text = ' ', attrs = {}})
+ table.insert(cols, {text = ' ', attrs = self._clear_attrs})
end
table.insert(rows, cols)
end
@@ -353,14 +361,24 @@ function Screen:_handle_resize(width, height)
}
end
+function Screen:_handle_grid_resize(grid, width, height)
+ assert(grid == 1)
+ self:_handle_resize(width, height)
+end
+
+
function Screen:_handle_mode_info_set(cursor_style_enabled, mode_info)
self._cursor_style_enabled = cursor_style_enabled
self._mode_info = mode_info
end
function Screen:_handle_clear()
- self:_clear_block(self._scroll_region.top, self._scroll_region.bot,
- self._scroll_region.left, self._scroll_region.right)
+ self:_clear_block(1, self._height, 1, self._width)
+end
+
+function Screen:_handle_grid_clear(grid)
+ assert(grid == 1)
+ self:_handle_clear()
end
function Screen:_handle_eol_clear()
@@ -373,6 +391,12 @@ function Screen:_handle_cursor_goto(row, col)
self._cursor.col = col + 1
end
+function Screen:_handle_grid_cursor_goto(grid, row, col)
+ assert(grid == 1)
+ self._cursor.row = row + 1
+ self._cursor.col = col + 1
+end
+
function Screen:_handle_busy_start()
self._busy = true
end
@@ -434,6 +458,27 @@ function Screen:_handle_scroll(count)
end
end
+function Screen:_handle_grid_scroll(grid, top, bot, left, right, rows, cols)
+ assert(grid == 1)
+ assert(cols == 0)
+ -- TODO: if we truly believe we should translate the other way
+ self:_handle_set_scroll_region(top,bot-1,left,right-1)
+ self:_handle_scroll(rows)
+end
+
+function Screen:_handle_hl_attr_define(id, rgb_attrs, cterm_attrs, info)
+ self._attr_table[id] = {rgb_attrs, cterm_attrs}
+ self._new_attrs = true
+end
+
+function Screen:get_hl(val)
+ if self._options.ext_newgrid then
+ return self._attr_table[val][1]
+ else
+ return val
+ end
+end
+
function Screen:_handle_highlight_set(attrs)
self._attrs = attrs
end
@@ -445,6 +490,25 @@ function Screen:_handle_put(str)
self._cursor.col = self._cursor.col + 1
end
+function Screen:_handle_grid_line(grid, row, col, items)
+ assert(grid == 1)
+ local line = self._rows[row+1]
+ local colpos = col+1
+ local hl = self._clear_attrs
+ for _,item in ipairs(items) do
+ local text, hlid, count = unpack(item)
+ if hlid ~= nil then
+ hl = self._attr_table[hlid]
+ end
+ for _ = 1, (count or 1) do
+ local cell = line[colpos]
+ cell.text = text
+ cell.attrs = hl
+ colpos = colpos+1
+ end
+ end
+end
+
function Screen:_handle_bell()
self.bell = true
end
@@ -498,7 +562,7 @@ function Screen:_clear_row_section(rownum, startcol, stopcol)
local row = self._rows[rownum]
for i = startcol, stopcol do
row[i].text = ' '
- row[i].attrs = {}
+ row[i].attrs = self._clear_attrs
end
end
@@ -506,7 +570,11 @@ function Screen:_row_repr(row, attr_ids, attr_ignore)
local rv = {}
local current_attr_id
for i = 1, self._width do
- local attr_id = self:_get_attr_id(attr_ids, attr_ignore, row[i].attrs)
+ local attrs = row[i].attrs
+ if self._options.ext_newgrid then
+ attrs = attrs[(self._options.rgb and 1) or 2]
+ end
+ local attr_id = self:_get_attr_id(attr_ids, attr_ignore, attrs, row[i].hl_id)
if current_attr_id and attr_id ~= current_attr_id then
-- close current attribute bracket, add it before any whitespace
-- up to the current cell
@@ -647,6 +715,7 @@ function Screen:_get_attr_id(attr_ids, ignore, attrs)
if not attr_ids then
return
end
+
for id, a in pairs(attr_ids) do
if self:_equal_attrs(a, attrs) then
return id
diff --git a/test/functional/ui/screen_basic_spec.lua b/test/functional/ui/screen_basic_spec.lua
index 6f04cde4d4..75a2d4978d 100644
--- a/test/functional/ui/screen_basic_spec.lua
+++ b/test/functional/ui/screen_basic_spec.lua
@@ -48,13 +48,13 @@ describe('screen', function()
end)
end)
-describe('Screen', function()
+local function screen_tests(newgrid)
local screen
before_each(function()
clear()
screen = Screen.new()
- screen:attach()
+ screen:attach({rgb=true,ext_newgrid=newgrid})
screen:set_default_attr_ids( {
[0] = {bold=true, foreground=255},
[1] = {bold=true, reverse=true},
@@ -741,4 +741,12 @@ describe('Screen', function()
|
]])
end)
+end
+
+describe("Screen (char-based)", function()
+ screen_tests(false)
+end)
+
+describe("Screen (line-based)", function()
+ screen_tests(true)
end)