Merge pull request #9064 from bfredl/uidoc

UI: rename ext_newgrid to ext_linegrid and add --embed UI startup recommendations
This commit is contained in:
Björn Linse
2018-10-02 10:55:47 +02:00
committed by GitHub
15 changed files with 148 additions and 79 deletions

View File

@@ -358,6 +358,8 @@ argument.
instance a `nvim_get_api_info` call so that UI features can be instance a `nvim_get_api_info` call so that UI features can be
safely detected by the UI before attaching. safely detected by the UI before attaching.
See |ui-startup| for more information about UI startup.
To embed nvim without using the UI protocol, `--headless` should To embed nvim without using the UI protocol, `--headless` should
be supplied together with `--embed`. Then initialization is be supplied together with `--embed`. Then initialization is
performed without waiting for an UI. This is also equivalent performed without waiting for an UI. This is also equivalent

View File

@@ -16,10 +16,12 @@ RPC API. The UI model consists of a terminal-like grid with a single,
monospace font size. Some elements (UI "widgets") can be drawn separately from monospace font size. Some elements (UI "widgets") can be drawn separately from
the grid ("externalized"). the grid ("externalized").
*ui-options* *ui-options*
After connecting to Nvim (usually a spawned, embedded instance) use the The |nvim_ui_attach()| API method is used to tell Nvim that your program wants to
|nvim_ui_attach()| API method to tell Nvim that your program wants to draw the draw the Nvim screen grid with a size of width × height cells. This is typically
Nvim screen grid with a size of width × height cells. `options` must be done by an embedder, see |ui-startup| below for details, but an UI can also
connect to a running nvim instance and invoke this method. `options` must be
a dictionary with these (optional) keys: a dictionary with these (optional) keys:
`rgb` Decides the color format. *ui-rgb* `rgb` Decides the color format. *ui-rgb*
Set true (default) for 24-bit RGB colors. Set true (default) for 24-bit RGB colors.
@@ -29,7 +31,7 @@ a dictionary with these (optional) keys:
`ext_tabline` Externalize the tabline. |ui-tabline| `ext_tabline` Externalize the tabline. |ui-tabline|
`ext_cmdline` Externalize the cmdline. |ui-cmdline| `ext_cmdline` Externalize the cmdline. |ui-cmdline|
`ext_wildmenu` Externalize the wildmenu. |ui-wildmenu| `ext_wildmenu` Externalize the wildmenu. |ui-wildmenu|
`ext_newgrid` Use new revision of the grid events. |ui-newgrid| `ext_linegrid` Use new revision of the grid events. |ui-linegrid|
`ext_hlstate` Use detailed highlight state. |ui-hlstate| `ext_hlstate` Use detailed highlight state. |ui-hlstate|
Specifying a non-existent option is an error. UIs can check the |api-metadata| Specifying a non-existent option is an error. UIs can check the |api-metadata|
@@ -43,22 +45,26 @@ Nvim sends msgpack-rpc notifications to all attached UIs, with method name
Each update event is itself an array whose first element is the event name and Each update event is itself an array whose first element is the event name and
remaining elements are event-parameter tuples. This allows multiple events of remaining elements are event-parameter tuples. This allows multiple events of
the same kind to be sent in a row without the event name being repeated. This the same kind to be sent in a row without the event name being repeated. This
batching is mostly used for "put", because each "put" event puts contents in batching is mostly used for "grid_line", because each "grid_line" event puts
one screen cell, but clients must be prepared for multiple argument sets being contents in one screen line, but clients must be prepared for multiple argument
batched for all event kinds. sets being batched for all event kinds.
Events must be handled in-order. The user should only see the updated screen Events must be handled in-order. A "flush" event is sent when nvim is done
state after all events in the same "redraw" batch are processed (not any redrawing the entire screen (so that all windows have a consistent view of
intermediate state after processing only part of the array). buffer state, options etc). Clients should be prepared that several "redraw"
batches are sent before the entire screen has been redrawn, and only the last
batch will end in "flush". The user should only see the final state when
"flush" is sent, and not any intermediate state after processing only part of
the batch array, nor after a batch not ending with "flush".
By default, Nvim sends |ui-global| and |ui-grid-old| events; these suffice to 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 implement a terminal-like interface. However there are two revisions of the
grid part of the protocol. The newer revision |ui-newgrid|, enabled by grid part of the protocol. The newer revision |ui-linegrid|, enabled by
`ext_newgrid` option, has some improvements, such as a more efficient `ext_linegrid` option, has a more effecient representation of text (especially
representation of highlighted text, simplified events and room for futher highlighted text), and room for futher enhancements that will use
enhancements that will use multiple grids. The older revision is available and multiple grids. The older revision is available and used by default only for
used by default only for backwards compatibility reasons. New UIs are strongly backwards compatibility reasons. New UIs are strongly recommended to use
recommended to use |ui-newgrid|, as further protocol extensions will require it. |ui-linegrid|, as further protocol extensions will require it.
Nvim optionally sends screen elements "semantically" as structured events Nvim optionally sends screen elements "semantically" as structured events
instead of raw grid-lines, controlled by |ui-ext-options|. The UI must present instead of raw grid-lines, controlled by |ui-ext-options|. The UI must present
@@ -68,9 +74,45 @@ 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, to existing update kinds. Clients must be prepared to ignore such extensions,
for forward-compatibility. |api-contract| for forward-compatibility. |api-contract|
==============================================================================
UI startup *ui-startup*
Nvim defines a standard procedure for how an embedding UI should interact with
the startup phase of Nvim. When spawning the nvim process, use the |--embed| flag
but not the |--headless| flag. The started Nvim process will pause before loading
startup files and reading buffers, and give the UI a chance to invoke requests
to do early initialization. As soon as the UI invokes |nvim_ui_attach()|, the
startup will continue.
A simple UI only need to do a single |nvim_ui_attach()| request and then
be prepared to handle any UI event. A more featureful UI, which might need
additional configuration of the nvim process, should use the following startup
procedure:
1. Invoke |nvim_get_api_info()|, if this is needed to setup the client library
and/or to get the list of supported UI extensions.
2. At this time, any configuration that should be happen before init.vim
loading should be done. Buffers and windows are not available at this
point, but this could be used to set |g:| variables visible to init.vim
3. If the UI wants to do additional setup after the init.vim file was loaded
register an autocmd for VimEnter at this point: >
nvim_command("autocmd VimEnter * call rpcrequest(1, 'vimenter')")
<4. Now invoke |nvim_ui_attach()|. The UI will need to handle keyboard input
at this point, as sourcing init.vim and loading buffers might lead to
blocking prompts.
5. If step 3 was used, nvim will send a blocking "vimenter" request to the
UI. Inside this request handler, the UI can safely do any initialization
before entering normal mode, for instance reading variables set by
init.vim.
============================================================================== ==============================================================================
Global Events *ui-global* Global Events *ui-global*
The following events will always be available, and describe global state of
the editor.
["set_title", title] ["set_title", title]
["set_icon", icon] ["set_icon", icon]
Set the window title, and icon (minimized) window title, respectively. Set the window title, and icon (minimized) window title, respectively.
@@ -144,15 +186,15 @@ Global Events *ui-global*
would conflict with other usages of the mouse. It is safe for a client would conflict with other usages of the mouse. It is safe for a client
to ignore this and always send mouse events. to ignore this and always send mouse events.
["busy_on"] ["busy_start"]
["busy_off"] ["busy_stop"]
Nvim started or stopped being busy, and possibly not responsive to Nvim started or stopped being busy, and possibly not responsive to
user input. This could be indicated to the user by hiding the cursor. user input. This could be indicated to the user by hiding the cursor.
["suspend"] ["suspend"]
|:suspend| command or |CTRL-Z| mapping is used. A terminal client (or other |:suspend| command or |CTRL-Z| mapping is used. A terminal client (or
client where it makes sense) could suspend itself. Other clients can another client where it makes sense) could suspend itself. Other
safely ignore it. clients can safely ignore it.
["update_menu"] ["update_menu"]
The menu mappings changed. The menu mappings changed.
@@ -161,16 +203,24 @@ Global Events *ui-global*
["visual_bell"] ["visual_bell"]
Notify the user with an audible or visual bell, respectively. Notify the user with an audible or visual bell, respectively.
============================================================================== ["flush"]
Grid Events (new revision) *ui-newgrid* Nvim is done redrawing the screen. For an implementation that renders
to an internal buffer, this is the time to display the redrawn parts
to the user.
These events are used if `ext_newgrid` option is set (recommended for all new ==============================================================================
UIs). Grid Events (line-based) *ui-linegrid*
These events are used if `ext_linegrid` option is set (recommended for all new
UIs). The biggest change compared to previous revision is to use a single
event `grid_line` to update the contents of a screen line (where the old
protocol used a combination of cursor, highlight and text events)
Most of these events take a `grid` index as first parameter. Grid 1 is the 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 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` than that will be defined by future extensions. Just activating the
option by itself will never cause any additional grids to be created. `ext_linegrid` option by itself will never cause any additional grids to be
created.
Highlight attribute groups are predefined. UIs should maintain a table to map Highlight attribute groups are predefined. UIs should maintain a table to map
numerical highlight `id`:s to the actual attributes. numerical highlight `id`:s to the actual attributes.
@@ -299,10 +349,10 @@ numerical highlight `id`:s to the actual attributes.
from `set_scroll_region` which was end-inclusive. from `set_scroll_region` which was end-inclusive.
============================================================================== ==============================================================================
Grid Events (first revision) *ui-grid-old* Legacy Grid Events (cell based) *ui-grid-old*
This is an older representation of the screen grid, used if `ext_newgrid` This is an older representation of the screen grid, used if `ext_linegrid`
option is not set. option is not set. New UIs should use |ui-linegrid|.
["resize", width, height] ["resize", width, height]
The grid is resized to `width` and `height` cells. The grid is resized to `width` and `height` cells.
@@ -389,9 +439,8 @@ option is not set.
============================================================================== ==============================================================================
Detailed highlight state Extension *ui-hlstate* Detailed highlight state Extension *ui-hlstate*
Only sent if `ext_hlstate` option is set in |ui-options|. `ext_hlstate` implies Only sent if `ext_hlstate` option is set in |ui-options|. `ext_hlstate` implies
`ext_newgrid`. `ext_linegrid`.
By default, nvim will only describe grid cells using the final calculated 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|. higlight attributes, as described by the dict keys in |ui-event-highlight_set|.

View File

@@ -127,7 +127,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height,
} }
if (ui->ui_ext[kUIHlState]) { if (ui->ui_ext[kUIHlState]) {
ui->ui_ext[kUINewgrid] = true; ui->ui_ext[kUILinegrid] = true;
} }
UIData *data = xmalloc(sizeof(UIData)); UIData *data = xmalloc(sizeof(UIData));
@@ -227,11 +227,11 @@ static void ui_set_option(UI *ui, bool init, String name, Object value,
return; return;
} }
bool boolval = value.data.boolean; bool boolval = value.data.boolean;
if (!init && i == kUINewgrid && boolval != ui->ui_ext[i]) { if (!init && i == kUILinegrid && boolval != ui->ui_ext[i]) {
// There shouldn't be a reason for an UI to do this ever // There shouldn't be a reason for an UI to do this ever
// so explicitly don't support this. // so explicitly don't support this.
api_set_error(error, kErrorTypeValidation, api_set_error(error, kErrorTypeValidation,
"ext_newgrid option cannot be changed"); "ext_linegrid option cannot be changed");
} }
ui->ui_ext[i] = boolval; ui->ui_ext[i] = boolval;
if (!init) { if (!init) {
@@ -271,10 +271,10 @@ static void push_call(UI *ui, const char *name, Array args)
static void remote_ui_grid_clear(UI *ui, Integer grid) static void remote_ui_grid_clear(UI *ui, Integer grid)
{ {
Array args = ARRAY_DICT_INIT; Array args = ARRAY_DICT_INIT;
if (ui->ui_ext[kUINewgrid]) { if (ui->ui_ext[kUILinegrid]) {
ADD(args, INTEGER_OBJ(grid)); ADD(args, INTEGER_OBJ(grid));
} }
const char *name = ui->ui_ext[kUINewgrid] ? "grid_clear" : "clear"; const char *name = ui->ui_ext[kUILinegrid] ? "grid_clear" : "clear";
push_call(ui, name, args); push_call(ui, name, args);
} }
@@ -282,12 +282,12 @@ static void remote_ui_grid_resize(UI *ui, Integer grid,
Integer width, Integer height) Integer width, Integer height)
{ {
Array args = ARRAY_DICT_INIT; Array args = ARRAY_DICT_INIT;
if (ui->ui_ext[kUINewgrid]) { if (ui->ui_ext[kUILinegrid]) {
ADD(args, INTEGER_OBJ(grid)); ADD(args, INTEGER_OBJ(grid));
} }
ADD(args, INTEGER_OBJ(width)); ADD(args, INTEGER_OBJ(width));
ADD(args, INTEGER_OBJ(height)); ADD(args, INTEGER_OBJ(height));
const char *name = ui->ui_ext[kUINewgrid] ? "grid_resize" : "resize"; const char *name = ui->ui_ext[kUILinegrid] ? "grid_resize" : "resize";
push_call(ui, name, args); push_call(ui, name, args);
} }
@@ -295,7 +295,7 @@ static void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top,
Integer bot, Integer left, Integer right, Integer bot, Integer left, Integer right,
Integer rows, Integer cols) Integer rows, Integer cols)
{ {
if (ui->ui_ext[kUINewgrid]) { if (ui->ui_ext[kUILinegrid]) {
Array args = ARRAY_DICT_INIT; Array args = ARRAY_DICT_INIT;
ADD(args, INTEGER_OBJ(grid)); ADD(args, INTEGER_OBJ(grid));
ADD(args, INTEGER_OBJ(top)); ADD(args, INTEGER_OBJ(top));
@@ -341,7 +341,7 @@ static void remote_ui_default_colors_set(UI *ui, Integer rgb_fg,
push_call(ui, "default_colors_set", args); push_call(ui, "default_colors_set", args);
// Deprecated // Deprecated
if (!ui->ui_ext[kUINewgrid]) { if (!ui->ui_ext[kUILinegrid]) {
args = (Array)ARRAY_DICT_INIT; args = (Array)ARRAY_DICT_INIT;
ADD(args, INTEGER_OBJ(ui->rgb ? rgb_fg : cterm_fg - 1)); ADD(args, INTEGER_OBJ(ui->rgb ? rgb_fg : cterm_fg - 1));
push_call(ui, "update_fg", args); push_call(ui, "update_fg", args);
@@ -359,7 +359,7 @@ static void remote_ui_default_colors_set(UI *ui, Integer rgb_fg,
static void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, static void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs,
HlAttrs cterm_attrs, Array info) HlAttrs cterm_attrs, Array info)
{ {
if (!ui->ui_ext[kUINewgrid]) { if (!ui->ui_ext[kUILinegrid]) {
return; return;
} }
Array args = ARRAY_DICT_INIT; Array args = ARRAY_DICT_INIT;
@@ -397,7 +397,7 @@ static void remote_ui_highlight_set(UI *ui, int id)
static void remote_ui_grid_cursor_goto(UI *ui, Integer grid, Integer row, static void remote_ui_grid_cursor_goto(UI *ui, Integer grid, Integer row,
Integer col) Integer col)
{ {
if (ui->ui_ext[kUINewgrid]) { if (ui->ui_ext[kUILinegrid]) {
Array args = ARRAY_DICT_INIT; Array args = ARRAY_DICT_INIT;
ADD(args, INTEGER_OBJ(grid)); ADD(args, INTEGER_OBJ(grid));
ADD(args, INTEGER_OBJ(row)); ADD(args, INTEGER_OBJ(row));
@@ -442,7 +442,7 @@ static void remote_ui_raw_line(UI *ui, Integer grid, Integer row,
const sattr_T *attrs) const sattr_T *attrs)
{ {
UIData *data = ui->data; UIData *data = ui->data;
if (ui->ui_ext[kUINewgrid]) { if (ui->ui_ext[kUILinegrid]) {
Array args = ARRAY_DICT_INIT; Array args = ARRAY_DICT_INIT;
ADD(args, INTEGER_OBJ(grid)); ADD(args, INTEGER_OBJ(grid));
ADD(args, INTEGER_OBJ(row)); ADD(args, INTEGER_OBJ(row));
@@ -508,9 +508,10 @@ static void remote_ui_flush(UI *ui)
{ {
UIData *data = ui->data; UIData *data = ui->data;
if (data->buffer.size > 0) { if (data->buffer.size > 0) {
if (!ui->ui_ext[kUINewgrid]) { if (!ui->ui_ext[kUILinegrid]) {
remote_ui_cursor_goto(ui, data->cursor_row, data->cursor_col); remote_ui_cursor_goto(ui, data->cursor_row, data->cursor_col);
} }
push_call(ui, "flush", (Array)ARRAY_DICT_INIT);
rpc_send_event(data->channel_id, "redraw", data->buffer); rpc_send_event(data->channel_id, "redraw", data->buffer);
data->buffer = (Array)ARRAY_DICT_INIT; data->buffer = (Array)ARRAY_DICT_INIT;
} }
@@ -549,7 +550,7 @@ static Array translate_firstarg(UI *ui, Array args)
static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed) static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed)
{ {
if (!ui->ui_ext[kUINewgrid]) { if (!ui->ui_ext[kUILinegrid]) {
// the representation of highlights in cmdline changed, translate back // the representation of highlights in cmdline changed, translate back
// never consumes args // never consumes args
if (strequal(name, "cmdline_show")) { if (strequal(name, "cmdline_show")) {

View File

@@ -63,7 +63,7 @@ void set_scroll_region(Integer top, Integer bot, Integer left, Integer right)
void scroll(Integer count) void scroll(Integer count)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
// Second revison of the grid protocol, used with ext_newgrid ui option // Second revison of the grid protocol, used with ext_linegrid ui option
void default_colors_set(Integer rgb_fg, Integer rgb_bg, Integer rgb_sp, void default_colors_set(Integer rgb_fg, Integer rgb_bg, Integer rgb_sp,
Integer cterm_fg, Integer cterm_bg) Integer cterm_fg, Integer cterm_bg)
FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL; FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL;

View File

@@ -6884,7 +6884,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
// "fg", which have been changed now. // "fg", which have been changed now.
highlight_attr_set_all(); highlight_attr_set_all();
if (!ui_is_external(kUINewgrid) && starting == 0) { if (!ui_is_external(kUILinegrid) && starting == 0) {
// Older UIs assume that we clear the screen after normal group is // Older UIs assume that we clear the screen after normal group is
// changed // changed
ui_refresh(); ui_refresh();

View File

@@ -156,7 +156,7 @@ UI *tui_start(void)
ui->raw_line = tui_raw_line; ui->raw_line = tui_raw_line;
memset(ui->ui_ext, 0, sizeof(ui->ui_ext)); memset(ui->ui_ext, 0, sizeof(ui->ui_ext));
ui->ui_ext[kUINewgrid] = true; ui->ui_ext[kUILinegrid] = true;
return ui_bridge_attach(ui, tui_main, tui_scheduler); return ui_bridge_attach(ui, tui_main, tui_scheduler);
} }

View File

@@ -15,7 +15,7 @@ typedef enum {
kUITabline, kUITabline,
kUIWildmenu, kUIWildmenu,
#define kUIGlobalCount (kUIWildmenu+1) #define kUIGlobalCount (kUIWildmenu+1)
kUINewgrid, kUILinegrid,
kUIHlState, kUIHlState,
kUIExtCount, kUIExtCount,
} UIExtension; } UIExtension;
@@ -25,7 +25,7 @@ EXTERN const char *ui_ext_names[] INIT(= {
"ext_popupmenu", "ext_popupmenu",
"ext_tabline", "ext_tabline",
"ext_wildmenu", "ext_wildmenu",
"ext_newgrid", "ext_linegrid",
"ext_hlstate", "ext_hlstate",
}); });

View File

@@ -156,6 +156,6 @@ describe("ui_options in metadata", function()
local api = helpers.call('api_info') local api = helpers.call('api_info')
local options = api.ui_options local options = api.ui_options
eq({'rgb', 'ext_cmdline', 'ext_popupmenu', eq({'rgb', 'ext_cmdline', 'ext_popupmenu',
'ext_tabline', 'ext_wildmenu', 'ext_newgrid', 'ext_hlstate'}, options) 'ext_tabline', 'ext_wildmenu', 'ext_linegrid', 'ext_hlstate'}, options)
end) end)
end) end)

View File

@@ -1254,7 +1254,7 @@ describe('API', function()
ext_popupmenu = false, ext_popupmenu = false,
ext_tabline = false, ext_tabline = false,
ext_wildmenu = false, ext_wildmenu = false,
ext_newgrid = screen._options.ext_newgrid or false, ext_linegrid = screen._options.ext_linegrid or false,
ext_hlstate=false, ext_hlstate=false,
height = 4, height = 4,
rgb = true, rgb = true,

View File

@@ -258,10 +258,10 @@ describe('tui', function()
feed_data(':echo map(nvim_list_uis(), {k,v -> sort(items(v))})\013') feed_data(':echo map(nvim_list_uis(), {k,v -> sort(items(v))})\013')
screen:expect([=[ screen:expect([=[
[[['ext_cmdline', v:false], ['ext_hlstate', v:fals| [[['ext_cmdline', v:false], ['ext_hlstate', v:fals|
e], ['ext_newgrid', v:true], ['ext_popupmenu', v:f| e], ['ext_linegrid', v:true], ['ext_popupmenu', v:|
alse], ['ext_tabline', v:false], ['ext_wildmenu', | false], ['ext_tabline', v:false], ['ext_wildmenu',|
v:false], ['height', 6], ['rgb', v:false], ['width| v:false], ['height', 6], ['rgb', v:false], ['widt|
', 50]]] | h', 50]]] |
{10:Press ENTER or type command to continue}{1: } | {10:Press ENTER or type command to continue}{1: } |
{3:-- TERMINAL --} | {3:-- TERMINAL --} |
]=]) ]=])

View File

@@ -4,13 +4,13 @@ local clear, feed = helpers.clear, helpers.feed
local source = helpers.source local source = helpers.source
local command = helpers.command local command = helpers.command
local function test_cmdline(newgrid) local function test_cmdline(linegrid)
local screen local screen
before_each(function() before_each(function()
clear() clear()
screen = Screen.new(25, 5) screen = Screen.new(25, 5)
screen:attach({rgb=true, ext_cmdline=true, ext_newgrid=newgrid}) screen:attach({rgb=true, ext_cmdline=true, ext_linegrid=linegrid})
screen:set_default_attr_ids({ screen:set_default_attr_ids({
[1] = {bold = true, foreground = Screen.colors.Blue1}, [1] = {bold = true, foreground = Screen.colors.Blue1},
[2] = {reverse = true}, [2] = {reverse = true},
@@ -608,7 +608,7 @@ local function test_cmdline(newgrid)
end) end)
end end
-- the representation of cmdline and cmdline_block contents changed with ext_newgrid -- the representation of cmdline and cmdline_block contents changed with ext_linegrid
-- (which uses indexed highlights) so make sure to test both -- (which uses indexed highlights) so make sure to test both
describe('ui/ext_cmdline', function() test_cmdline(true) end) describe('ui/ext_cmdline', function() test_cmdline(true) end)
describe('ui/ext_cmdline (legacy highlights)', function() test_cmdline(false) end) describe('ui/ext_cmdline (legacy highlights)', function() test_cmdline(false) end)

View File

@@ -5,14 +5,14 @@ local feed = helpers.feed
local eq = helpers.eq local eq = helpers.eq
local clear = helpers.clear local clear = helpers.clear
local function test_embed(ext_newgrid) local function test_embed(ext_linegrid)
local screen local screen
local function startup(...) local function startup(...)
clear{headless=false, args={...}} clear{headless=false, args={...}}
-- attach immediately after startup, for early UI -- attach immediately after startup, for early UI
screen = Screen.new(60, 8) screen = Screen.new(60, 8)
screen:attach{ext_newgrid=ext_newgrid} screen:attach{ext_linegrid=ext_linegrid}
screen:set_default_attr_ids({ screen:set_default_attr_ids({
[1] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, [1] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
[2] = {bold = true, foreground = Screen.colors.SeaGreen4}, [2] = {bold = true, foreground = Screen.colors.SeaGreen4},
@@ -77,5 +77,5 @@ local function test_embed(ext_newgrid)
end) end)
end end
describe('--embed UI on startup (ext_newgrid=true)', function() test_embed(true) end) describe('--embed UI on startup (ext_linegrid=true)', function() test_embed(true) end)
describe('--embed UI on startup (ext_newgrid=false)', function() test_embed(false) end) describe('--embed UI on startup (ext_linegrid=false)', function() test_embed(false) end)

View File

@@ -30,15 +30,15 @@ describe('ui receives option updates', function()
ext_popupmenu=false, ext_popupmenu=false,
ext_tabline=false, ext_tabline=false,
ext_wildmenu=false, ext_wildmenu=false,
ext_newgrid=false, ext_linegrid=false,
ext_hlstate=false, ext_hlstate=false,
} }
it("for defaults", function() it("for defaults", function()
screen:attach() screen:attach()
-- NB: UI test suite can be run in both "newgrid" and legacy grid mode. -- NB: UI test suite can be run in both "linegrid" and legacy grid mode.
-- In both cases check that the received value is the one requested. -- In both cases check that the received value is the one requested.
defaults.ext_newgrid = screen._options.ext_newgrid or false defaults.ext_linegrid = screen._options.ext_linegrid or false
screen:expect(function() screen:expect(function()
eq(defaults, screen.options) eq(defaults, screen.options)
end) end)
@@ -46,7 +46,7 @@ describe('ui receives option updates', function()
it("when setting options", function() it("when setting options", function()
screen:attach() screen:attach()
defaults.ext_newgrid = screen._options.ext_newgrid or false defaults.ext_linegrid = screen._options.ext_linegrid or false
local changed = {} local changed = {}
for k,v in pairs(defaults) do for k,v in pairs(defaults) do
changed[k] = v changed[k] = v
@@ -95,7 +95,7 @@ describe('ui receives option updates', function()
end end
screen:attach({ext_cmdline=true, ext_wildmenu=true}) screen:attach({ext_cmdline=true, ext_wildmenu=true})
defaults.ext_newgrid = screen._options.ext_newgrid or false defaults.ext_linegrid = screen._options.ext_linegrid or false
changed.ext_cmdline = true changed.ext_cmdline = true
changed.ext_wildmenu = true changed.ext_wildmenu = true
screen:expect(function() screen:expect(function()

View File

@@ -185,11 +185,11 @@ function Screen:attach(options)
if options == nil then if options == nil then
options = {} options = {}
end end
if options.ext_newgrid == nil then if options.ext_linegrid == nil then
options.ext_newgrid = true options.ext_linegrid = true
end end
self._options = options self._options = options
self._clear_attrs = (options.ext_newgrid and {{},{}}) or {} self._clear_attrs = (options.ext_linegrid and {{},{}}) or {}
uimeths.attach(self._width, self._height, options) uimeths.attach(self._width, self._height, options)
if self._options.rgb == nil then if self._options.rgb == nil then
-- nvim defaults to rgb=true internally, -- nvim defaults to rgb=true internally,
@@ -386,9 +386,13 @@ function Screen:wait(check, timeout)
local err, checked = false local err, checked = false
local success_seen = false local success_seen = false
local failure_after_success = false local failure_after_success = false
local did_flush = true
local function notification_cb(method, args) local function notification_cb(method, args)
assert(method == 'redraw') assert(method == 'redraw')
self:_redraw(args) did_flush = self:_redraw(args)
if not did_flush then
return
end
err = check() err = check()
checked = true checked = true
if not err then if not err then
@@ -402,7 +406,9 @@ function Screen:wait(check, timeout)
return true return true
end end
run(nil, notification_cb, nil, timeout or self.timeout) run(nil, notification_cb, nil, timeout or self.timeout)
if not checked then if not did_flush then
err = "no flush received"
elseif not checked then
err = check() err = check()
end end
@@ -431,7 +437,8 @@ function Screen:sleep(ms)
end end
function Screen:_redraw(updates) function Screen:_redraw(updates)
for _, update in ipairs(updates) do local did_flush = false
for k, update in ipairs(updates) do
-- print('--') -- print('--')
-- print(require('inspect')(update)) -- print(require('inspect')(update))
local method = update[1] local method = update[1]
@@ -446,7 +453,11 @@ function Screen:_redraw(updates)
self._on_event(method, update[i]) self._on_event(method, update[i])
end end
end end
if k == #updates and method == "flush" then
did_flush = true
end
end end
return did_flush
end end
function Screen:set_on_event_handler(callback) function Screen:set_on_event_handler(callback)
@@ -472,6 +483,10 @@ function Screen:_handle_resize(width, height)
} }
end end
function Screen:_handle_flush()
end
function Screen:_handle_grid_resize(grid, width, height) function Screen:_handle_grid_resize(grid, width, height)
assert(grid == 1) assert(grid == 1)
self:_handle_resize(width, height) self:_handle_resize(width, height)
@@ -609,6 +624,7 @@ function Screen:_handle_highlight_set(attrs)
end end
function Screen:_handle_put(str) function Screen:_handle_put(str)
assert(not self._options.ext_linegrid)
local cell = self._rows[self._cursor.row][self._cursor.col] local cell = self._rows[self._cursor.row][self._cursor.col]
cell.text = str cell.text = str
cell.attrs = self._attrs cell.attrs = self._attrs
@@ -617,6 +633,7 @@ function Screen:_handle_put(str)
end end
function Screen:_handle_grid_line(grid, row, col, items) function Screen:_handle_grid_line(grid, row, col, items)
assert(self._options.ext_linegrid)
assert(grid == 1) assert(grid == 1)
local line = self._rows[row+1] local line = self._rows[row+1]
local colpos = col+1 local colpos = col+1
@@ -764,7 +781,7 @@ function Screen:_row_repr(row, attr_state)
local current_attr_id local current_attr_id
for i = 1, self._width do for i = 1, self._width do
local attrs = row[i].attrs local attrs = row[i].attrs
if self._options.ext_newgrid then if self._options.ext_linegrid then
attrs = attrs[(self._options.rgb and 1) or 2] attrs = attrs[(self._options.rgb and 1) or 2]
end end
local attr_id = self:_get_attr_id(attr_state, attrs, row[i].hl_id) local attr_id = self:_get_attr_id(attr_state, attrs, row[i].hl_id)
@@ -820,7 +837,7 @@ function Screen:_chunks_repr(chunks, attr_state)
for i, chunk in ipairs(chunks) do for i, chunk in ipairs(chunks) do
local hl, text = unpack(chunk) local hl, text = unpack(chunk)
local attrs local attrs
if self._options.ext_newgrid then if self._options.ext_linegrid then
attrs = self._attr_table[hl][1] attrs = self._attr_table[hl][1]
else else
attrs = hl attrs = hl

View File

@@ -48,13 +48,13 @@ describe('screen', function()
end) end)
end) end)
local function screen_tests(newgrid) local function screen_tests(linegrid)
local screen local screen
before_each(function() before_each(function()
clear() clear()
screen = Screen.new() screen = Screen.new()
screen:attach({rgb=true,ext_newgrid=newgrid}) screen:attach({rgb=true,ext_linegrid=linegrid})
screen:set_default_attr_ids( { screen:set_default_attr_ids( {
[0] = {bold=true, foreground=255}, [0] = {bold=true, foreground=255},
[1] = {bold=true, reverse=true}, [1] = {bold=true, reverse=true},