mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
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.
This commit is contained in:
parent
2134396074
commit
1adb01c120
@ -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) {
|
||||
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,
|
||||
"popupmenu_external must be a Boolean");
|
||||
"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_highlight_set(UI *ui, HlAttrs attrs)
|
||||
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_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
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -132,6 +132,7 @@ for i = 1, #events do
|
||||
end
|
||||
end
|
||||
|
||||
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')
|
||||
@ -145,6 +146,7 @@ for i = 1, #events do
|
||||
call_output:write(";\n")
|
||||
end
|
||||
call_output:write("}\n\n")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
///
|
||||
|
@ -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");
|
||||
|
@ -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 */
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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++;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
// 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;
|
||||
}
|
||||
if (col < clear_width) {
|
||||
screen_fill(row, row + 1, col + coloff, clear_width + coloff, ' ', ' ',
|
||||
bg_attr);
|
||||
off_to += clear_width - col;
|
||||
col = clear_width;
|
||||
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,13 +5404,31 @@ 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare for 'hlsearch' highlighting.
|
||||
*/
|
||||
@ -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
|
||||
*
|
||||
|
@ -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
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
TUIData *data = ui->data;
|
||||
ugrid_set_scroll_region(&data->grid, (int)top, (int)bot,
|
||||
(int)left, (int)right);
|
||||
data->scroll_region_is_full_screen =
|
||||
left == 0 && right == ui->width - 1
|
||||
&& top == 0 && bot == ui->height - 1;
|
||||
}
|
||||
|
||||
static void tui_scroll(UI *ui, Integer count)
|
||||
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 *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
|
||||
&& top == 0 && bot == ui->height;
|
||||
|
||||
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;
|
||||
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
183
src/nvim/ui.c
183
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];
|
||||
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,96 +302,33 @@ 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)
|
||||
void ui_set_ext_option(UI *ui, UIExtension ext, bool active)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
void ui_line(int row, int startcol, int endcol, int clearcol, int clearattr)
|
||||
{
|
||||
ui_set_highlight(0);
|
||||
}
|
||||
|
||||
void ui_puts(uint8_t *str)
|
||||
{
|
||||
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;
|
||||
|
||||
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)
|
||||
{
|
||||
@ -450,30 +387,19 @@ int ui_current_col(void)
|
||||
void ui_flush(void)
|
||||
{
|
||||
cmdline_ui_flush();
|
||||
if (pending_cursor_update) {
|
||||
ui_call_grid_cursor_goto(1, row, col);
|
||||
pending_cursor_update = false;
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
pending_cursor_update = false;
|
||||
ui_call_cursor_goto(row, col);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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();
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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 --} |
|
||||
]=])
|
||||
|
@ -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:~ }|
|
||||
^ |
|
||||
{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:~ }|
|
||||
^ |
|
||||
{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:~ }|
|
||||
^ |
|
||||
{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:~ }|
|
||||
^ |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
|
|
||||
]], nil, nil, function()
|
||||
eq({{
|
||||
content = { { {}, "make" } },
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user