Merge pull request #8221 from bfredl/hlstate
UI grid protocol revision: line based updates and semantic highlights
This commit is contained in:
commit
94841e5eae
|
@ -21,7 +21,7 @@ After connecting to Nvim (usually a spawned, embedded instance) use the
|
|||
|nvim_ui_attach| API method to tell Nvim that your program wants to draw the
|
||||
Nvim screen grid with a size of width × height cells. `options` must be
|
||||
a dictionary with these (optional) keys:
|
||||
`rgb` Decides the color format.
|
||||
`rgb` Decides the color format. |ui-rgb|
|
||||
Set true (default) for 24-bit RGB colors.
|
||||
Set false for terminal colors (max of 256).
|
||||
*ui-ext-options*
|
||||
|
@ -29,6 +29,8 @@ a dictionary with these (optional) keys:
|
|||
`ext_tabline` Externalize the tabline. |ui-tabline|
|
||||
`ext_cmdline` Externalize the cmdline. |ui-cmdline|
|
||||
`ext_wildmenu` Externalize the wildmenu. |ui-ext-wildmenu|
|
||||
`ext_newgrid` Use new revision of the grid events. |ui-newgrid|
|
||||
`ext_hlstate` Use detailed highlight state. |ui-hlstate|
|
||||
|
||||
Specifying a non-existent option is an error. UIs can check the |api-metadata|
|
||||
`ui_options` key for supported options. Additionally Nvim (currently) requires
|
||||
|
@ -49,12 +51,18 @@ Events must be handled in-order. The user should only see the updated screen
|
|||
state after all events in the same "redraw" batch are processed (not any
|
||||
intermediate state after processing only part of the array).
|
||||
|
||||
Nvim sends |ui-global| and |ui-grid| events unconditionally; these suffice to
|
||||
implement a terminal-like layout.
|
||||
By default, Nvim sends |ui-global| and |ui-grid-old| events; these suffice to
|
||||
implement a terminal-like interface. However there are two revisions of the
|
||||
grid part of the protocol. The newer revision |ui-newgrid|, enabled by
|
||||
`ext_newgrid` option, has some improvements, such as a more efficient
|
||||
representation of highlighted text, simplified events and room for futher
|
||||
enhancements that will use multiple grids. The older revision is available and
|
||||
used by default only for backwards compatibility reasons. New UIs are strongly
|
||||
recommended to use |ui-newgrid|, as further protocol extensions will require it.
|
||||
|
||||
Nvim optionally sends screen elements "semantically" as structured events
|
||||
instead of raw grid-lines, controlled by |ui-ext-options|. The UI must present
|
||||
those elements itself; Nvim will not draw those elements on the |ui-grid|.
|
||||
those elements itself; Nvim will not draw those elements on the grid.
|
||||
|
||||
Future versions of Nvim may add new update kinds and may append new parameters
|
||||
to existing update kinds. Clients must be prepared to ignore such extensions,
|
||||
|
@ -149,7 +157,147 @@ Global Events *ui-global*
|
|||
Notify the user with an audible or visual bell, respectively.
|
||||
|
||||
==============================================================================
|
||||
Grid Events *ui-grid*
|
||||
Grid Events (new revision) *ui-newgrid*
|
||||
|
||||
These events are used if `ext_newgrid` option is set (recommended for all new
|
||||
UIs).
|
||||
|
||||
Most of these events take a `grid` index as first parameter. Grid 1 is the
|
||||
global grid used by default for the entire editor screen state. Grids other
|
||||
than that will be defined by future extensions. Just activating the `ext_newgrid`
|
||||
option by itself will never cause any additional grids to be created.
|
||||
|
||||
Highlight attribute groups are predefined. UIs should maintain a table to map
|
||||
numerical highlight `id`:s to the actual attributes.
|
||||
|
||||
["grid_resize", grid, width, height]
|
||||
Resize a `grid`. If `grid` wasn't seen by the client before, a new grid is
|
||||
being created with this size.
|
||||
|
||||
["default_colors_set", rgb_fg, rgb_bg, rgb_sp, cterm_fg, cterm_bg]
|
||||
The first three arguments set the default foreground, background and
|
||||
special colors respectively. `cterm_fg` and `cterm_bg` specifies the
|
||||
default color codes to use in a 256-color terminal.
|
||||
|
||||
Note: unlike the corresponding events in the first revision, the
|
||||
screen is not always cleared after sending this event. The GUI has to
|
||||
repaint the screen with changed background color itself.
|
||||
|
||||
*ui-event-hl_attr_define*
|
||||
["hl_attr_define", id, rgb_attr, cterm_attr, info]
|
||||
Add a highlight with `id` to the highlight table, with the
|
||||
attributes specified by the `rgb_attr` and `cterm_attr` dicts, with the
|
||||
following (all optional) keys.
|
||||
|
||||
`foreground`: foreground color.
|
||||
`background`: background color.
|
||||
`special`: color to use for underline and undercurl, when present.
|
||||
`reverse`: reverse video. Foreground and background colors are
|
||||
switched.
|
||||
`italic`: italic text.
|
||||
`bold`: bold text.
|
||||
`underline`: underlined text. The line has `special` color.
|
||||
`undercurl`: undercurled text. The curl has `special` color.
|
||||
|
||||
For absent color keys the default color should be used. Don't store
|
||||
the default value in the table, rather a sentinel value, so that
|
||||
a changed default color will take effect.
|
||||
All boolean keys default to false, and will only be sent when they
|
||||
are true.
|
||||
|
||||
Highlights are always transmitted both for both the rgb format and as
|
||||
terminal 256-color codes, as the `rgb_attr` and `cterm_attr` parameters
|
||||
respectively. The |ui-rgb| option has no effect effect anymore.
|
||||
Most external UIs will only need to store and use the `rgb_attr`
|
||||
attributes.
|
||||
|
||||
`id` 0 will always be used for the default highlight with colors defined
|
||||
by `default_colors_set` and no styles applied.
|
||||
|
||||
Note: `id`:s can be reused if Nvim's internal highlight table is full.
|
||||
In this case, Nvim will always issue redraws of screen cells that are
|
||||
affected by redefined `id`:s, so UIs do not need to keep track of this
|
||||
themselves.
|
||||
|
||||
`info` is an empty array per default, and will be used by the
|
||||
|ui-hlstate| extension explaned below.
|
||||
|
||||
*ui-event-grid_line*
|
||||
["grid_line", grid, row, col_start, cells]
|
||||
Redraw a continous part of a `row` on a `grid`, starting at the column
|
||||
`col_start`. `cells` is an array of arrays each with 1 to 3 items:
|
||||
`[text(, hl_id, repeat)]` . `text` is the UTF-8 text that should be put in
|
||||
a cell, with the highlight `hl_id` defined by a previous `hl_attr_define`
|
||||
call. If `hl_id` is not present the most recently seen `hl_id` in
|
||||
the same call should be used (it is always sent for the first
|
||||
cell in the event). If `repeat` is present, the cell should be
|
||||
repeated `repeat` times (including the first time), otherwise just
|
||||
once.
|
||||
|
||||
The right cell of a double-width char will be represented as the empty
|
||||
string. Double-width chars never use `repeat`.
|
||||
|
||||
If the array of cell changes doesn't reach to the end of the line, the
|
||||
rest should remain unchanged. A whitespace char, repeated
|
||||
enough to cover the remaining line, will be sent when the rest of the
|
||||
line should be cleared.
|
||||
|
||||
["grid_clear", grid]
|
||||
Clear a `grid`.
|
||||
|
||||
["grid_destroy", grid]
|
||||
`grid` will not be used anymore and the UI can free any data associated
|
||||
with it.
|
||||
|
||||
["grid_cursor_goto", grid, row, column]
|
||||
Makes `grid` the current grid and `row, column` the cursor position on this
|
||||
grid. This event will be sent at most once in a `redraw` batch and
|
||||
indicates the visible cursor position.
|
||||
|
||||
["grid_scroll", grid, top, bot, left, right, rows, cols]
|
||||
Scroll the text in the a region of `grid`. The diagrams below illustrate
|
||||
what will happen, depending on the scroll direction. "=" is used to
|
||||
represent the SR(scroll region) boundaries and "-" the moved rectangles.
|
||||
Note that dst and src share a common region.
|
||||
|
||||
If `rows` is bigger than 0, move a rectangle in the SR up, this can
|
||||
happen while scrolling down.
|
||||
>
|
||||
+-------------------------+
|
||||
| (clipped above SR) | ^
|
||||
|=========================| dst_top |
|
||||
| dst (still in SR) | |
|
||||
+-------------------------+ src_top |
|
||||
| src (moved up) and dst | |
|
||||
|-------------------------| dst_bot |
|
||||
| src (cleared) | |
|
||||
+=========================+ src_bot
|
||||
<
|
||||
If `rows` is less than zero, move a rectangle in the SR down, this can
|
||||
happen while scrolling up.
|
||||
>
|
||||
+=========================+ src_top
|
||||
| src (cleared) | |
|
||||
|------------------------ | dst_top |
|
||||
| src (moved down) and dst| |
|
||||
+-------------------------+ src_bot |
|
||||
| dst (still in SR) | |
|
||||
|=========================| dst_bot |
|
||||
| (clipped below SR) | v
|
||||
+-------------------------+
|
||||
<
|
||||
`cols` is always zero in this version of Nvim, and reserved for future
|
||||
use.
|
||||
|
||||
Note when updating code from |ui-grid-old| events: ranges are
|
||||
end-exclusive, which is consistent with API conventions, but different
|
||||
from `set_scroll_region` which was end-inclusive.
|
||||
|
||||
==============================================================================
|
||||
Grid Events (first revision) *ui-grid-old*
|
||||
|
||||
This is an older representation of the screen grid, used if `ext_newgrid`
|
||||
option is not set.
|
||||
|
||||
["resize", width, height]
|
||||
The grid is resized to `width` and `height` cells.
|
||||
|
@ -173,7 +321,7 @@ Grid Events *ui-grid*
|
|||
Set the default foreground, background and special colors
|
||||
respectively.
|
||||
|
||||
*ui-event-highlight_set*
|
||||
*ui-event-highlight_set*
|
||||
["highlight_set", attrs]
|
||||
Set the attributes that the next text put on the grid will have.
|
||||
`attrs` is a dict with the keys below. Any absent key is reset
|
||||
|
@ -197,6 +345,9 @@ Grid Events *ui-grid*
|
|||
|
||||
["set_scroll_region", top, bot, left, right]
|
||||
Define the scroll region used by `scroll` below.
|
||||
|
||||
Note: ranges are end-inclusive, which is inconsistent with API
|
||||
conventions.
|
||||
|
||||
["scroll", count]
|
||||
Scroll the text in the scroll region. The diagrams below illustrate
|
||||
|
@ -230,6 +381,42 @@ Grid Events *ui-grid*
|
|||
| (clipped below SR) | v
|
||||
+-------------------------+
|
||||
<
|
||||
==============================================================================
|
||||
Detailed highlight state Extension *ui-hlstate*
|
||||
|
||||
|
||||
Only sent if `ext_hlstate` option is set in |ui-options|. `ext_hlstate` implies
|
||||
`ext_newgrid`.
|
||||
|
||||
By default, nvim will only describe grid cells using the final calculated
|
||||
higlight attributes, as described by the dict keys in |ui-event-highlight_set|.
|
||||
The `ext_hlstate` extension allows to the UI to also receive a semantic
|
||||
describtion of the higlights active in a cell. In this mode highlights will be
|
||||
predefined in a table, see |ui-event-hl_attr_define| and |ui-event-grid_line|.
|
||||
The `info` parameter in `hl_attr_define` will contain a semantic description
|
||||
of the highlights. As highlight groups can be combined, this will be an array
|
||||
of items, with the item with highest priority last. Each item is a dictionary
|
||||
with the following possible keys:
|
||||
|
||||
`kind`: always present. One of the following values:
|
||||
"ui": A builtin ui highlight.
|
||||
"syntax": highlight applied to a buffer by a syntax declaration or
|
||||
other runtime/plugin functionallity such as
|
||||
|nvim_buf_add_highlight|
|
||||
"terminal": highlight from a process running in a |terminal-emulator|.
|
||||
Contains no futher semantic information.
|
||||
`ui_name`: Name of the builtin highlight. See |highlight-groups| for
|
||||
possible values. Only present for "ui".
|
||||
`hi_name`: Name of the final |:highlight| group where the used
|
||||
attributes are defined.
|
||||
`id`: Unique numeric id representing this item.
|
||||
|
||||
Note: "ui" items will have both `ui_name` and `hi_name` present. These can
|
||||
differ, because the builtin group was linked to another group |hi-link| , or
|
||||
because 'winhighlight' was used. UI items will be transmitted, even if the
|
||||
highlight group is cleared, so `ui_name` can always be used to reliably identify
|
||||
screen elements, even if no attributes have been applied.
|
||||
|
||||
==============================================================================
|
||||
Popupmenu Events *ui-popupmenu*
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "nvim/api/private/helpers.h"
|
||||
#include "nvim/popupmnu.h"
|
||||
#include "nvim/cursor_shape.h"
|
||||
#include "nvim/highlight.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "api/ui.c.generated.h"
|
||||
|
@ -25,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;
|
||||
|
@ -70,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;
|
||||
|
@ -81,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;
|
||||
|
@ -102,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);
|
||||
|
@ -173,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) {
|
||||
|
@ -187,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;
|
||||
bool boolval = value.data.boolean;
|
||||
if (!init && i == kUINewgrid && boolval != ui->ui_ext[i]) {
|
||||
// There shouldn't be a reason for an UI to do this ever
|
||||
// so explicitly don't support this.
|
||||
api_set_error(error, kErrorTypeValidation,
|
||||
"ext_newgrid option cannot be changed");
|
||||
}
|
||||
ui->ui_ext[i] = boolval;
|
||||
if (!init) {
|
||||
ui_set_ext_option(ui, i, boolval);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (strequal(name.data, "popupmenu_external")) {
|
||||
// LEGACY: Deprecated option, use `ext_cmdline` instead.
|
||||
if (value.type != kObjectTypeBoolean) {
|
||||
api_set_error(error, kErrorTypeValidation,
|
||||
"popupmenu_external must be a Boolean");
|
||||
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;
|
||||
|
@ -242,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)
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "nvim/vim.h"
|
||||
#include "nvim/buffer.h"
|
||||
#include "nvim/file_search.h"
|
||||
#include "nvim/highlight.h"
|
||||
#include "nvim/window.h"
|
||||
#include "nvim/types.h"
|
||||
#include "nvim/ex_docmd.h"
|
||||
|
@ -1850,3 +1851,22 @@ Object nvim_get_proc(Integer pid, Error *err)
|
|||
#endif
|
||||
return rvobj;
|
||||
}
|
||||
|
||||
/// NB: if your UI doesn't use hlstate, this will not return hlstate first time
|
||||
Array nvim__inspect_cell(Integer row, Integer col, Error *err)
|
||||
{
|
||||
Array ret = ARRAY_DICT_INIT;
|
||||
if (row < 0 || row >= screen_Rows
|
||||
|| col < 0 || col >= screen_Columns) {
|
||||
return ret;
|
||||
}
|
||||
size_t off = LineOffset[(size_t)row] + (size_t)col;
|
||||
ADD(ret, STRING_OBJ(cstr_to_string((char *)ScreenLines[off])));
|
||||
int attr = ScreenAttrs[off];
|
||||
ADD(ret, DICTIONARY_OBJ(hl_get_attr_by_id(attr, true, err)));
|
||||
// will not work first time
|
||||
if (!highlight_use_hlstate()) {
|
||||
ADD(ret, ARRAY_OBJ(hl_inspect(attr)));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include "nvim/fold.h"
|
||||
#include "nvim/getchar.h"
|
||||
#include "nvim/hashtab.h"
|
||||
#include "nvim/highlight.h"
|
||||
#include "nvim/indent.h"
|
||||
#include "nvim/indent_c.h"
|
||||
#include "nvim/main.h"
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#define EVENT_HANDLER_MAX_ARGC 6
|
||||
#define EVENT_HANDLER_MAX_ARGC 9
|
||||
|
||||
typedef void (*argv_callback)(void **argv);
|
||||
typedef struct message {
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "nvim/fileio.h"
|
||||
#include "nvim/fold.h"
|
||||
#include "nvim/getchar.h"
|
||||
#include "nvim/highlight.h"
|
||||
#include "nvim/indent.h"
|
||||
#include "nvim/buffer_updates.h"
|
||||
#include "nvim/main.h"
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "nvim/fileio.h"
|
||||
#include "nvim/func_attr.h"
|
||||
#include "nvim/getchar.h"
|
||||
#include "nvim/highlight.h"
|
||||
#include "nvim/if_cscope.h"
|
||||
#include "nvim/indent.h"
|
||||
#include "nvim/main.h"
|
||||
|
@ -214,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"
|
||||
|
@ -2943,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_cterm_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));
|
||||
}
|
||||
|
@ -3032,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.
|
||||
|
@ -3500,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,19 +132,21 @@ for i = 1, #events do
|
|||
end
|
||||
end
|
||||
|
||||
call_output:write('void ui_call_'..ev.name)
|
||||
write_signature(call_output, ev, '')
|
||||
call_output:write('\n{\n')
|
||||
if ev.remote_only then
|
||||
write_arglist(call_output, ev, false)
|
||||
call_output:write(' UI_LOG('..ev.name..', 0);\n')
|
||||
call_output:write(' ui_event("'..ev.name..'", args);\n')
|
||||
else
|
||||
call_output:write(' UI_CALL')
|
||||
write_signature(call_output, ev, ev.name, true)
|
||||
call_output:write(";\n")
|
||||
if not (ev.remote_only and ev.remote_impl) then
|
||||
call_output:write('void ui_call_'..ev.name)
|
||||
write_signature(call_output, ev, '')
|
||||
call_output:write('\n{\n')
|
||||
if ev.remote_only then
|
||||
write_arglist(call_output, ev, false)
|
||||
call_output:write(' UI_LOG('..ev.name..', 0);\n')
|
||||
call_output:write(' ui_event("'..ev.name..'", args);\n')
|
||||
else
|
||||
call_output:write(' UI_CALL')
|
||||
write_signature(call_output, ev, ev.name, true)
|
||||
call_output:write(";\n")
|
||||
end
|
||||
call_output:write("}\n\n")
|
||||
end
|
||||
call_output:write("}\n\n")
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,418 @@
|
|||
// This is an open source non-commercial project. Dear PVS-Studio, please check
|
||||
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
|
||||
|
||||
// highlight.c: low level code for UI and syntax highlighting
|
||||
|
||||
#include "nvim/vim.h"
|
||||
#include "nvim/highlight.h"
|
||||
#include "nvim/highlight_defs.h"
|
||||
#include "nvim/map.h"
|
||||
#include "nvim/screen.h"
|
||||
#include "nvim/syntax.h"
|
||||
#include "nvim/ui.h"
|
||||
#include "nvim/api/private/defs.h"
|
||||
#include "nvim/api/private/helpers.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "highlight.c.generated.h"
|
||||
#endif
|
||||
|
||||
static bool hlstate_active = false;
|
||||
|
||||
static kvec_t(HlEntry) attr_entries = KV_INITIAL_VALUE;
|
||||
|
||||
static Map(HlEntry, int) *attr_entry_ids;
|
||||
static Map(int, int) *combine_attr_entries;
|
||||
|
||||
void highlight_init(void)
|
||||
{
|
||||
attr_entry_ids = map_new(HlEntry, int)();
|
||||
combine_attr_entries = map_new(int, int)();
|
||||
|
||||
// index 0 is no attribute, add dummy entry:
|
||||
kv_push(attr_entries, ((HlEntry){ .attr = HLATTRS_INIT, .kind = kHlUnknown,
|
||||
.id1 = 0, .id2 = 0 }));
|
||||
}
|
||||
|
||||
/// @return TRUE if hl table was reset
|
||||
bool highlight_use_hlstate(void)
|
||||
{
|
||||
if (hlstate_active) {
|
||||
return false;
|
||||
}
|
||||
hlstate_active = true;
|
||||
// hl tables must now be rebuilt.
|
||||
clear_hl_tables(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Return the attr number for a set of colors and font, and optionally
|
||||
/// a semantic description (see ext_hlstate documentation).
|
||||
/// Add a new entry to the attr_entries array if the combination is new.
|
||||
/// @return 0 for error.
|
||||
static int get_attr_entry(HlEntry entry)
|
||||
{
|
||||
if (!hlstate_active) {
|
||||
// This information will not be used, erase it and reduce the table size.
|
||||
entry.kind = kHlUnknown;
|
||||
entry.id1 = 0;
|
||||
entry.id2 = 0;
|
||||
}
|
||||
|
||||
int id = map_get(HlEntry, int)(attr_entry_ids, entry);
|
||||
if (id > 0) {
|
||||
return id;
|
||||
}
|
||||
|
||||
static bool recursive = false;
|
||||
if (kv_size(attr_entries) > MAX_TYPENR) {
|
||||
// Running out of attribute entries! remove all attributes, and
|
||||
// compute new ones for all groups.
|
||||
// When called recursively, we are really out of numbers.
|
||||
if (recursive) {
|
||||
EMSG(_("E424: Too many different highlighting attributes in use"));
|
||||
return 0;
|
||||
}
|
||||
recursive = true;
|
||||
|
||||
clear_hl_tables(true);
|
||||
|
||||
recursive = false;
|
||||
if (entry.kind == kHlCombine) {
|
||||
// This entry is now invalid, don't put it
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
id = (int)kv_size(attr_entries);
|
||||
kv_push(attr_entries, 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)
|
||||
{
|
||||
// TODO(bfredl): should we do this unconditionally
|
||||
if (at_en.cterm_fg_color != 0 || at_en.cterm_bg_color != 0
|
||||
|| at_en.rgb_fg_color != -1 || at_en.rgb_bg_color != -1
|
||||
|| at_en.rgb_sp_color != -1 || at_en.cterm_ae_attr != 0
|
||||
|| at_en.rgb_ae_attr != 0) {
|
||||
return get_attr_entry((HlEntry){ .attr = at_en, .kind = kHlSyntax,
|
||||
.id1 = idx, .id2 = 0 });
|
||||
} else {
|
||||
// If all the fields are cleared, clear the attr field back to default value
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get attribute code for a builtin highlight group.
|
||||
///
|
||||
/// The final syntax group could be modified by hi-link or 'winhighlight'.
|
||||
int hl_get_ui_attr(int idx, int final_id, bool optional)
|
||||
{
|
||||
HlAttrs attrs = HLATTRS_INIT;
|
||||
bool available = false;
|
||||
|
||||
int syn_attr = syn_id2attr(final_id);
|
||||
if (syn_attr != 0) {
|
||||
HlAttrs *aep = syn_attr2entry(syn_attr);
|
||||
if (aep) {
|
||||
attrs = *aep;
|
||||
available = true;
|
||||
}
|
||||
}
|
||||
if (optional && !available) {
|
||||
return 0;
|
||||
}
|
||||
return get_attr_entry((HlEntry){ .attr = attrs, .kind = kHlUI,
|
||||
.id1 = idx, .id2 = final_id });
|
||||
}
|
||||
|
||||
void update_window_hl(win_T *wp, bool invalid)
|
||||
{
|
||||
if (!wp->w_hl_needs_update && !invalid) {
|
||||
return;
|
||||
}
|
||||
wp->w_hl_needs_update = false;
|
||||
|
||||
// determine window specific background set in 'winhighlight'
|
||||
if (wp != curwin && wp->w_hl_ids[HLF_INACTIVE] > 0) {
|
||||
wp->w_hl_attr_normal = hl_get_ui_attr(HLF_INACTIVE,
|
||||
wp->w_hl_ids[HLF_INACTIVE], true);
|
||||
} else if (wp->w_hl_id_normal > 0) {
|
||||
wp->w_hl_attr_normal = hl_get_ui_attr(-1, wp->w_hl_id_normal, true);
|
||||
} else {
|
||||
wp->w_hl_attr_normal = 0;
|
||||
}
|
||||
if (wp != curwin) {
|
||||
wp->w_hl_attr_normal = hl_combine_attr(HL_ATTR(HLF_INACTIVE),
|
||||
wp->w_hl_attr_normal);
|
||||
}
|
||||
|
||||
for (int hlf = 0; hlf < (int)HLF_COUNT; hlf++) {
|
||||
int attr;
|
||||
if (wp->w_hl_ids[hlf] > 0) {
|
||||
attr = hl_get_ui_attr(hlf, wp->w_hl_ids[hlf], false);
|
||||
} else {
|
||||
attr = HL_ATTR(hlf);
|
||||
}
|
||||
wp->w_hl_attrs[hlf] = attr;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get attribute code for forwarded :terminal highlights.
|
||||
int get_term_attr_entry(HlAttrs *aep)
|
||||
{
|
||||
return get_attr_entry((HlEntry){ .attr= *aep, .kind = kHlTerminal,
|
||||
.id1 = 0, .id2 = 0 });
|
||||
}
|
||||
|
||||
/// Clear all highlight tables.
|
||||
void clear_hl_tables(bool reinit)
|
||||
{
|
||||
if (reinit) {
|
||||
kv_size(attr_entries) = 1;
|
||||
map_clear(HlEntry, int)(attr_entry_ids);
|
||||
map_clear(int, int)(combine_attr_entries);
|
||||
highlight_attr_set_all();
|
||||
highlight_changed();
|
||||
redraw_all_later(NOT_VALID);
|
||||
if (ScreenAttrs) {
|
||||
// the meaning of 0 doesn't change anyway
|
||||
// but the rest must be retransmitted
|
||||
memset(ScreenAttrs, 0,
|
||||
sizeof(*ScreenAttrs) * (size_t)(screen_Rows * screen_Columns));
|
||||
}
|
||||
} else {
|
||||
kv_destroy(attr_entries);
|
||||
map_free(HlEntry, int)(attr_entry_ids);
|
||||
map_free(int, int)(combine_attr_entries);
|
||||
}
|
||||
}
|
||||
|
||||
// Combine special attributes (e.g., for spelling) with other attributes
|
||||
// (e.g., for syntax highlighting).
|
||||
// "prim_attr" overrules "char_attr".
|
||||
// This creates a new group when required.
|
||||
// Since we expect there to be few spelling mistakes we don't cache the
|
||||
// result.
|
||||
// Return the resulting attributes.
|
||||
int hl_combine_attr(int char_attr, int prim_attr)
|
||||
{
|
||||
if (char_attr == 0) {
|
||||
return prim_attr;
|
||||
} else if (prim_attr == 0) {
|
||||
return char_attr;
|
||||
}
|
||||
|
||||
// TODO(bfredl): could use a struct for clearer intent.
|
||||
int combine_tag = (char_attr << 16) + prim_attr;
|
||||
int id = map_get(int, int)(combine_attr_entries, combine_tag);
|
||||
if (id > 0) {
|
||||
return id;
|
||||
}
|
||||
|
||||
HlAttrs *char_aep, *spell_aep;
|
||||
HlAttrs new_en = HLATTRS_INIT;
|
||||
|
||||
|
||||
// Find the entry for char_attr
|
||||
char_aep = syn_attr2entry(char_attr);
|
||||
|
||||
if (char_aep != NULL) {
|
||||
// Copy all attributes from char_aep to the new entry
|
||||
new_en = *char_aep;
|
||||
}
|
||||
|
||||
spell_aep = syn_attr2entry(prim_attr);
|
||||
if (spell_aep != NULL) {
|
||||
new_en.cterm_ae_attr |= spell_aep->cterm_ae_attr;
|
||||
new_en.rgb_ae_attr |= spell_aep->rgb_ae_attr;
|
||||
|
||||
if (spell_aep->cterm_fg_color > 0) {
|
||||
new_en.cterm_fg_color = spell_aep->cterm_fg_color;
|
||||
}
|
||||
|
||||
if (spell_aep->cterm_bg_color > 0) {
|
||||
new_en.cterm_bg_color = spell_aep->cterm_bg_color;
|
||||
}
|
||||
|
||||
if (spell_aep->rgb_fg_color >= 0) {
|
||||
new_en.rgb_fg_color = spell_aep->rgb_fg_color;
|
||||
}
|
||||
|
||||
if (spell_aep->rgb_bg_color >= 0) {
|
||||
new_en.rgb_bg_color = spell_aep->rgb_bg_color;
|
||||
}
|
||||
|
||||
if (spell_aep->rgb_sp_color >= 0) {
|
||||
new_en.rgb_sp_color = spell_aep->rgb_sp_color;
|
||||
}
|
||||
}
|
||||
|
||||
id = get_attr_entry((HlEntry){ .attr = new_en, .kind = kHlCombine,
|
||||
.id1 = char_attr, .id2 = prim_attr });
|
||||
if (id > 0) {
|
||||
map_put(int, int)(combine_attr_entries, combine_tag, id);
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/// Get highlight attributes for a attribute code
|
||||
HlAttrs *syn_attr2entry(int attr)
|
||||
{
|
||||
if (attr <= 0 || attr >= (int)kv_size(attr_entries)) {
|
||||
// invalid attribute code, or the tables were cleared
|
||||
return NULL;
|
||||
}
|
||||
return &(kv_A(attr_entries, attr).attr);
|
||||
}
|
||||
|
||||
/// Gets highlight description for id `attr_id` as a map.
|
||||
Dictionary hl_get_attr_by_id(Integer attr_id, Boolean rgb, Error *err)
|
||||
{
|
||||
HlAttrs *aep = NULL;
|
||||
Dictionary dic = ARRAY_DICT_INIT;
|
||||
|
||||
if (attr_id == 0) {
|
||||
return dic;
|
||||
}
|
||||
|
||||
aep = syn_attr2entry((int)attr_id);
|
||||
if (!aep) {
|
||||
api_set_error(err, kErrorTypeException,
|
||||
"Invalid attribute id: %" PRId64, attr_id);
|
||||
return dic;
|
||||
}
|
||||
|
||||
return hlattrs2dict(aep, rgb);
|
||||
}
|
||||
|
||||
/// Converts an HlAttrs into Dictionary
|
||||
///
|
||||
/// @param[in] aep data to convert
|
||||
/// @param use_rgb use 'gui*' settings if true, else resorts to 'cterm*'
|
||||
Dictionary hlattrs2dict(const HlAttrs *aep, bool use_rgb)
|
||||
{
|
||||
assert(aep);
|
||||
Dictionary hl = ARRAY_DICT_INIT;
|
||||
int mask = use_rgb ? aep->rgb_ae_attr : aep->cterm_ae_attr;
|
||||
|
||||
if (mask & HL_BOLD) {
|
||||
PUT(hl, "bold", BOOLEAN_OBJ(true));
|
||||
}
|
||||
|
||||
if (mask & HL_STANDOUT) {
|
||||
PUT(hl, "standout", BOOLEAN_OBJ(true));
|
||||
}
|
||||
|
||||
if (mask & HL_UNDERLINE) {
|
||||
PUT(hl, "underline", BOOLEAN_OBJ(true));
|
||||
}
|
||||
|
||||
if (mask & HL_UNDERCURL) {
|
||||
PUT(hl, "undercurl", BOOLEAN_OBJ(true));
|
||||
}
|
||||
|
||||
if (mask & HL_ITALIC) {
|
||||
PUT(hl, "italic", BOOLEAN_OBJ(true));
|
||||
}
|
||||
|
||||
if (mask & HL_INVERSE) {
|
||||
PUT(hl, "reverse", BOOLEAN_OBJ(true));
|
||||
}
|
||||
|
||||
if (use_rgb) {
|
||||
if (aep->rgb_fg_color != -1) {
|
||||
PUT(hl, "foreground", INTEGER_OBJ(aep->rgb_fg_color));
|
||||
}
|
||||
|
||||
if (aep->rgb_bg_color != -1) {
|
||||
PUT(hl, "background", INTEGER_OBJ(aep->rgb_bg_color));
|
||||
}
|
||||
|
||||
if (aep->rgb_sp_color != -1) {
|
||||
PUT(hl, "special", INTEGER_OBJ(aep->rgb_sp_color));
|
||||
}
|
||||
} else {
|
||||
if (cterm_normal_fg_color != aep->cterm_fg_color) {
|
||||
PUT(hl, "foreground", INTEGER_OBJ(aep->cterm_fg_color - 1));
|
||||
}
|
||||
|
||||
if (cterm_normal_bg_color != aep->cterm_bg_color) {
|
||||
PUT(hl, "background", INTEGER_OBJ(aep->cterm_bg_color - 1));
|
||||
}
|
||||
}
|
||||
|
||||
return hl;
|
||||
}
|
||||
|
||||
Array hl_inspect(int attr)
|
||||
{
|
||||
Array ret = ARRAY_DICT_INIT;
|
||||
if (hlstate_active) {
|
||||
hl_inspect_impl(&ret, attr);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void hl_inspect_impl(Array *arr, int attr)
|
||||
{
|
||||
Dictionary item = ARRAY_DICT_INIT;
|
||||
if (attr <= 0 || attr >= (int)kv_size(attr_entries)) {
|
||||
return;
|
||||
}
|
||||
|
||||
HlEntry e = kv_A(attr_entries, attr);
|
||||
switch (e.kind) {
|
||||
case kHlSyntax:
|
||||
PUT(item, "kind", STRING_OBJ(cstr_to_string("syntax")));
|
||||
PUT(item, "hi_name",
|
||||
STRING_OBJ(cstr_to_string((char *)syn_id2name(e.id1))));
|
||||
break;
|
||||
|
||||
case kHlUI:
|
||||
PUT(item, "kind", STRING_OBJ(cstr_to_string("ui")));
|
||||
const char *ui_name = (e.id1 == -1) ? "Normal" : hlf_names[e.id1];
|
||||
PUT(item, "ui_name", STRING_OBJ(cstr_to_string(ui_name)));
|
||||
PUT(item, "hi_name",
|
||||
STRING_OBJ(cstr_to_string((char *)syn_id2name(e.id2))));
|
||||
break;
|
||||
|
||||
case kHlTerminal:
|
||||
PUT(item, "kind", STRING_OBJ(cstr_to_string("term")));
|
||||
break;
|
||||
|
||||
case kHlCombine:
|
||||
// attribute combination is associative, so flatten to an array
|
||||
hl_inspect_impl(arr, e.id1);
|
||||
hl_inspect_impl(arr, e.id2);
|
||||
return;
|
||||
|
||||
case kHlUnknown:
|
||||
return;
|
||||
}
|
||||
PUT(item, "id", INTEGER_OBJ(attr));
|
||||
ADD(*arr, DICTIONARY_OBJ(item));
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef NVIM_HIGHLIGHT_H
|
||||
#define NVIM_HIGHLIGHT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "nvim/highlight_defs.h"
|
||||
#include "nvim/api/private/defs.h"
|
||||
#include "nvim/ui.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "highlight.h.generated.h"
|
||||
#endif
|
||||
|
||||
#endif // NVIM_HIGHLIGHT_H
|
|
@ -8,6 +8,8 @@
|
|||
typedef int32_t RgbValue;
|
||||
|
||||
/// Highlighting attribute bits.
|
||||
///
|
||||
/// sign bit should not be used here, as it identifies invalid highlight
|
||||
typedef enum {
|
||||
HL_INVERSE = 0x01,
|
||||
HL_BOLD = 0x02,
|
||||
|
@ -35,6 +37,17 @@ typedef struct attr_entry {
|
|||
.cterm_bg_color = 0, \
|
||||
}
|
||||
|
||||
// sentinel value that compares unequal to any valid highlight
|
||||
#define HLATTRS_INVALID (HlAttrs) { \
|
||||
.rgb_ae_attr = -1, \
|
||||
.cterm_ae_attr = -1, \
|
||||
.rgb_fg_color = -1, \
|
||||
.rgb_bg_color = -1, \
|
||||
.rgb_sp_color = -1, \
|
||||
.cterm_fg_color = 0, \
|
||||
.cterm_bg_color = 0, \
|
||||
}
|
||||
|
||||
/// Values for index in highlight_attr[].
|
||||
/// When making changes, also update hlf_names below!
|
||||
typedef enum {
|
||||
|
@ -152,4 +165,19 @@ EXTERN RgbValue normal_fg INIT(= -1);
|
|||
EXTERN RgbValue normal_bg INIT(= -1);
|
||||
EXTERN RgbValue normal_sp INIT(= -1);
|
||||
|
||||
typedef enum {
|
||||
kHlUnknown,
|
||||
kHlUI,
|
||||
kHlSyntax,
|
||||
kHlTerminal,
|
||||
kHlCombine,
|
||||
} HlKind;
|
||||
|
||||
typedef struct {
|
||||
HlAttrs attr;
|
||||
HlKind kind;
|
||||
int id1;
|
||||
int id2;
|
||||
} HlEntry;
|
||||
|
||||
#endif // NVIM_HIGHLIGHT_DEFS_H
|
||||
|
|
|
@ -98,14 +98,14 @@
|
|||
(*kv_pushp(v) = (x))
|
||||
|
||||
#define kv_a(v, i) \
|
||||
(((v).capacity <= (size_t) (i) \
|
||||
(*(((v).capacity <= (size_t) (i) \
|
||||
? ((v).capacity = (v).size = (i) + 1, \
|
||||
kv_roundup32((v).capacity), \
|
||||
kv_resize((v), (v).capacity), 0) \
|
||||
kv_resize((v), (v).capacity), 0UL) \
|
||||
: ((v).size <= (size_t) (i) \
|
||||
? (v).size = (i) + 1 \
|
||||
: 0)), \
|
||||
(v).items[(i)])
|
||||
: 0UL)), \
|
||||
&(v).items[(i)]))
|
||||
|
||||
/// Type of a vector with a few first members allocated on stack
|
||||
///
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "nvim/fold.h"
|
||||
#include "nvim/getchar.h"
|
||||
#include "nvim/hashtab.h"
|
||||
#include "nvim/highlight.h"
|
||||
#include "nvim/iconv.h"
|
||||
#include "nvim/if_cscope.h"
|
||||
#ifdef HAVE_LOCALE_H
|
||||
|
@ -182,6 +183,7 @@ void early_init(void)
|
|||
eval_init(); // init global variables
|
||||
init_path(argv0 ? argv0 : "nvim");
|
||||
init_normal_cmds(); // Init the table of Normal mode commands.
|
||||
highlight_init();
|
||||
|
||||
#if defined(HAVE_LOCALE_H)
|
||||
// Setup to use the current locale (for ctype() and many other things).
|
||||
|
@ -452,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.
|
||||
|
@ -1372,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");
|
||||
|
|
|
@ -140,6 +140,22 @@ static inline bool String_eq(String a, String b)
|
|||
return memcmp(a.data, b.data, a.size) == 0;
|
||||
}
|
||||
|
||||
static inline khint_t HlEntry_hash(HlEntry ae)
|
||||
{
|
||||
const uint8_t *data = (const uint8_t *)&ae;
|
||||
khint_t h = 0;
|
||||
for (size_t i = 0; i < sizeof(ae); i++) {
|
||||
h = (h << 5) - h + data[i];
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
static inline bool HlEntry_eq(HlEntry ae1, HlEntry ae2)
|
||||
{
|
||||
return memcmp(&ae1, &ae2, sizeof(ae1)) == 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
MAP_IMPL(int, int, DEFAULT_INITIALIZER)
|
||||
MAP_IMPL(cstr_t, ptr_t, DEFAULT_INITIALIZER)
|
||||
|
@ -149,3 +165,4 @@ MAP_IMPL(handle_T, ptr_t, DEFAULT_INITIALIZER)
|
|||
#define MSGPACK_HANDLER_INITIALIZER { .fn = NULL, .async = false }
|
||||
MAP_IMPL(String, MsgpackRpcRequestHandler, MSGPACK_HANDLER_INITIALIZER)
|
||||
#define KVEC_INITIALIZER { .size = 0, .capacity = 0, .items = NULL }
|
||||
MAP_IMPL(HlEntry, int, DEFAULT_INITIALIZER)
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "nvim/api/private/defs.h"
|
||||
#include "nvim/api/private/dispatch.h"
|
||||
#include "nvim/bufhl_defs.h"
|
||||
#include "nvim/highlight_defs.h"
|
||||
|
||||
#if defined(__NetBSD__)
|
||||
# undef uint64_t
|
||||
|
@ -35,6 +36,7 @@ MAP_DECLS(ptr_t, ptr_t)
|
|||
MAP_DECLS(uint64_t, ptr_t)
|
||||
MAP_DECLS(handle_T, ptr_t)
|
||||
MAP_DECLS(String, MsgpackRpcRequestHandler)
|
||||
MAP_DECLS(HlEntry, int)
|
||||
|
||||
#define map_new(T, U) map_##T##_##U##_new
|
||||
#define map_free(T, U) map_##T##_##U##_free
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "nvim/vim.h"
|
||||
#include "nvim/eval.h"
|
||||
#include "nvim/highlight.h"
|
||||
#include "nvim/memfile.h"
|
||||
#include "nvim/memory.h"
|
||||
#include "nvim/message.h"
|
||||
|
@ -696,7 +697,7 @@ void free_all_mem(void)
|
|||
/* screenlines (can't display anything now!) */
|
||||
free_screenlines();
|
||||
|
||||
clear_hl_tables();
|
||||
clear_hl_tables(false);
|
||||
list_free_log();
|
||||
}
|
||||
|
||||
|
|
|
@ -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++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,6 +85,7 @@
|
|||
#include "nvim/fold.h"
|
||||
#include "nvim/indent.h"
|
||||
#include "nvim/getchar.h"
|
||||
#include "nvim/highlight.h"
|
||||
#include "nvim/main.h"
|
||||
#include "nvim/mark.h"
|
||||
#include "nvim/mbyte.h"
|
||||
|
@ -299,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) {
|
||||
|
@ -1479,6 +1481,8 @@ static void win_update(win_T *wp)
|
|||
wp->w_empty_rows = 0;
|
||||
wp->w_filler_rows = 0;
|
||||
if (!eof && !didline) {
|
||||
int at_attr = hl_combine_attr(wp->w_hl_attr_normal,
|
||||
win_hl_attr(wp, HLF_AT));
|
||||
if (lnum == wp->w_topline) {
|
||||
/*
|
||||
* Single line that does not fit!
|
||||
|
@ -1493,12 +1497,11 @@ static void win_update(win_T *wp)
|
|||
int scr_row = wp->w_winrow + wp->w_height - 1;
|
||||
|
||||
// Last line isn't finished: Display "@@@" in the last screen line.
|
||||
screen_puts_len((char_u *)"@@", 2, scr_row, wp->w_wincol,
|
||||
win_hl_attr(wp, HLF_AT));
|
||||
screen_puts_len((char_u *)"@@", 2, scr_row, wp->w_wincol, at_attr);
|
||||
|
||||
screen_fill(scr_row, scr_row + 1,
|
||||
(int)wp->w_wincol + 2, (int)W_ENDCOL(wp),
|
||||
'@', ' ', win_hl_attr(wp, HLF_AT));
|
||||
'@', ' ', at_attr);
|
||||
set_empty_rows(wp, srow);
|
||||
wp->w_botline = lnum;
|
||||
} else if (dy_flags & DY_LASTLINE) { // 'display' has "lastline"
|
||||
|
@ -1506,7 +1509,7 @@ static void win_update(win_T *wp)
|
|||
screen_fill(wp->w_winrow + wp->w_height - 1,
|
||||
wp->w_winrow + wp->w_height,
|
||||
W_ENDCOL(wp) - 3, W_ENDCOL(wp),
|
||||
'@', '@', win_hl_attr(wp, HLF_AT));
|
||||
'@', '@', at_attr);
|
||||
set_empty_rows(wp, srow);
|
||||
wp->w_botline = lnum;
|
||||
} else {
|
||||
|
@ -1604,7 +1607,7 @@ static void win_draw_end(win_T *wp, int c1, int c2, int row, int endrow, hlf_T h
|
|||
# define FDC_OFF n
|
||||
int fdc = compute_foldcolumn(wp, 0);
|
||||
|
||||
int attr = win_hl_attr(wp, hl);
|
||||
int attr = hl_combine_attr(wp->w_hl_attr_normal, win_hl_attr(wp, hl));
|
||||
|
||||
if (wp->w_p_rl) {
|
||||
// No check for cmdline window: should never be right-left.
|
||||
|
@ -1991,7 +1994,7 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T
|
|||
}
|
||||
|
||||
screen_line(row + wp->w_winrow, wp->w_wincol, wp->w_width,
|
||||
wp->w_width, false, wp, 0);
|
||||
wp->w_width, false, wp, wp->w_hl_attr_normal);
|
||||
|
||||
/*
|
||||
* Update w_cline_height and w_cline_folded if the cursor line was
|
||||
|
@ -2407,7 +2410,7 @@ win_line (
|
|||
if (wp->w_p_cul && lnum == wp->w_cursor.lnum
|
||||
&& !(wp == curwin && VIsual_active)) {
|
||||
int cul_attr = win_hl_attr(wp, HLF_CUL);
|
||||
HlAttrs *aep = syn_cterm_attr2entry(cul_attr);
|
||||
HlAttrs *aep = syn_attr2entry(cul_attr);
|
||||
|
||||
// We make a compromise here (#7383):
|
||||
// * low-priority CursorLine if fg is not set
|
||||
|
@ -4224,25 +4227,7 @@ win_line (
|
|||
LineOffset[screen_row] + screen_Columns)
|
||||
== 2))
|
||||
) {
|
||||
/* First make sure we are at the end of the screen line,
|
||||
* then output the same character again to let the
|
||||
* terminal know about the wrap. If the terminal doesn't
|
||||
* auto-wrap, we overwrite the character. */
|
||||
if (ui_current_col() != wp->w_width)
|
||||
screen_char(LineOffset[screen_row - 1]
|
||||
+ (unsigned)Columns - 1,
|
||||
screen_row - 1, (int)(Columns - 1));
|
||||
|
||||
/* When there is a multi-byte character, just output a
|
||||
* space to keep it simple. */
|
||||
if (ScreenLines[LineOffset[screen_row - 1]
|
||||
+ (Columns - 1)][1] != 0) {
|
||||
ui_putc(' ');
|
||||
} else {
|
||||
ui_puts(ScreenLines[LineOffset[screen_row - 1] + (Columns - 1)]);
|
||||
}
|
||||
/* force a redraw of the first char on the next line */
|
||||
ScreenAttrs[LineOffset[screen_row]] = (sattr_T)-1;
|
||||
ui_add_linewrap(screen_row-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4330,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;
|
||||
|
@ -4384,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.
|
||||
|
@ -4404,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;
|
||||
|
@ -4421,23 +4410,29 @@ static void screen_line(int row, int coloff, int endcol,
|
|||
/* Clear the second half of a double-wide character of which the left
|
||||
* half was overwritten with a single-wide character. */
|
||||
schar_from_ascii(ScreenLines[off_to], ' ');
|
||||
screen_char(off_to, row, col + coloff);
|
||||
end_dirty++;
|
||||
}
|
||||
|
||||
int clear_end = -1;
|
||||
if (clear_width > 0 && !rlflag) {
|
||||
// blank out the rest of the line
|
||||
while (col < clear_width && ScreenLines[off_to][0] == ' '
|
||||
&& ScreenLines[off_to][1] == NUL
|
||||
&& ScreenAttrs[off_to] == bg_attr
|
||||
) {
|
||||
++off_to;
|
||||
++col;
|
||||
}
|
||||
if (col < clear_width) {
|
||||
screen_fill(row, row + 1, col + coloff, clear_width + coloff, ' ', ' ',
|
||||
bg_attr);
|
||||
off_to += clear_width - col;
|
||||
col = clear_width;
|
||||
// TODO(bfredl): we could cache winline widths
|
||||
while (col < clear_width) {
|
||||
if (ScreenLines[off_to][0] != ' ' || ScreenLines[off_to][1] != NUL
|
||||
|| ScreenAttrs[off_to] != bg_attr) {
|
||||
ScreenLines[off_to][0] = ' ';
|
||||
ScreenLines[off_to][1] = NUL;
|
||||
ScreenAttrs[off_to] = bg_attr;
|
||||
if (start_dirty == -1) {
|
||||
start_dirty = col;
|
||||
end_dirty = col;
|
||||
} else if (clear_end == -1) {
|
||||
end_dirty = endcol;
|
||||
}
|
||||
clear_end = col+1;
|
||||
}
|
||||
col++;
|
||||
off_to++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4452,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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -4738,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 {
|
||||
|
@ -5106,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++) {
|
||||
|
@ -5126,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;
|
||||
|
@ -5223,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[].
|
||||
|
@ -5235,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.
|
||||
|
@ -5258,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;
|
||||
|
@ -5268,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;
|
||||
|
@ -5349,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;
|
||||
|
@ -5361,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.
|
||||
*/
|
||||
|
@ -5391,41 +5452,6 @@ static void end_search_hl(void)
|
|||
}
|
||||
}
|
||||
|
||||
static void update_window_hl(win_T *wp, bool invalid)
|
||||
{
|
||||
if (!wp->w_hl_needs_update && !invalid) {
|
||||
return;
|
||||
}
|
||||
wp->w_hl_needs_update = false;
|
||||
|
||||
// determine window specific background set in 'winhighlight'
|
||||
if (wp != curwin && wp->w_hl_ids[HLF_INACTIVE] > 0) {
|
||||
wp->w_hl_attr_normal = syn_id2attr(wp->w_hl_ids[HLF_INACTIVE]);
|
||||
} else if (wp->w_hl_id_normal > 0) {
|
||||
wp->w_hl_attr_normal = syn_id2attr(wp->w_hl_id_normal);
|
||||
} else {
|
||||
wp->w_hl_attr_normal = 0;
|
||||
}
|
||||
if (wp != curwin) {
|
||||
wp->w_hl_attr_normal = hl_combine_attr(HL_ATTR(HLF_INACTIVE),
|
||||
wp->w_hl_attr_normal);
|
||||
}
|
||||
|
||||
for (int hlf = 0; hlf < (int)HLF_COUNT; hlf++) {
|
||||
int attr;
|
||||
if (wp->w_hl_ids[hlf] > 0) {
|
||||
attr = syn_id2attr(wp->w_hl_ids[hlf]);
|
||||
} else {
|
||||
attr = HL_ATTR(hlf);
|
||||
}
|
||||
if (wp->w_hl_attr_normal != 0) {
|
||||
attr = hl_combine_attr(wp->w_hl_attr_normal, attr);
|
||||
}
|
||||
wp->w_hl_attrs[hlf] = attr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Init for calling prepare_search_hl().
|
||||
|
@ -5692,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'
|
||||
|
@ -5726,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 */
|
||||
|
@ -5743,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
|
||||
|
@ -5757,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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6078,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
|
||||
|
@ -6115,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));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -6204,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;
|
||||
}
|
||||
|
||||
|
@ -6240,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;
|
||||
|
@ -6264,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];
|
||||
|
@ -6284,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;
|
||||
|
@ -6318,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;
|
||||
|
@ -6339,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
|
||||
*
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "nvim/fileio.h"
|
||||
#include "nvim/fold.h"
|
||||
#include "nvim/hashtab.h"
|
||||
#include "nvim/highlight.h"
|
||||
#include "nvim/indent_c.h"
|
||||
#include "nvim/mbyte.h"
|
||||
#include "nvim/memline.h"
|
||||
|
@ -42,7 +43,6 @@
|
|||
#include "nvim/ui.h"
|
||||
#include "nvim/os/os.h"
|
||||
#include "nvim/os/time.h"
|
||||
#include "nvim/api/private/helpers.h"
|
||||
#include "nvim/buffer.h"
|
||||
|
||||
static bool did_syntax_onoff = false;
|
||||
|
@ -216,12 +216,6 @@ struct name_list {
|
|||
# include "syntax.c.generated.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* An attribute number is the index in attr_table plus ATTR_OFF.
|
||||
*/
|
||||
#define ATTR_OFF 1
|
||||
|
||||
|
||||
static char *(spo_name_tab[SPO_COUNT]) =
|
||||
{"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
|
||||
|
||||
|
@ -6804,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;
|
||||
|
||||
|
@ -6915,8 +6907,16 @@ void do_highlight(const char *line, const bool forceit, const bool init)
|
|||
// Need to update all groups, because they might be using "bg" and/or
|
||||
// "fg", which have been changed now.
|
||||
highlight_attr_set_all();
|
||||
// If the normal group has changed, it is simpler to refresh every UI
|
||||
ui_refresh();
|
||||
|
||||
if (!ui_is_external(kUINewgrid)) {
|
||||
// Older UIs assume that we clear the screen after normal group is
|
||||
// changed
|
||||
ui_refresh();
|
||||
} else {
|
||||
// TUI and newer UIs will repaint the screen themselves. NOT_VALID
|
||||
// redraw below will still handle usages of guibg=fg etc.
|
||||
ui_default_colors_set();
|
||||
}
|
||||
} else {
|
||||
set_hl_attr(idx);
|
||||
}
|
||||
|
@ -7001,161 +7001,6 @@ static void highlight_clear(int idx)
|
|||
}
|
||||
|
||||
|
||||
/// Table with the specifications for an attribute number.
|
||||
/// Note that this table is used by ALL buffers. This is required because the
|
||||
/// GUI can redraw at any time for any buffer.
|
||||
static garray_T attr_table = GA_EMPTY_INIT_VALUE;
|
||||
|
||||
static inline HlAttrs * ATTR_ENTRY(int idx)
|
||||
{
|
||||
return &((HlAttrs *)attr_table.ga_data)[idx];
|
||||
}
|
||||
|
||||
|
||||
/// Return the attr number for a set of colors and font.
|
||||
/// Add a new entry to the term_attr_table, attr_table or gui_attr_table
|
||||
/// if the combination is new.
|
||||
/// @return 0 for error.
|
||||
int get_attr_entry(HlAttrs *aep)
|
||||
{
|
||||
garray_T *table = &attr_table;
|
||||
HlAttrs *taep;
|
||||
static int recursive = false;
|
||||
|
||||
/*
|
||||
* Init the table, in case it wasn't done yet.
|
||||
*/
|
||||
table->ga_itemsize = sizeof(HlAttrs);
|
||||
ga_set_growsize(table, 7);
|
||||
|
||||
// Try to find an entry with the same specifications.
|
||||
for (int i = 0; i < table->ga_len; i++) {
|
||||
taep = &(((HlAttrs *)table->ga_data)[i]);
|
||||
if (aep->cterm_ae_attr == taep->cterm_ae_attr
|
||||
&& aep->cterm_fg_color == taep->cterm_fg_color
|
||||
&& aep->cterm_bg_color == taep->cterm_bg_color
|
||||
&& aep->rgb_ae_attr == taep->rgb_ae_attr
|
||||
&& aep->rgb_fg_color == taep->rgb_fg_color
|
||||
&& aep->rgb_bg_color == taep->rgb_bg_color
|
||||
&& aep->rgb_sp_color == taep->rgb_sp_color) {
|
||||
return i + ATTR_OFF;
|
||||
}
|
||||
}
|
||||
|
||||
if (table->ga_len + ATTR_OFF > MAX_TYPENR) {
|
||||
/*
|
||||
* Running out of attribute entries! remove all attributes, and
|
||||
* compute new ones for all groups.
|
||||
* When called recursively, we are really out of numbers.
|
||||
*/
|
||||
if (recursive) {
|
||||
EMSG(_("E424: Too many different highlighting attributes in use"));
|
||||
return 0;
|
||||
}
|
||||
recursive = TRUE;
|
||||
|
||||
clear_hl_tables();
|
||||
|
||||
must_redraw = CLEAR;
|
||||
|
||||
for (int i = 0; i < highlight_ga.ga_len; ++i) {
|
||||
set_hl_attr(i);
|
||||
}
|
||||
|
||||
recursive = FALSE;
|
||||
}
|
||||
|
||||
|
||||
// This is a new combination of colors and font, add an entry.
|
||||
taep = GA_APPEND_VIA_PTR(HlAttrs, table);
|
||||
memset(taep, 0, sizeof(*taep));
|
||||
taep->cterm_ae_attr = aep->cterm_ae_attr;
|
||||
taep->cterm_fg_color = aep->cterm_fg_color;
|
||||
taep->cterm_bg_color = aep->cterm_bg_color;
|
||||
taep->rgb_ae_attr = aep->rgb_ae_attr;
|
||||
taep->rgb_fg_color = aep->rgb_fg_color;
|
||||
taep->rgb_bg_color = aep->rgb_bg_color;
|
||||
taep->rgb_sp_color = aep->rgb_sp_color;
|
||||
|
||||
return table->ga_len - 1 + ATTR_OFF;
|
||||
}
|
||||
|
||||
// Clear all highlight tables.
|
||||
void clear_hl_tables(void)
|
||||
{
|
||||
ga_clear(&attr_table);
|
||||
}
|
||||
|
||||
// Combine special attributes (e.g., for spelling) with other attributes
|
||||
// (e.g., for syntax highlighting).
|
||||
// "prim_attr" overrules "char_attr".
|
||||
// This creates a new group when required.
|
||||
// Since we expect there to be few spelling mistakes we don't cache the
|
||||
// result.
|
||||
// Return the resulting attributes.
|
||||
int hl_combine_attr(int char_attr, int prim_attr)
|
||||
{
|
||||
HlAttrs *char_aep = NULL;
|
||||
HlAttrs *spell_aep;
|
||||
HlAttrs new_en = HLATTRS_INIT;
|
||||
|
||||
if (char_attr == 0) {
|
||||
return prim_attr;
|
||||
}
|
||||
|
||||
if (prim_attr == 0) {
|
||||
return char_attr;
|
||||
}
|
||||
|
||||
// Find the entry for char_attr
|
||||
char_aep = syn_cterm_attr2entry(char_attr);
|
||||
|
||||
if (char_aep != NULL) {
|
||||
// Copy all attributes from char_aep to the new entry
|
||||
new_en = *char_aep;
|
||||
}
|
||||
|
||||
spell_aep = syn_cterm_attr2entry(prim_attr);
|
||||
if (spell_aep != NULL) {
|
||||
new_en.cterm_ae_attr |= spell_aep->cterm_ae_attr;
|
||||
new_en.rgb_ae_attr |= spell_aep->rgb_ae_attr;
|
||||
|
||||
if (spell_aep->cterm_fg_color > 0) {
|
||||
new_en.cterm_fg_color = spell_aep->cterm_fg_color;
|
||||
}
|
||||
|
||||
if (spell_aep->cterm_bg_color > 0) {
|
||||
new_en.cterm_bg_color = spell_aep->cterm_bg_color;
|
||||
}
|
||||
|
||||
if (spell_aep->rgb_fg_color >= 0) {
|
||||
new_en.rgb_fg_color = spell_aep->rgb_fg_color;
|
||||
}
|
||||
|
||||
if (spell_aep->rgb_bg_color >= 0) {
|
||||
new_en.rgb_bg_color = spell_aep->rgb_bg_color;
|
||||
}
|
||||
|
||||
if (spell_aep->rgb_sp_color >= 0) {
|
||||
new_en.rgb_sp_color = spell_aep->rgb_sp_color;
|
||||
}
|
||||
}
|
||||
return get_attr_entry(&new_en);
|
||||
}
|
||||
|
||||
/// \note this function does not apply exclusively to cterm attr contrary
|
||||
/// to what its name implies
|
||||
/// \warn don't call it with attr 0 (i.e., the null attribute)
|
||||
HlAttrs *syn_cterm_attr2entry(int attr)
|
||||
{
|
||||
attr -= ATTR_OFF;
|
||||
if (attr >= attr_table.ga_len) {
|
||||
// did ":syntax clear"
|
||||
return NULL;
|
||||
}
|
||||
return ATTR_ENTRY(attr);
|
||||
}
|
||||
|
||||
/// \addtogroup LIST_XXX
|
||||
/// @{
|
||||
#define LIST_ATTR 1
|
||||
|
@ -7410,15 +7255,7 @@ static void set_hl_attr(int idx)
|
|||
at_en.rgb_bg_color = sgp->sg_rgb_bg_name ? sgp->sg_rgb_bg : -1;
|
||||
at_en.rgb_sp_color = sgp->sg_rgb_sp_name ? sgp->sg_rgb_sp : -1;
|
||||
|
||||
if (at_en.cterm_fg_color != 0 || at_en.cterm_bg_color != 0
|
||||
|| at_en.rgb_fg_color != -1 || at_en.rgb_bg_color != -1
|
||||
|| at_en.rgb_sp_color != -1 || at_en.cterm_ae_attr != 0
|
||||
|| at_en.rgb_ae_attr != 0) {
|
||||
sgp->sg_attr = get_attr_entry(&at_en);
|
||||
} else {
|
||||
// If all the fields are cleared, clear the attr field back to default value
|
||||
sgp->sg_attr = 0;
|
||||
}
|
||||
sgp->sg_attr = hl_get_syn_attr(idx+1, at_en);
|
||||
}
|
||||
|
||||
/// Lookup a highlight group name and return its ID.
|
||||
|
@ -7553,7 +7390,7 @@ static void syn_unadd_group(void)
|
|||
|
||||
|
||||
/// Translate a group ID to highlight attributes.
|
||||
/// @see syn_cterm_attr2entry
|
||||
/// @see syn_attr2entry
|
||||
int syn_id2attr(int hl_id)
|
||||
{
|
||||
struct hl_group *sgp;
|
||||
|
@ -7590,7 +7427,7 @@ int syn_get_final_id(int hl_id)
|
|||
}
|
||||
|
||||
/// Refresh the color attributes of all highlight groups.
|
||||
static void highlight_attr_set_all(void)
|
||||
void highlight_attr_set_all(void)
|
||||
{
|
||||
for (int idx = 0; idx < highlight_ga.ga_len; idx++) {
|
||||
struct hl_group *sgp = &HL_TABLE()[idx];
|
||||
|
@ -7613,7 +7450,6 @@ static void highlight_attr_set_all(void)
|
|||
/// screen redraw after any :highlight command.
|
||||
void highlight_changed(void)
|
||||
{
|
||||
int attr;
|
||||
int id;
|
||||
char_u userhl[10];
|
||||
int id_SNC = -1;
|
||||
|
@ -7628,13 +7464,15 @@ void highlight_changed(void)
|
|||
if (id == 0) {
|
||||
abort();
|
||||
}
|
||||
attr = syn_id2attr(id);
|
||||
int final_id = syn_get_final_id(id);
|
||||
if (hlf == (int)HLF_SNC) {
|
||||
id_SNC = syn_get_final_id(id);
|
||||
id_SNC = final_id;
|
||||
} else if (hlf == (int)HLF_S) {
|
||||
id_S = syn_get_final_id(id);
|
||||
id_S = final_id;
|
||||
}
|
||||
highlight_attr[hlf] = attr;
|
||||
|
||||
highlight_attr[hlf] = hl_get_ui_attr(hlf, final_id,
|
||||
hlf == (int)HLF_INACTIVE);
|
||||
}
|
||||
|
||||
/* Setup the user highlights
|
||||
|
@ -8522,26 +8360,6 @@ RgbValue name_to_color(const char_u *name)
|
|||
return -1;
|
||||
}
|
||||
|
||||
/// Gets highlight description for id `attr_id` as a map.
|
||||
Dictionary hl_get_attr_by_id(Integer attr_id, Boolean rgb, Error *err)
|
||||
{
|
||||
HlAttrs *aep = NULL;
|
||||
Dictionary dic = ARRAY_DICT_INIT;
|
||||
|
||||
if (attr_id == 0) {
|
||||
return dic;
|
||||
}
|
||||
|
||||
aep = syn_cterm_attr2entry((int)attr_id);
|
||||
if (!aep) {
|
||||
api_set_error(err, kErrorTypeException,
|
||||
"Invalid attribute id: %" PRId64, attr_id);
|
||||
return dic;
|
||||
}
|
||||
|
||||
return hlattrs2dict(aep, rgb);
|
||||
}
|
||||
|
||||
|
||||
/**************************************
|
||||
* End of Highlighting stuff *
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include "nvim/message.h"
|
||||
#include "nvim/memory.h"
|
||||
#include "nvim/option.h"
|
||||
#include "nvim/highlight.h"
|
||||
#include "nvim/macros.h"
|
||||
#include "nvim/mbyte.h"
|
||||
#include "nvim/buffer.h"
|
||||
|
@ -602,7 +603,7 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr,
|
|||
int attr_id = 0;
|
||||
|
||||
if (hl_attrs || vt_fg != -1 || vt_bg != -1) {
|
||||
attr_id = get_attr_entry(&(HlAttrs) {
|
||||
attr_id = get_term_attr_entry(&(HlAttrs) {
|
||||
.cterm_ae_attr = (int16_t)hl_attrs,
|
||||
.cterm_fg_color = vt_fg_idx,
|
||||
.cterm_bg_color = vt_bg_idx,
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "nvim/vim.h"
|
||||
#include "nvim/log.h"
|
||||
#include "nvim/ui.h"
|
||||
#include "nvim/highlight.h"
|
||||
#include "nvim/map.h"
|
||||
#include "nvim/main.h"
|
||||
#include "nvim/memory.h"
|
||||
|
@ -31,13 +32,13 @@
|
|||
#include "nvim/os/input.h"
|
||||
#include "nvim/os/os.h"
|
||||
#include "nvim/strings.h"
|
||||
#include "nvim/syntax.h"
|
||||
#include "nvim/ui_bridge.h"
|
||||
#include "nvim/ugrid.h"
|
||||
#include "nvim/tui/input.h"
|
||||
#include "nvim/tui/tui.h"
|
||||
#include "nvim/tui/terminfo.h"
|
||||
#include "nvim/cursor_shape.h"
|
||||
#include "nvim/syntax.h"
|
||||
#include "nvim/macros.h"
|
||||
|
||||
// Space reserved in two output buffers to make the cursor normal or invisible
|
||||
|
@ -87,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;
|
||||
|
@ -97,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;
|
||||
|
@ -125,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;
|
||||
|
@ -136,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;
|
||||
|
@ -148,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);
|
||||
}
|
||||
|
@ -289,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);
|
||||
|
@ -345,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
|
||||
|
@ -379,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);
|
||||
}
|
||||
|
||||
|
@ -437,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;
|
||||
|
@ -591,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);
|
||||
|
@ -678,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) {
|
||||
|
@ -724,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)
|
||||
|
@ -791,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);
|
||||
|
@ -809,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);
|
||||
}
|
||||
|
||||
|
@ -932,7 +936,7 @@ static void tui_set_mode(UI *ui, ModeShape mode)
|
|||
if (c.id != 0 && ui->rgb) {
|
||||
int attr = syn_id2attr(c.id);
|
||||
if (attr > 0) {
|
||||
HlAttrs *aep = syn_cterm_attr2entry(attr);
|
||||
HlAttrs *aep = syn_attr2entry(attr);
|
||||
UNIBI_SET_NUM_VAR(data->params[0], aep->rgb_bg_color);
|
||||
unibi_out_ext(ui, data->unibi_ext.set_cursor_color);
|
||||
}
|
||||
|
@ -957,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);
|
||||
|
||||
|
@ -988,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);
|
||||
}
|
||||
}
|
||||
|
@ -1011,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
|
||||
|
@ -1024,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)
|
||||
|
@ -1057,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)
|
||||
|
@ -1082,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);
|
||||
|
@ -1094,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);
|
||||
}
|
||||
|
@ -1175,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;
|
||||
|
|
285
src/nvim/ui.c
285
src/nvim/ui.c
|
@ -32,7 +32,7 @@
|
|||
#include "nvim/os/signal.h"
|
||||
#include "nvim/popupmnu.h"
|
||||
#include "nvim/screen.h"
|
||||
#include "nvim/syntax.h"
|
||||
#include "nvim/highlight.h"
|
||||
#include "nvim/window.h"
|
||||
#include "nvim/cursor_shape.h"
|
||||
#ifdef FEAT_TUI
|
||||
|
@ -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__)
|
||||
|
@ -172,66 +166,6 @@ void ui_event(char *name, Array args)
|
|||
}
|
||||
|
||||
|
||||
/// Converts an HlAttrs into Dictionary
|
||||
///
|
||||
/// @param[in] aep data to convert
|
||||
/// @param use_rgb use 'gui*' settings if true, else resorts to 'cterm*'
|
||||
Dictionary hlattrs2dict(const HlAttrs *aep, bool use_rgb)
|
||||
{
|
||||
assert(aep);
|
||||
Dictionary hl = ARRAY_DICT_INIT;
|
||||
int mask = use_rgb ? aep->rgb_ae_attr : aep->cterm_ae_attr;
|
||||
|
||||
if (mask & HL_BOLD) {
|
||||
PUT(hl, "bold", BOOLEAN_OBJ(true));
|
||||
}
|
||||
|
||||
if (mask & HL_STANDOUT) {
|
||||
PUT(hl, "standout", BOOLEAN_OBJ(true));
|
||||
}
|
||||
|
||||
if (mask & HL_UNDERLINE) {
|
||||
PUT(hl, "underline", BOOLEAN_OBJ(true));
|
||||
}
|
||||
|
||||
if (mask & HL_UNDERCURL) {
|
||||
PUT(hl, "undercurl", BOOLEAN_OBJ(true));
|
||||
}
|
||||
|
||||
if (mask & HL_ITALIC) {
|
||||
PUT(hl, "italic", BOOLEAN_OBJ(true));
|
||||
}
|
||||
|
||||
if (mask & HL_INVERSE) {
|
||||
PUT(hl, "reverse", BOOLEAN_OBJ(true));
|
||||
}
|
||||
|
||||
|
||||
if (use_rgb) {
|
||||
if (aep->rgb_fg_color != -1) {
|
||||
PUT(hl, "foreground", INTEGER_OBJ(aep->rgb_fg_color));
|
||||
}
|
||||
|
||||
if (aep->rgb_bg_color != -1) {
|
||||
PUT(hl, "background", INTEGER_OBJ(aep->rgb_bg_color));
|
||||
}
|
||||
|
||||
if (aep->rgb_sp_color != -1) {
|
||||
PUT(hl, "special", INTEGER_OBJ(aep->rgb_sp_color));
|
||||
}
|
||||
} else {
|
||||
if (cterm_normal_fg_color != aep->cterm_fg_color) {
|
||||
PUT(hl, "foreground", INTEGER_OBJ(aep->cterm_fg_color - 1));
|
||||
}
|
||||
|
||||
if (cterm_normal_bg_color != aep->cterm_bg_color) {
|
||||
PUT(hl, "background", INTEGER_OBJ(aep->cterm_bg_color - 1));
|
||||
}
|
||||
}
|
||||
|
||||
return hl;
|
||||
}
|
||||
|
||||
void ui_refresh(void)
|
||||
{
|
||||
if (!ui_active()) {
|
||||
|
@ -259,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 ...
|
||||
|
@ -267,13 +204,14 @@ void ui_refresh(void)
|
|||
|
||||
for (UIExtension i = 0; (int)i < kUIExtCount; i++) {
|
||||
ui_ext[i] = ext_widgets[i];
|
||||
ui_call_option_set(cstr_as_string((char *)ui_ext_names[i]),
|
||||
BOOLEAN_OBJ(ext_widgets[i]));
|
||||
if (i < kUIGlobalCount) {
|
||||
ui_call_option_set(cstr_as_string((char *)ui_ext_names[i]),
|
||||
BOOLEAN_OBJ(ext_widgets[i]));
|
||||
}
|
||||
}
|
||||
ui_mode_info_set();
|
||||
old_mode_idx = -1;
|
||||
pending_mode_update = true;
|
||||
ui_cursor_shape();
|
||||
current_attr_code = -1;
|
||||
}
|
||||
|
||||
static void ui_refresh_event(void **argv)
|
||||
|
@ -286,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)
|
||||
|
@ -329,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();
|
||||
}
|
||||
|
||||
|
@ -362,95 +302,32 @@ 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_cterm_attr2entry(attr_code);
|
||||
if (aep) {
|
||||
attrs = *aep;
|
||||
}
|
||||
}
|
||||
|
||||
UI_CALL(highlight_set, attrs);
|
||||
}
|
||||
|
||||
void ui_clear_highlight(void)
|
||||
{
|
||||
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;
|
||||
|
||||
if (p_wd) { // 'writedelay': flush & delay each time.
|
||||
ui_flush();
|
||||
uint64_t wd = (uint64_t)labs(p_wd);
|
||||
os_microdelay(wd * 1000u, true);
|
||||
}
|
||||
if (ui->option_set) {
|
||||
ui->option_set(ui, cstr_as_string((char *)ui_ext_names[ext]),
|
||||
BOOLEAN_OBJ(active));
|
||||
}
|
||||
}
|
||||
|
||||
void ui_putc(uint8_t c)
|
||||
void ui_line(int row, int startcol, int endcol, int clearcol, int clearattr)
|
||||
{
|
||||
uint8_t buf[2] = {c, 0};
|
||||
ui_puts(buf);
|
||||
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_cursor_goto(int new_row, int new_col)
|
||||
|
@ -463,6 +340,32 @@ void ui_cursor_goto(int new_row, int new_col)
|
|||
pending_cursor_update = true;
|
||||
}
|
||||
|
||||
void ui_add_linewrap(int row)
|
||||
{
|
||||
// TODO(bfredl): check that this actually still works
|
||||
// and move to TUI module in that case.
|
||||
#if 0
|
||||
// First make sure we are at the end of the screen line,
|
||||
// then output the same character again to let the
|
||||
// terminal know about the wrap. If the terminal doesn't
|
||||
// auto-wrap, we overwrite the character.
|
||||
if (ui_current_col() != Columns) {
|
||||
screen_char(LineOffset[row] + (unsigned)Columns - 1, row,
|
||||
(int)(Columns - 1));
|
||||
}
|
||||
|
||||
// When there is a multi-byte character, just output a
|
||||
// space to keep it simple. */
|
||||
if (ScreenLines[LineOffset[row] + (Columns - 1)][1] != 0) {
|
||||
ui_putc(' ');
|
||||
} else {
|
||||
ui_puts(ScreenLines[LineOffset[row] + (Columns - 1)]);
|
||||
}
|
||||
// force a redraw of the first char on the next line
|
||||
ScreenAttrs[LineOffset[row+1]] = (sattr_T)-1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ui_mode_info_set(void)
|
||||
{
|
||||
Array style = mode_style_array();
|
||||
|
@ -484,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)
|
||||
|
@ -515,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,14 +5,18 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "api/private/defs.h"
|
||||
#include "nvim/buffer_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;
|
||||
|
||||
|
@ -20,7 +24,9 @@ EXTERN const char *ui_ext_names[] INIT(= {
|
|||
"ext_cmdline",
|
||||
"ext_popupmenu",
|
||||
"ext_tabline",
|
||||
"ext_wildmenu"
|
||||
"ext_wildmenu",
|
||||
"ext_newgrid",
|
||||
"ext_hlstate",
|
||||
});
|
||||
|
||||
|
||||
|
@ -31,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" } },
|
||||
|
|
|
@ -834,7 +834,7 @@ describe("'winhighlight' highlight", function()
|
|||
{1:a^a }|
|
||||
{2:~ }|
|
||||
{2:~ }|
|
||||
{11:[No Name] [+] }|
|
||||
{3:[No Name] [+] }|
|
||||
aa |
|
||||
{0:~ }|
|
||||
{4:[No Name] [+] }|
|
||||
|
@ -846,7 +846,7 @@ describe("'winhighlight' highlight", function()
|
|||
{1:^ }|
|
||||
{2:~ }|
|
||||
{2:~ }|
|
||||
{11:[No Name] }|
|
||||
{3:[No Name] }|
|
||||
aa |
|
||||
{0:~ }|
|
||||
{4:[No Name] [+] }|
|
||||
|
@ -891,7 +891,7 @@ describe("'winhighlight' highlight", function()
|
|||
{1:a^a }|
|
||||
{2:~ }|
|
||||
{2:~ }|
|
||||
{11:[No Name] [+] }|
|
||||
{3:[No Name] [+] }|
|
||||
aa |
|
||||
{0:~ }|
|
||||
{4:[No Name] [+] }|
|
||||
|
@ -915,7 +915,7 @@ describe("'winhighlight' highlight", function()
|
|||
{1:^aa }|
|
||||
{2:~ }|
|
||||
{2:~ }|
|
||||
{11:[No Name] [+] }|
|
||||
{3:[No Name] [+] }|
|
||||
aa |
|
||||
{0:~ }|
|
||||
{4:[No Name] [+] }|
|
||||
|
@ -931,10 +931,10 @@ describe("'winhighlight' highlight", function()
|
|||
{1:^ }|
|
||||
{2:~ }|
|
||||
{2:~ }|
|
||||
{11:[No Name] }|
|
||||
{3:[No Name] }|
|
||||
{5: }|
|
||||
{6:~ }|
|
||||
{12:[No Name] }|
|
||||
{4:[No Name] }|
|
||||
|
|
||||
]])
|
||||
|
||||
|
@ -943,10 +943,10 @@ describe("'winhighlight' highlight", function()
|
|||
{5: }|
|
||||
{6:~ }|
|
||||
{6:~ }|
|
||||
{12:[No Name] }|
|
||||
{4:[No Name] }|
|
||||
{1:^ }|
|
||||
{2:~ }|
|
||||
{11:[No Name] }|
|
||||
{3:[No Name] }|
|
||||
|
|
||||
]])
|
||||
|
||||
|
@ -955,10 +955,10 @@ describe("'winhighlight' highlight", function()
|
|||
{1:^ }|
|
||||
{2:~ }|
|
||||
{2:~ }|
|
||||
{11:[No Name] }|
|
||||
{3:[No Name] }|
|
||||
{5: }|
|
||||
{6:~ }|
|
||||
{12:[No Name] }|
|
||||
{4:[No Name] }|
|
||||
|
|
||||
]])
|
||||
end)
|
||||
|
@ -974,7 +974,7 @@ describe("'winhighlight' highlight", function()
|
|||
{3:[No Name] }|
|
||||
{7: }|
|
||||
{8:~ }|
|
||||
{13:[No Name] }|
|
||||
{4:[No Name] }|
|
||||
|
|
||||
]])
|
||||
|
||||
|
@ -983,7 +983,7 @@ describe("'winhighlight' highlight", function()
|
|||
{7: }|
|
||||
{8:~ }|
|
||||
{8:~ }|
|
||||
{13:[No Name] }|
|
||||
{4:[No Name] }|
|
||||
^ |
|
||||
{0:~ }|
|
||||
{3:[No Name] }|
|
||||
|
@ -997,10 +997,10 @@ describe("'winhighlight' highlight", function()
|
|||
{7: }|
|
||||
{8:~ }|
|
||||
{8:~ }|
|
||||
{13:[No Name] }|
|
||||
{4:[No Name] }|
|
||||
{1:^ }|
|
||||
{2:~ }|
|
||||
{11:[No Name] }|
|
||||
{3:[No Name] }|
|
||||
|
|
||||
]])
|
||||
|
||||
|
@ -1012,7 +1012,7 @@ describe("'winhighlight' highlight", function()
|
|||
{3:[No Name] }|
|
||||
{1: }|
|
||||
{2:~ }|
|
||||
{14:[No Name] }|
|
||||
{4:[No Name] }|
|
||||
|
|
||||
]])
|
||||
|
||||
|
@ -1022,10 +1022,10 @@ describe("'winhighlight' highlight", function()
|
|||
{7: }|
|
||||
{8:~ }|
|
||||
{8:~ }|
|
||||
{13:[No Name] }|
|
||||
{4:[No Name] }|
|
||||
{1:^ }|
|
||||
{2:~ }|
|
||||
{11:[No Name] }|
|
||||
{3:[No Name] }|
|
||||
|
|
||||
]])
|
||||
|
||||
|
@ -1037,7 +1037,7 @@ describe("'winhighlight' highlight", function()
|
|||
{3:[No Name] }|
|
||||
{5: }|
|
||||
{6:~ }|
|
||||
{12:[No Name] }|
|
||||
{4:[No Name] }|
|
||||
|
|
||||
]])
|
||||
end)
|
||||
|
|
|
@ -0,0 +1,287 @@
|
|||
local helpers = require('test.functional.helpers')(after_each)
|
||||
local Screen = require('test.functional.ui.screen')
|
||||
|
||||
local clear, insert = helpers.clear, helpers.insert
|
||||
local command = helpers.command
|
||||
local meths = helpers.meths
|
||||
local iswin = helpers.iswin
|
||||
local nvim_dir = helpers.nvim_dir
|
||||
local thelpers = require('test.functional.terminal.helpers')
|
||||
|
||||
describe('ext_hlstate detailed highlights', function()
|
||||
local screen
|
||||
|
||||
before_each(function()
|
||||
clear()
|
||||
command('syntax on')
|
||||
screen = Screen.new(40, 8)
|
||||
screen:attach({ext_hlstate=true})
|
||||
end)
|
||||
|
||||
after_each(function()
|
||||
screen:detach()
|
||||
end)
|
||||
|
||||
|
||||
it('work with combined UI and syntax highlights', function()
|
||||
insert([[
|
||||
these are some lines
|
||||
with colorful text]])
|
||||
meths.buf_add_highlight(0, -1, "String", 0 , 10, 14)
|
||||
meths.buf_add_highlight(0, -1, "Statement", 1 , 5, -1)
|
||||
command("/th co")
|
||||
|
||||
screen:expect([[
|
||||
these are {1:some} lines |
|
||||
^wi{2:th }{4:co}{3:lorful text} |
|
||||
{5:~ }|
|
||||
{5:~ }|
|
||||
{5:~ }|
|
||||
{5:~ }|
|
||||
{5:~ }|
|
||||
{6:search hit BOTTOM, continuing at TOP} |
|
||||
]], {
|
||||
[1] = {{foreground = Screen.colors.Magenta},
|
||||
{{hi_name = "Constant", kind = "syntax"}}},
|
||||
[2] = {{background = Screen.colors.Yellow},
|
||||
{{hi_name = "Search", ui_name = "Search", kind = "ui"}}},
|
||||
[3] = {{bold = true, foreground = Screen.colors.Brown},
|
||||
{{hi_name = "Statement", kind = "syntax"}}},
|
||||
[4] = {{bold = true, background = Screen.colors.Yellow, foreground = Screen.colors.Brown}, {3, 2}},
|
||||
[5] = {{bold = true, foreground = Screen.colors.Blue1},
|
||||
{{hi_name = "NonText", ui_name = "EndOfBuffer", kind = "ui"}}},
|
||||
[6] = {{foreground = Screen.colors.Red},
|
||||
{{hi_name = "WarningMsg", ui_name = "WarningMsg", kind = "ui"}}},
|
||||
})
|
||||
end)
|
||||
|
||||
it('work with cleared UI highlights', function()
|
||||
screen:set_default_attr_ids({
|
||||
[1] = {{}, {{hi_name = "VertSplit", ui_name = "VertSplit", kind = "ui"}}},
|
||||
[2] = {{bold = true, foreground = Screen.colors.Blue1},
|
||||
{{hi_name = "NonText", ui_name = "EndOfBuffer", kind = "ui"}}},
|
||||
[3] = {{bold = true, reverse = true},
|
||||
{{hi_name = "StatusLine", ui_name = "StatusLine", kind = "ui"}}} ,
|
||||
[4] = {{reverse = true},
|
||||
{{hi_name = "StatusLineNC", ui_name = "StatusLineNC" , kind = "ui"}}},
|
||||
[5] = {{}, {{hi_name = "StatusLine", ui_name = "StatusLine", kind = "ui"}}},
|
||||
[6] = {{}, {{hi_name = "StatusLineNC", ui_name = "StatusLineNC", kind = "ui"}}},
|
||||
})
|
||||
command("hi clear VertSplit")
|
||||
command("vsplit")
|
||||
|
||||
screen:expect([[
|
||||
^ {1:│} |
|
||||
{2:~ }{1:│}{2:~ }|
|
||||
{2:~ }{1:│}{2:~ }|
|
||||
{2:~ }{1:│}{2:~ }|
|
||||
{2:~ }{1:│}{2:~ }|
|
||||
{2:~ }{1:│}{2:~ }|
|
||||
{3:[No Name] }{4:[No Name] }|
|
||||
|
|
||||
]])
|
||||
|
||||
command("hi clear StatusLine | hi clear StatuslineNC")
|
||||
screen:expect([[
|
||||
^ {1:│} |
|
||||
{2:~ }{1:│}{2:~ }|
|
||||
{2:~ }{1:│}{2:~ }|
|
||||
{2:~ }{1:│}{2:~ }|
|
||||
{2:~ }{1:│}{2:~ }|
|
||||
{2:~ }{1:│}{2:~ }|
|
||||
{5:[No Name] }{6:[No Name] }|
|
||||
|
|
||||
]])
|
||||
|
||||
-- redrawing is done even if visible highlights didn't change
|
||||
command("wincmd w")
|
||||
screen:expect([[
|
||||
{1:│}^ |
|
||||
{2:~ }{1:│}{2:~ }|
|
||||
{2:~ }{1:│}{2:~ }|
|
||||
{2:~ }{1:│}{2:~ }|
|
||||
{2:~ }{1:│}{2:~ }|
|
||||
{2:~ }{1:│}{2:~ }|
|
||||
{6:[No Name] }{5:[No Name] }|
|
||||
|
|
||||
]])
|
||||
|
||||
end)
|
||||
|
||||
it("work with window-local highlights", function()
|
||||
screen:set_default_attr_ids({
|
||||
[1] = {{foreground = Screen.colors.Brown}, {{hi_name = "LineNr", ui_name = "LineNr", kind = "ui"}}},
|
||||
[2] = {{bold = true, foreground = Screen.colors.Blue1}, {{hi_name = "NonText", ui_name = "EndOfBuffer", kind = "ui"}}},
|
||||
[3] = {{bold = true, reverse = true}, {{hi_name = "StatusLine", ui_name = "StatusLine", kind = "ui"}}},
|
||||
[4] = {{reverse = true}, {{hi_name = "StatusLineNC", ui_name = "StatusLineNC", kind = "ui"}}},
|
||||
[5] = {{background = Screen.colors.Red, foreground = Screen.colors.Grey100}, {{hi_name = "ErrorMsg", ui_name = "LineNr", kind = "ui"}}},
|
||||
[6] = {{bold = true, reverse = true}, {{hi_name = "MsgSeparator", ui_name = "Normal", kind = "ui"}}},
|
||||
[7] = {{foreground = Screen.colors.Brown, bold = true, reverse = true}, {6, 1}},
|
||||
[8] = {{foreground = Screen.colors.Blue1, bold = true, reverse = true}, {6, 2}},
|
||||
[9] = {{bold = true, foreground = Screen.colors.Brown}, {{hi_name = "Statement", ui_name = "NormalNC", kind = "ui"}}},
|
||||
[10] = {{bold = true, foreground = Screen.colors.Brown}, {9, 1}},
|
||||
[11] = {{bold = true, foreground = Screen.colors.Blue1}, {9, 2}}
|
||||
})
|
||||
|
||||
command("set number")
|
||||
command("split")
|
||||
-- NormalNC is not applied if not set, to avoid spurious redraws
|
||||
screen:expect([[
|
||||
{1: 1 }^ |
|
||||
{2:~ }|
|
||||
{2:~ }|
|
||||
{3:[No Name] }|
|
||||
{1: 1 } |
|
||||
{2:~ }|
|
||||
{4:[No Name] }|
|
||||
|
|
||||
]])
|
||||
|
||||
command("set winhl=LineNr:ErrorMsg")
|
||||
screen:expect([[
|
||||
{5: 1 }^ |
|
||||
{2:~ }|
|
||||
{2:~ }|
|
||||
{3:[No Name] }|
|
||||
{1: 1 } |
|
||||
{2:~ }|
|
||||
{4:[No Name] }|
|
||||
|
|
||||
]])
|
||||
|
||||
command("set winhl=Normal:MsgSeparator,NormalNC:Statement")
|
||||
screen:expect([[
|
||||
{7: 1 }{6:^ }|
|
||||
{8:~ }|
|
||||
{8:~ }|
|
||||
{3:[No Name] }|
|
||||
{1: 1 } |
|
||||
{2:~ }|
|
||||
{4:[No Name] }|
|
||||
|
|
||||
]])
|
||||
|
||||
command("wincmd w")
|
||||
screen:expect([[
|
||||
{10: 1 }{9: }|
|
||||
{11:~ }|
|
||||
{11:~ }|
|
||||
{4:[No Name] }|
|
||||
{1: 1 }^ |
|
||||
{2:~ }|
|
||||
{3:[No Name] }|
|
||||
|
|
||||
]])
|
||||
end)
|
||||
|
||||
it("work with :terminal", function()
|
||||
screen:set_default_attr_ids({
|
||||
[1] = {{}, {{hi_name = "TermCursorNC", ui_name = "TermCursorNC", kind = "ui"}}},
|
||||
[2] = {{special = Screen.colors.Grey0, foreground = 52479}, {{kind = "term"}}},
|
||||
[3] = {{special = Screen.colors.Grey0, bold = true, foreground = 52479}, {{kind = "term"}}},
|
||||
[4] = {{special = Screen.colors.Grey0, foreground = 52479}, {2, 1}},
|
||||
[5] = {{special = Screen.colors.Grey0, foreground = 4259839}, {{kind = "term"}}},
|
||||
[6] = {{special = Screen.colors.Grey0, foreground = 4259839}, {5, 1}},
|
||||
})
|
||||
command('enew | call termopen(["'..nvim_dir..'/tty-test"])')
|
||||
screen:expect([[
|
||||
^tty ready |
|
||||
{1: } |
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
]])
|
||||
|
||||
thelpers.feed_data('x ')
|
||||
thelpers.set_fg(45)
|
||||
thelpers.feed_data('y ')
|
||||
thelpers.set_bold()
|
||||
thelpers.feed_data('z\n')
|
||||
-- TODO(bfredl): check if this distinction makes sense
|
||||
if iswin() then
|
||||
screen:expect([[
|
||||
^tty ready |
|
||||
x {5:y z} |
|
||||
{1: } |
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
]])
|
||||
else
|
||||
screen:expect([[
|
||||
^tty ready |
|
||||
x {2:y }{3:z} |
|
||||
{1: } |
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
]])
|
||||
end
|
||||
|
||||
thelpers.feed_termcode("[A")
|
||||
thelpers.feed_termcode("[2C")
|
||||
if iswin() then
|
||||
screen:expect([[
|
||||
^tty ready |
|
||||
x {6:y}{5: z} |
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
]])
|
||||
else
|
||||
screen:expect([[
|
||||
^tty ready |
|
||||
x {4:y}{2: }{3:z} |
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
]])
|
||||
end
|
||||
end)
|
||||
|
||||
it("can use independent cterm and rgb colors", function()
|
||||
-- tell test module to save all attributes (doesn't change nvim options)
|
||||
screen:set_hlstate_cterm(true)
|
||||
|
||||
screen:set_default_attr_ids({
|
||||
[1] = {{bold = true, foreground = Screen.colors.Blue1}, {foreground = 12}, {{hi_name = "NonText", ui_name = "EndOfBuffer", kind = "ui"}}},
|
||||
[2] = {{reverse = true, foreground = Screen.colors.Red}, {foreground = 10, italic=true}, {{hi_name = "NonText", ui_name = "EndOfBuffer", kind = "ui"}}},
|
||||
})
|
||||
screen:expect([[
|
||||
^ |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
|
|
||||
]])
|
||||
|
||||
command("hi NonText guifg=Red gui=reverse ctermfg=Green cterm=italic")
|
||||
screen:expect([[
|
||||
^ |
|
||||
{2:~ }|
|
||||
{2:~ }|
|
||||
{2:~ }|
|
||||
{2:~ }|
|
||||
{2:~ }|
|
||||
{2:~ }|
|
||||
|
|
||||
]])
|
||||
|
||||
end)
|
||||
end)
|
|
@ -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,10 @@ function Screen.new(width, height)
|
|||
_default_attr_ignore = nil,
|
||||
_mouse_enabled = true,
|
||||
_attrs = {},
|
||||
_hl_info = {},
|
||||
_attr_table = {[0]={{},{}}},
|
||||
_clear_attrs = {},
|
||||
_new_attrs = false,
|
||||
_cursor = {
|
||||
row = 1, col = 1
|
||||
},
|
||||
|
@ -159,10 +163,19 @@ function Screen:set_default_attr_ignore(attr_ignore)
|
|||
self._default_attr_ignore = attr_ignore
|
||||
end
|
||||
|
||||
function Screen:set_hlstate_cterm(val)
|
||||
self._hlstate_cterm = val
|
||||
end
|
||||
|
||||
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 +189,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.
|
||||
|
@ -210,6 +224,11 @@ function Screen:expect(expected, attr_ids, attr_ignore, condition, any)
|
|||
end
|
||||
local ids = attr_ids or self._default_attr_ids
|
||||
local ignore = attr_ignore or self._default_attr_ignore
|
||||
local id_to_index
|
||||
if self._options.ext_hlstate then
|
||||
id_to_index = self:hlstate_check_attrs(ids or {})
|
||||
end
|
||||
self._new_attrs = false
|
||||
self:wait(function()
|
||||
if condition ~= nil then
|
||||
local status, res = pcall(condition)
|
||||
|
@ -223,9 +242,14 @@ function Screen:expect(expected, attr_ids, attr_ignore, condition, any)
|
|||
.. ') differs from configured height(' .. self._height .. ') of Screen.')
|
||||
end
|
||||
|
||||
if self._options.ext_hlstate and self._new_attrs then
|
||||
id_to_index = self:hlstate_check_attrs(ids or {})
|
||||
end
|
||||
|
||||
local info = self._options.ext_hlstate and id_to_index or ids
|
||||
local actual_rows = {}
|
||||
for i = 1, self._height do
|
||||
actual_rows[i] = self:_row_repr(self._rows[i], ids, ignore)
|
||||
actual_rows[i] = self:_row_repr(self._rows[i], info, ignore)
|
||||
end
|
||||
|
||||
if expected == nil then
|
||||
|
@ -339,7 +363,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, hl_id = 0})
|
||||
end
|
||||
table.insert(rows, cols)
|
||||
end
|
||||
|
@ -353,14 +377,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 +407,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
|
||||
|
@ -425,6 +465,7 @@ function Screen:_handle_scroll(count)
|
|||
for j = left, right do
|
||||
target[j].text = source[j].text
|
||||
target[j].attrs = source[j].attrs
|
||||
target[j].hl_id = source[j].hl_id
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -434,6 +475,28 @@ 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._hl_info[id] = info
|
||||
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
|
||||
|
@ -442,9 +505,32 @@ function Screen:_handle_put(str)
|
|||
local cell = self._rows[self._cursor.row][self._cursor.col]
|
||||
cell.text = str
|
||||
cell.attrs = self._attrs
|
||||
cell.hl_id = -1
|
||||
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
|
||||
local hl_id = 0
|
||||
for _,item in ipairs(items) do
|
||||
local text, hl_id_cell, count = unpack(item)
|
||||
if hl_id_cell ~= nil then
|
||||
hl_id = hl_id_cell
|
||||
hl = self._attr_table[hl_id]
|
||||
end
|
||||
for _ = 1, (count or 1) do
|
||||
local cell = line[colpos]
|
||||
cell.text = text
|
||||
cell.hl_id = hl_id
|
||||
cell.attrs = hl
|
||||
colpos = colpos+1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Screen:_handle_bell()
|
||||
self.bell = true
|
||||
end
|
||||
|
@ -498,7 +584,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 +592,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
|
||||
|
@ -573,22 +663,33 @@ function Screen:print_snapshot(attrs, ignore)
|
|||
if ignore == nil then
|
||||
ignore = self._default_attr_ignore
|
||||
end
|
||||
local id_to_index = {}
|
||||
if attrs == nil then
|
||||
attrs = {}
|
||||
if self._default_attr_ids ~= nil then
|
||||
for i, a in pairs(self._default_attr_ids) do
|
||||
attrs[i] = a
|
||||
end
|
||||
if self._options.ext_hlstate then
|
||||
id_to_index = self:hlstate_check_attrs(attrs)
|
||||
end
|
||||
end
|
||||
|
||||
if ignore ~= true then
|
||||
for i = 1, self._height do
|
||||
local row = self._rows[i]
|
||||
for j = 1, self._width do
|
||||
local attr = row[j].attrs
|
||||
if self:_attr_index(attrs, attr) == nil and self:_attr_index(ignore, attr) == nil then
|
||||
if not self:_equal_attrs(attr, {}) then
|
||||
table.insert(attrs, attr)
|
||||
if self._options.ext_hlstate then
|
||||
local hl_id = row[j].hl_id
|
||||
if hl_id ~= 0 then
|
||||
self:_insert_hl_id(attrs, id_to_index, hl_id)
|
||||
end
|
||||
else
|
||||
local attr = row[j].attrs
|
||||
if self:_attr_index(attrs, attr) == nil and self:_attr_index(ignore, attr) == nil then
|
||||
if not self:_equal_attrs(attr, {}) then
|
||||
table.insert(attrs, attr)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -597,8 +698,9 @@ function Screen:print_snapshot(attrs, ignore)
|
|||
end
|
||||
|
||||
local rv = {}
|
||||
local info = self._options.ext_hlstate and id_to_index or attrs
|
||||
for i = 1, self._height do
|
||||
table.insert(rv, " "..self:_row_repr(self._rows[i],attrs, ignore).."|")
|
||||
table.insert(rv, " "..self:_row_repr(self._rows[i], info, ignore).."|")
|
||||
end
|
||||
local attrstrs = {}
|
||||
local alldefault = true
|
||||
|
@ -606,7 +708,12 @@ function Screen:print_snapshot(attrs, ignore)
|
|||
if self._default_attr_ids == nil or self._default_attr_ids[i] ~= a then
|
||||
alldefault = false
|
||||
end
|
||||
local dict = "{"..self:_pprint_attrs(a).."}"
|
||||
local dict
|
||||
if self._options.ext_hlstate then
|
||||
dict = self:_pprint_hlstate(a)
|
||||
else
|
||||
dict = "{"..self:_pprint_attrs(a).."}"
|
||||
end
|
||||
table.insert(attrstrs, "["..tostring(i).."] = "..dict)
|
||||
end
|
||||
local attrstr = "{"..table.concat(attrstrs, ", ").."}"
|
||||
|
@ -620,6 +727,117 @@ function Screen:print_snapshot(attrs, ignore)
|
|||
io.stdout:flush()
|
||||
end
|
||||
|
||||
function Screen:_insert_hl_id(attrs, id_to_index, hl_id)
|
||||
if id_to_index[hl_id] ~= nil then
|
||||
return id_to_index[hl_id]
|
||||
end
|
||||
local raw_info = self._hl_info[hl_id]
|
||||
local info = {}
|
||||
if #raw_info > 1 then
|
||||
for i, item in ipairs(raw_info) do
|
||||
info[i] = self:_insert_hl_id(attrs, id_to_index, item.id)
|
||||
end
|
||||
else
|
||||
info[1] = {}
|
||||
for k, v in pairs(raw_info[1]) do
|
||||
if k ~= "id" then
|
||||
info[1][k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local entry = self._attr_table[hl_id]
|
||||
local attrval
|
||||
if self._hlstate_cterm then
|
||||
attrval = {entry[1], entry[2], info} -- unpack() doesn't work
|
||||
else
|
||||
attrval = {entry[1], info}
|
||||
end
|
||||
|
||||
|
||||
table.insert(attrs, attrval)
|
||||
id_to_index[hl_id] = #attrs
|
||||
return #attrs
|
||||
end
|
||||
|
||||
function Screen:hlstate_check_attrs(attrs)
|
||||
local id_to_index = {}
|
||||
for i = 1,#self._attr_table do
|
||||
local iinfo = self._hl_info[i]
|
||||
local matchinfo = {}
|
||||
if #iinfo > 1 then
|
||||
for k,item in ipairs(iinfo) do
|
||||
matchinfo[k] = id_to_index[item.id]
|
||||
end
|
||||
else
|
||||
matchinfo = iinfo
|
||||
end
|
||||
for k,v in pairs(attrs) do
|
||||
local attr, info, attr_rgb, attr_cterm
|
||||
if self._hlstate_cterm then
|
||||
attr_rgb, attr_cterm, info = unpack(v)
|
||||
attr = {attr_rgb, attr_cterm}
|
||||
else
|
||||
attr, info = unpack(v)
|
||||
end
|
||||
if self:_equal_attr_def(attr, self._attr_table[i]) then
|
||||
if #info == #matchinfo then
|
||||
local match = false
|
||||
if #info == 1 then
|
||||
if self:_equal_info(info[1],matchinfo[1]) then
|
||||
match = true
|
||||
end
|
||||
else
|
||||
match = true
|
||||
for j = 1,#info do
|
||||
if info[j] ~= matchinfo[j] then
|
||||
match = false
|
||||
end
|
||||
end
|
||||
end
|
||||
if match then
|
||||
id_to_index[i] = k
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return id_to_index
|
||||
end
|
||||
|
||||
|
||||
function Screen:_pprint_hlstate(item)
|
||||
--print(require('inspect')(item))
|
||||
local attrdict = "{"..self:_pprint_attrs(item[1]).."}, "
|
||||
local attrdict2, hlinfo
|
||||
if self._hlstate_cterm then
|
||||
attrdict2 = "{"..self:_pprint_attrs(item[2]).."}, "
|
||||
hlinfo = item[3]
|
||||
else
|
||||
attrdict2 = ""
|
||||
hlinfo = item[2]
|
||||
end
|
||||
local descdict = "{"..self:_pprint_hlinfo(hlinfo).."}"
|
||||
return "{"..attrdict..attrdict2..descdict.."}"
|
||||
end
|
||||
|
||||
function Screen:_pprint_hlinfo(states)
|
||||
if #states == 1 then
|
||||
local items = {}
|
||||
for f, v in pairs(states[1]) do
|
||||
local desc = tostring(v)
|
||||
if type(v) == type("") then
|
||||
desc = '"'..desc..'"'
|
||||
end
|
||||
table.insert(items, f.." = "..desc)
|
||||
end
|
||||
return "{"..table.concat(items, ", ").."}"
|
||||
else
|
||||
return table.concat(states, ", ")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function Screen:_pprint_attrs(attrs)
|
||||
local items = {}
|
||||
for f, v in pairs(attrs) do
|
||||
|
@ -643,32 +861,53 @@ local function backward_find_meaningful(tbl, from) -- luacheck: no unused
|
|||
return from
|
||||
end
|
||||
|
||||
function Screen:_get_attr_id(attr_ids, ignore, attrs)
|
||||
function Screen:_get_attr_id(attr_ids, ignore, attrs, hl_id)
|
||||
if not attr_ids then
|
||||
return
|
||||
end
|
||||
for id, a in pairs(attr_ids) do
|
||||
if self:_equal_attrs(a, attrs) then
|
||||
return id
|
||||
end
|
||||
|
||||
if self._options.ext_hlstate then
|
||||
local id = attr_ids[hl_id]
|
||||
if id ~= nil or hl_id == 0 then
|
||||
return id
|
||||
end
|
||||
return "UNEXPECTED "..self:_pprint_attrs(self._attr_table[hl_id][1])
|
||||
else
|
||||
for id, a in pairs(attr_ids) do
|
||||
if self:_equal_attrs(a, attrs) then
|
||||
return id
|
||||
end
|
||||
end
|
||||
if self:_equal_attrs(attrs, {}) or
|
||||
ignore == true or self:_attr_index(ignore, attrs) ~= nil then
|
||||
-- ignore this attrs
|
||||
return nil
|
||||
end
|
||||
return "UNEXPECTED "..self:_pprint_attrs(attrs)
|
||||
end
|
||||
if self:_equal_attrs(attrs, {}) or
|
||||
ignore == true or self:_attr_index(ignore, attrs) ~= nil then
|
||||
-- ignore this attrs
|
||||
return nil
|
||||
end
|
||||
|
||||
function Screen:_equal_attr_def(a, b)
|
||||
if self._hlstate_cterm then
|
||||
return self:_equal_attrs(a[1],b[1]) and self:_equal_attrs(a[2],b[2])
|
||||
else
|
||||
return self:_equal_attrs(a,b[1])
|
||||
end
|
||||
return "UNEXPECTED "..self:_pprint_attrs(attrs)
|
||||
end
|
||||
|
||||
function Screen:_equal_attrs(a, b)
|
||||
return a.bold == b.bold and a.standout == b.standout and
|
||||
a.underline == b.underline and a.undercurl == b.undercurl and
|
||||
a.italic == b.italic and a.reverse == b.reverse and
|
||||
a.foreground == b.foreground and
|
||||
a.background == b.background and
|
||||
a.foreground == b.foreground and a.background == b.background and
|
||||
a.special == b.special
|
||||
end
|
||||
|
||||
function Screen:_equal_info(a, b)
|
||||
return a.kind == b.kind and a.hi_name == b.hi_name and
|
||||
a.ui_name == b.ui_name
|
||||
end
|
||||
|
||||
function Screen:_attr_index(attrs, attr)
|
||||
if not attrs then
|
||||
return nil
|
||||
|
|
|
@ -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