aboutsummaryrefslogtreecommitdiff
path: root/test/functional/ui/screen.lua
diff options
context:
space:
mode:
authorLewis Russell <lewis6991@gmail.com>2024-01-23 00:53:15 +0000
committerLewis Russell <me@lewisr.dev>2024-01-23 11:45:17 +0000
commit0054c185001e54316c0ee36841dfe111878a321d (patch)
tree130ca3d01fb73443c9ac9fabd93e8182a1cf0634 /test/functional/ui/screen.lua
parent89ffdebd20fd1d9c1ceff5fb005db11a0cf8c4d4 (diff)
downloadrneovim-0054c185001e54316c0ee36841dfe111878a321d.tar.gz
rneovim-0054c185001e54316c0ee36841dfe111878a321d.tar.bz2
rneovim-0054c185001e54316c0ee36841dfe111878a321d.zip
test: typing for screen.lua
Very rough buts resolves most diagnostic errors and should provide some useful hovers.
Diffstat (limited to 'test/functional/ui/screen.lua')
-rw-r--r--test/functional/ui/screen.lua337
1 files changed, 224 insertions, 113 deletions
diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua
index 07333f2e21..a02130276d 100644
--- a/test/functional/ui/screen.lua
+++ b/test/functional/ui/screen.lua
@@ -88,7 +88,28 @@ local function isempty(v)
return type(v) == 'table' and next(v) == nil
end
+--- @class test.functional.ui.screen.Grid
+--- @field rows table[][]
+--- @field width integer
+--- @field height integer
+
--- @class test.functional.ui.screen
+--- @field colors table<string,integer>
+--- @field colornames table<integer,string>
+--- @field uimeths table<string,function>
+--- @field options? table<string,any>
+--- @field timeout integer
+--- @field win_position table<integer,table<string,integer>>
+--- @field float_pos table<integer,table>
+--- @field cmdline table<integer,table>
+--- @field cmdline_block table[]
+--- @field hl_groups table<string,integer>
+--- @field messages table<integer,table>
+--- @field private _cursor {grid:integer,row:integer,col:integer}
+--- @field private _grids table<integer,test.functional.ui.screen.Grid>
+--- @field private _grid_win_extmarks table<integer,table>
+--- @field private _attr_table table<integer,table>
+--- @field private _hl_info table<integer,table>
local Screen = {}
Screen.__index = Screen
@@ -103,13 +124,14 @@ end
local default_screen_timeout = default_timeout_factor * 3500
-function Screen._init_colors(session)
+local function _init_colors()
+ local session = get_session()
local status, rv = session:request('nvim_get_color_map')
if not status then
error('failed to get color map')
end
- local colors = rv
- local colornames = {}
+ local colors = rv --- @type table<string,integer>
+ local colornames = {} --- @type table<integer,string>
for name, rgb in pairs(colors) do
-- we disregard the case that colornames might not be unique, as
-- this is just a helper to get any canonical name of a color
@@ -119,17 +141,14 @@ function Screen._init_colors(session)
Screen.colornames = colornames
end
+--- @param width? integer
+--- @param height? integer
+--- @return test.functional.ui.screen
function Screen.new(width, height)
if not Screen.colors then
- Screen._init_colors(get_session())
+ _init_colors()
end
- if not width then
- width = 53
- end
- if not height then
- height = 14
- end
local self = setmetatable({
timeout = default_screen_timeout,
title = '',
@@ -166,8 +185,8 @@ function Screen.new(width, height)
_attr_table = { [0] = { {}, {} } },
_clear_attrs = nil,
_new_attrs = false,
- _width = width,
- _height = height,
+ _width = width or 53,
+ _height = height or 14,
_grids = {},
_grid_win_extmarks = {},
_cursor = {
@@ -177,6 +196,7 @@ function Screen.new(width, height)
},
_busy = false,
}, Screen)
+
local function ui(method, ...)
if self.rpc_async then
self._session:notify('nvim_ui_' .. method, ...)
@@ -187,7 +207,9 @@ function Screen.new(width, height)
end
end
end
+
self.uimeths = create_callindex(ui)
+
return self
end
@@ -203,16 +225,25 @@ function Screen:set_rgb_cterm(val)
self._rgb_cterm = val
end
+--- @class test.functional.ui.screen.Opts
+--- @field ext_linegrid? boolean
+--- @field ext_multigrid? boolean
+--- @field ext_newgrid? boolean
+--- @field ext_popupmenu? boolean
+--- @field ext_wildmenu? boolean
+--- @field rgb? boolean
+--- @field _debug_float? boolean
+
+--- @param options test.functional.ui.screen.Opts
+--- @param session? test.Session
function Screen:attach(options, session)
- if session == nil then
- session = get_session()
- end
- if options == nil then
- options = {}
- end
+ session = session or get_session()
+ options = options or {}
+
if options.ext_linegrid == nil then
options.ext_linegrid = true
end
+
self._session = session
self._options = options
self._clear_attrs = (not options.ext_linegrid) and {} or nil
@@ -243,8 +274,11 @@ function Screen:try_resize_grid(grid, columns, rows)
self.uimeths.try_resize_grid(grid, columns, rows)
end
+--- @param option 'ext_linegrid'|'ext_multigrid'|'ext_popupmenu'|'ext_wildmenu'|'rgb'
+--- @param value boolean
function Screen:set_option(option, value)
self.uimeths.set_option(option, value)
+ --- @diagnostic disable-next-line:no-unknown
self._options[option] = value
end
@@ -264,109 +298,154 @@ local ext_keys = {
'win_viewport',
}
--- Asserts that the screen state eventually matches an expected state.
---
--- Can be called with positional args:
--- screen:expect(grid, [attr_ids])
--- screen:expect(condition)
--- or keyword args (supports more options):
--- screen:expect{grid=[[...]], cmdline={...}, condition=function() ... end}
---
---
--- grid: Expected screen state (string). Each line represents a screen
--- row. Last character of each row (typically "|") is stripped.
--- Common indentation is stripped.
--- "{MATCH:x}" in a line is matched against Lua pattern `x`.
--- "*n" at the end of a line means it repeats `n` times.
--- attr_ids: Expected text attributes. Screen rows are transformed according
--- to this table, as follows: each substring S composed of
--- characters having the same attributes will be substituted by
--- "{K:S}", where K is a key in `attr_ids`. Any unexpected
--- attributes in the final state are an error.
--- Use an empty table for a text-only (no attributes) expectation.
--- Use screen:set_default_attr_ids() to define attributes for many
--- expect() calls.
--- extmarks: Expected win_extmarks accumulated for the grids. For each grid,
--- the win_extmark messages are accumulated into an array.
--- condition: Function asserting some arbitrary condition. Return value is
--- ignored, throw an error (use eq() or similar) to signal failure.
--- any: Lua pattern string expected to match a screen line. NB: the
--- following chars are magic characters
--- ( ) . % + - * ? [ ^ $
--- and must be escaped with a preceding % for a literal match.
--- mode: Expected mode as signaled by "mode_change" event
--- unchanged: Test that the screen state is unchanged since the previous
--- expect(...). Any flush event resulting in a different state is
--- considered an error. Not observing any events until timeout
--- is acceptable.
--- intermediate:Test that the final state is the same as the previous expect,
--- but expect an intermediate state that is different. If possible
--- it is better to use an explicit screen:expect(...) for this
--- intermediate state.
--- reset: Reset the state internal to the test Screen before starting to
--- receive updates. This should be used after command("redraw!")
--- or some other mechanism that will invoke "redraw!", to check
--- that all screen state is transmitted again. This includes
--- state related to ext_ features as mentioned below.
--- timeout: maximum time that will be waited until the expected state is
--- seen (or maximum time to observe an incorrect change when
--- `unchanged` flag is used)
---
--- The following keys should be used to expect the state of various ext_
--- features. Note that an absent key will assert that the item is currently
--- NOT present on the screen, also when positional form is used.
---
--- popupmenu: Expected ext_popupmenu state,
--- cmdline: Expected ext_cmdline state, as an array of cmdlines of
--- different level.
--- cmdline_block: Expected ext_cmdline block (for function definitions)
--- wildmenu_items: Expected items for ext_wildmenu
--- wildmenu_pos: Expected position for ext_wildmenu
+local expect_keys = {
+ grid = true,
+ attr_ids = true,
+ condition = true,
+ mouse_enabled = true,
+ any = true,
+ mode = true,
+ unchanged = true,
+ intermediate = true,
+ reset = true,
+ timeout = true,
+ request_cb = true,
+ hl_groups = true,
+ extmarks = true,
+}
+
+for _, v in ipairs(ext_keys) do
+ expect_keys[v] = true
+end
+
+--- @class test.function.ui.screen.Expect
+---
+--- Expected screen state (string). Each line represents a screen
+--- row. Last character of each row (typically "|") is stripped.
+--- Common indentation is stripped.
+--- "{MATCH:x}" in a line is matched against Lua pattern `x`.
+--- "*n" at the end of a line means it repeats `n` times.
+--- @field grid? string
+---
+--- Expected text attributes. Screen rows are transformed according
+--- to this table, as follows: each substring S composed of
+--- characters having the same attributes will be substituted by
+--- "{K:S}", where K is a key in `attr_ids`. Any unexpected
+--- attributes in the final state are an error.
+--- Use an empty table for a text-only (no attributes) expectation.
+--- Use screen:set_default_attr_ids() to define attributes for many
+--- expect() calls.
+--- @field attr_ids? table<integer,table<string,any>>
+---
+--- Expected win_extmarks accumulated for the grids. For each grid,
+--- the win_extmark messages are accumulated into an array.
+--- @field extmarks? table<integer,table>
+---
+--- Function asserting some arbitrary condition. Return value is
+--- ignored, throw an error (use eq() or similar) to signal failure.
+--- @field condition? fun()
+---
+--- Lua pattern string expected to match a screen line. NB: the
+--- following chars are magic characters
+--- ( ) . % + - * ? [ ^ $
+--- and must be escaped with a preceding % for a literal match.
+--- @field any? string
+---
+--- Expected mode as signaled by "mode_change" event
+--- @field mode? string
+---
+--- Test that the screen state is unchanged since the previous
+--- expect(...). Any flush event resulting in a different state is
+--- considered an error. Not observing any events until timeout
+--- is acceptable.
+--- @field unchanged? boolean
+---
+--- Test that the final state is the same as the previous expect,
+--- but expect an intermediate state that is different. If possible
+--- it is better to use an explicit screen:expect(...) for this
+--- intermediate state.
+--- @field intermediate? boolean
+---
+--- Reset the state internal to the test Screen before starting to
+--- receive updates. This should be used after command("redraw!")
+--- or some other mechanism that will invoke "redraw!", to check
+--- that all screen state is transmitted again. This includes
+--- state related to ext_ features as mentioned below.
+--- @field reset? boolean
+---
+--- maximum time that will be waited until the expected state is
+--- seen (or maximum time to observe an incorrect change when
+--- `unchanged` flag is used)
+--- @field timeout? integer
+---
+--- @field mouse_enabled? boolean
+---
+--- @field win_viewport? table<integer,table<string,integer>>
+--- @field float_pos? {[1]:integer,[2]:integer}
+--- @field hl_groups? table<string,integer>
+---
+--- The following keys should be used to expect the state of various ext_
+--- features. Note that an absent key will assert that the item is currently
+--- NOT present on the screen, also when positional form is used.
+---
+--- Expected ext_popupmenu state,
+--- @field popupmenu? table
+---
+--- Expected ext_cmdline state, as an array of cmdlines of
+--- different level.
+--- @field cmdline? table
+---
+--- Expected ext_cmdline block (for function definitions)
+--- @field cmdline_block? table
+---
+--- items for ext_wildmenu
+--- @field wildmenu_items? table
+---
+--- position for ext_wildmenu
+--- @field wildmenu_pos? table
+
+--- Asserts that the screen state eventually matches an expected state.
+---
+--- Can be called with positional args:
+--- screen:expect(grid, [attr_ids])
+--- screen:expect(condition)
+--- or keyword args (supports more options):
+--- screen:expect{grid=[[...]], cmdline={...}, condition=function() ... end}
+---
+--- @param expected string|function|test.function.ui.screen.Expect
+--- @param attr_ids? table<integer,table<string,any>>
function Screen:expect(expected, attr_ids, ...)
- local grid, condition = nil, nil
- local expected_rows = {}
+ --- @type string, fun()
+ local grid, condition
+
assert(next({ ... }) == nil, 'invalid args to expect()')
+
if type(expected) == 'table' then
- assert(not (attr_ids ~= nil))
- local is_key = {
- grid = true,
- attr_ids = true,
- condition = true,
- mouse_enabled = true,
- any = true,
- mode = true,
- unchanged = true,
- intermediate = true,
- reset = true,
- timeout = true,
- request_cb = true,
- hl_groups = true,
- extmarks = true,
- }
- for _, v in ipairs(ext_keys) do
- is_key[v] = true
- end
- for k, _ in pairs(expected) do
- if not is_key[k] then
+ assert(attr_ids == nil)
+ for k, _ in
+ pairs(expected --[[@as table<string,any>]])
+ do
+ if not expect_keys[k] then
error("Screen:expect: Unknown keyword argument '" .. k .. "'")
end
end
grid = expected.grid
attr_ids = expected.attr_ids
condition = expected.condition
- assert(not (expected.any ~= nil and grid ~= nil))
+ assert(expected.any == nil or grid == nil)
elseif type(expected) == 'string' then
grid = expected
expected = {}
elseif type(expected) == 'function' then
- assert(not (attr_ids ~= nil))
+ assert(attr_ids == nil)
condition = expected
expected = {}
else
assert(false)
end
- if grid ~= nil then
+ local expected_rows = {} --- @type string[]
+ if grid then
-- Remove the last line and dedent. Note that gsub returns more then one
-- value.
grid = dedent(grid:gsub('\n[ ]+$', ''), 0)
@@ -374,18 +453,23 @@ function Screen:expect(expected, attr_ids, ...)
table.insert(expected_rows, row)
end
end
+
local attr_state = {
ids = attr_ids or self._default_attr_ids,
}
+
if isempty(attr_ids) then
attr_state.ids = nil
end
+
if self._options.ext_linegrid then
attr_state.id_to_index = self:linegrid_check_attrs(attr_state.ids or {})
end
+
self._new_attrs = false
self:_wait(function()
- if condition ~= nil then
+ if condition then
+ --- @type boolean, string
local status, res = pcall(condition)
if not status then
return tostring(res)
@@ -398,10 +482,10 @@ function Screen:expect(expected, attr_ids, ...)
local actual_rows = self:render(not expected.any, attr_state)
- if expected.any ~= nil then
+ if expected.any then
-- Search for `any` anywhere in the screen lines.
local actual_screen_str = table.concat(actual_rows, '\n')
- if nil == string.find(actual_screen_str, expected.any) then
+ if not actual_screen_str:find(expected.any) then
return (
'Failed to match any screen lines.\n'
.. 'Expected (anywhere): "'
@@ -414,9 +498,9 @@ function Screen:expect(expected, attr_ids, ...)
end
end
- if grid ~= nil then
+ if grid then
for i, row in ipairs(expected_rows) do
- local count
+ local count --- @type integer?
row, count = row:match('^(.*%|)%*(%d+)$')
if row then
count = tonumber(count)
@@ -438,7 +522,7 @@ function Screen:expect(expected, attr_ids, ...)
local msg_expected_rows = shallowcopy(expected_rows)
local msg_actual_rows = shallowcopy(actual_rows)
for i, row in ipairs(expected_rows) do
- local pat = nil
+ local pat = nil --- @type string?
if actual_rows[i] and row ~= actual_rows[i] then
local after = row
while true do
@@ -447,6 +531,7 @@ function Screen:expect(expected, attr_ids, ...)
pat = pat and (pat .. pesc(after))
break
end
+ --- @type string
pat = (pat or '') .. pesc(after:sub(1, s - 1)) .. m
after = after:sub(e + 1)
end
@@ -568,8 +653,12 @@ function Screen:expect_unchanged(intermediate, waittime_ms, ignore_attrs)
self:expect(kwargs)
end
+--- @private
+--- @param check fun(): string
+--- @param flags table<string,any>
function Screen:_wait(check, flags)
- local err, checked = false, false
+ local err --- @type string?
+ local checked = false
local success_seen = false
local failure_after_success = false
local did_flush = true
@@ -600,7 +689,7 @@ function Screen:_wait(check, flags)
-- For an "unchanged" test, flags.timeout is the time during which the state
-- must not change, so always wait this full time.
- if (flags.unchanged or flags.intermediate) and flags.timeout ~= nil then
+ if (flags.unchanged or flags.intermediate) and flags.timeout then
minimal_timeout = timeout
end
@@ -717,6 +806,8 @@ function Screen:sleep(ms, request_cb)
run_session(self._session, request_cb, notification_cb, nil, ms)
end
+--- @private
+--- @param updates {[1]:string, [integer]:any[]}[]
function Screen:_redraw(updates)
local did_flush = false
for k, update in ipairs(updates) do
@@ -724,6 +815,7 @@ function Screen:_redraw(updates)
local method = update[1]
for i = 2, #update do
local handler_name = '_handle_' .. method
+ --- @type function
local handler = self[handler_name]
assert(handler ~= nil, 'missing handler: Screen:' .. handler_name)
local status, res = pcall(handler, self, unpack(update[i]))
@@ -815,6 +907,8 @@ function Screen:_reset()
self._grid_win_extmarks = {}
end
+--- @param cursor_style_enabled boolean
+--- @param mode_info table[]
function Screen:_handle_mode_info_set(cursor_style_enabled, mode_info)
self._cursor_style_enabled = cursor_style_enabled
for _, item in pairs(mode_info) do
@@ -973,11 +1067,19 @@ function Screen:_handle_scroll(count)
self:_handle_grid_scroll(1, top - 1, bot, left - 1, right, count, 0)
end
+--- @param g any
+--- @param top integer
+--- @param bot integer
+--- @param left integer
+--- @param right integer
+--- @param rows integer
+--- @param cols integer
function Screen:_handle_grid_scroll(g, top, bot, left, right, rows, cols)
top = top + 1
left = left + 1
assert(cols == 0)
local grid = self._grids[g]
+ --- @type integer, integer, integer
local start, stop, step
if rows > 0 then
@@ -1013,6 +1115,8 @@ function Screen:_handle_hl_attr_define(id, rgb_attrs, cterm_attrs, info)
self._new_attrs = true
end
+--- @param name string
+--- @param id integer
function Screen:_handle_hl_group_set(name, id)
self.hl_groups[name] = id
end
@@ -1020,9 +1124,8 @@ end
function Screen:get_hl(val)
if self._options.ext_newgrid then
return self._attr_table[val][1]
- else
- return val
end
+ return val
end
function Screen:_handle_highlight_set(attrs)
@@ -1038,6 +1141,10 @@ function Screen:_handle_put(str)
self._cursor.col = self._cursor.col + 1
end
+--- @param grid integer
+--- @param row integer
+--- @param col integer
+--- @param items integer[][]
function Screen:_handle_grid_line(grid, row, col, items)
assert(self._options.ext_linegrid)
assert(#items > 0)
@@ -1045,7 +1152,7 @@ function Screen:_handle_grid_line(grid, row, col, items)
local colpos = col + 1
local hl_id = 0
for _, item in ipairs(items) do
- local text, hl_id_cell, count = unpack(item)
+ local text, hl_id_cell, count = item[1], item[2], item[3]
if hl_id_cell ~= nil then
hl_id = hl_id_cell
end
@@ -1379,6 +1486,10 @@ function Screen:redraw_debug(attrs, ignore, timeout)
run_session(self._session, nil, notification_cb, nil, timeout)
end
+--- @param headers boolean
+--- @param attr_state any
+--- @param preview? boolean
+--- @return string[]
function Screen:render(headers, attr_state, preview)
headers = headers and (self._options.ext_multigrid or self._options._debug_float)
local rv = {}