diff options
author | Lewis Russell <lewis6991@gmail.com> | 2024-01-23 00:53:15 +0000 |
---|---|---|
committer | Lewis Russell <me@lewisr.dev> | 2024-01-23 11:45:17 +0000 |
commit | 0054c185001e54316c0ee36841dfe111878a321d (patch) | |
tree | 130ca3d01fb73443c9ac9fabd93e8182a1cf0634 /test/functional/ui/screen.lua | |
parent | 89ffdebd20fd1d9c1ceff5fb005db11a0cf8c4d4 (diff) | |
download | rneovim-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.lua | 337 |
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 = {} |