diff options
Diffstat (limited to 'test/functional/ui/screen.lua')
-rw-r--r-- | test/functional/ui/screen.lua | 939 |
1 files changed, 605 insertions, 334 deletions
diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index 810a68d387..e8d7d5c72d 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -72,23 +72,44 @@ local helpers = require('test.functional.helpers')(nil) local busted = require('busted') -local deepcopy = helpers.deepcopy +local deepcopy = vim.deepcopy local shallowcopy = helpers.shallowcopy local concat_tables = helpers.concat_tables -local pesc = helpers.pesc +local pesc = vim.pesc local run_session = helpers.run_session local eq = helpers.eq local dedent = helpers.dedent local get_session = helpers.get_session local create_callindex = helpers.create_callindex -local inspect = require('vim.inspect') +local inspect = vim.inspect 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 = '', @@ -140,6 +159,7 @@ function Screen.new(width, height) suspended = false, mode = 'normal', options = {}, + pwd = '', popupmenu = nil, cmdline = {}, cmdline_block = {}, @@ -161,30 +181,35 @@ function Screen.new(width, height) _default_attr_ids = nil, mouse_enabled = true, _attrs = {}, - _hl_info = {[0]={}}, - _attr_table = {[0]={{},{}}}, + _hl_info = { [0] = {} }, + _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 = { - grid = 1, row = 1, col = 1 + grid = 1, + row = 1, + col = 1, }, _busy = false, }, Screen) + local function ui(method, ...) if self.rpc_async then - self._session:notify('nvim_ui_'..method, ...) + self._session:notify('nvim_ui_' .. method, ...) else - local status, rv = self._session:request('nvim_ui_'..method, ...) + local status, rv = self._session:request('nvim_ui_' .. method, ...) if not status then error(rv[2]) end end end + self.uimeths = create_callindex(ui) + return self end @@ -200,13 +225,21 @@ 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 @@ -241,106 +274,178 @@ 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 -- canonical order of ext keys, used to generate asserts local ext_keys = { - 'popupmenu', 'cmdline', 'cmdline_block', 'wildmenu_items', 'wildmenu_pos', - 'messages', 'msg_history', 'showmode', 'showcmd', 'ruler', 'float_pos', 'win_viewport' + 'popupmenu', + 'cmdline', + 'cmdline_block', + 'wildmenu_items', + 'wildmenu_pos', + 'messages', + 'msg_history', + 'showmode', + 'showcmd', + 'ruler', + 'float_pos', + '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`. --- 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 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 = {} - 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 - error("Screen:expect: Unknown keyword argument '"..k.."'") + --- @type string, fun() + local grid, condition + + assert(next({ ... }) == nil, 'invalid args to expect()') + + if type(expected) == 'table' 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)) - elseif type(expected) == "string" then + 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)) + elseif type(expected) == 'function' then + 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) @@ -348,15 +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, + 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) @@ -369,27 +482,47 @@ 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): "' .. expected.any .. '"\n' - .. 'Actual:\n |' .. table.concat(actual_rows, '\n |') .. '\n\n') + .. 'Expected (anywhere): "' + .. expected.any + .. '"\n' + .. 'Actual:\n |' + .. table.concat(actual_rows, '\n |') + .. '\n\n' + ) end end - if grid ~= nil then - local err_msg, msg_expected_rows = nil, {} + if grid then + for i, row in ipairs(expected_rows) do + local count --- @type integer? + row, count = row:match('^(.*%|)%*(%d+)$') + if row then + count = tonumber(count) + table.remove(expected_rows, i) + for _ = 1, count do + table.insert(expected_rows, i, row) + end + end + end + local err_msg = nil -- `expected` must match the screen lines exactly. if #actual_rows ~= #expected_rows then - err_msg = "Expected screen height " .. #expected_rows - .. ' differs from actual height ' .. #actual_rows .. '.' + err_msg = 'Expected screen height ' + .. #expected_rows + .. ' differs from actual height ' + .. #actual_rows + .. '.' end + local msg_expected_rows = shallowcopy(expected_rows) + local msg_actual_rows = shallowcopy(actual_rows) for i, row in ipairs(expected_rows) do - msg_expected_rows[i] = row - local pat = nil + local pat = nil --- @type string? if actual_rows[i] and row ~= actual_rows[i] then local after = row while true do @@ -398,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 @@ -405,7 +539,7 @@ function Screen:expect(expected, attr_ids, ...) if row ~= actual_rows[i] and (not pat or not actual_rows[i]:match(pat)) then msg_expected_rows[i] = '*' .. msg_expected_rows[i] if i <= #actual_rows then - actual_rows[i] = '*' .. actual_rows[i] + msg_actual_rows[i] = '*' .. msg_actual_rows[i] end if err_msg == nil then err_msg = 'Row ' .. tostring(i) .. ' did not match.' @@ -414,11 +548,18 @@ function Screen:expect(expected, attr_ids, ...) end if err_msg ~= nil then return ( - err_msg..'\nExpected:\n |'..table.concat(msg_expected_rows, '\n |')..'\n' - ..'Actual:\n |'..table.concat(actual_rows, '\n |')..'\n\n'..[[ + err_msg + .. '\nExpected:\n |' + .. table.concat(msg_expected_rows, '\n |') + .. '\n' + .. 'Actual:\n |' + .. table.concat(msg_actual_rows, '\n |') + .. '\n\n' + .. [[ To print the expect() call that would assert the current screen state, use screen:snapshot_util(). In case of non-deterministic failures, use -screen:redraw_debug() to show all intermediate screen states. ]]) +screen:redraw_debug() to show all intermediate screen states.]] + ) end end @@ -447,12 +588,18 @@ screen:redraw_debug() to show all intermediate screen states. ]]) end -- Convert assertion errors into invalid screen state descriptions. - for _, k in ipairs(concat_tables(ext_keys, {'mode', 'mouse_enabled'})) do + for _, k in ipairs(concat_tables(ext_keys, { 'mode', 'mouse_enabled' })) do -- Empty states are considered the default and need not be mentioned. - if (not (expected[k] == nil and isempty(extstate[k]))) then + if not (expected[k] == nil and isempty(extstate[k])) then local status, res = pcall(eq, expected[k], extstate[k], k) if not status then - return (tostring(res)..'\nHint: full state of "'..k..'":\n '..inspect(extstate[k])) + return ( + tostring(res) + .. '\nHint: full state of "' + .. k + .. '":\n ' + .. inspect(extstate[k]) + ) end end end @@ -461,7 +608,7 @@ screen:redraw_debug() to show all intermediate screen states. ]]) for name, id in pairs(expected.hl_groups) do local expected_hl = attr_state.ids[id] local actual_hl = self._attr_table[self.hl_groups[name]][(self._options.rgb and 1) or 2] - local status, res = pcall(eq, expected_hl, actual_hl, "highlight "..name) + local status, res = pcall(eq, expected_hl, actual_hl, 'highlight ' .. name) if not status then return tostring(res) end @@ -472,9 +619,10 @@ screen:redraw_debug() to show all intermediate screen states. ]]) for gridid, expected_marks in pairs(expected.extmarks) do local stored_marks = self._grid_win_extmarks[gridid] if stored_marks == nil then - return 'no win_extmark for grid '..tostring(gridid) + return 'no win_extmark for grid ' .. tostring(gridid) end - local status, res = pcall(eq, expected_marks, stored_marks, "extmarks for grid "..tostring(gridid)) + local status, res = + pcall(eq, expected_marks, stored_marks, 'extmarks for grid ' .. tostring(gridid)) if not status then return tostring(res) end @@ -482,7 +630,7 @@ screen:redraw_debug() to show all intermediate screen states. ]]) for gridid, _ in pairs(self._grid_win_extmarks) do local expected_marks = expected.extmarks[gridid] if expected_marks == nil then - return 'unexpected win_extmark for grid '..tostring(gridid) + return 'unexpected win_extmark for grid ' .. tostring(gridid) end end end @@ -490,7 +638,6 @@ screen:redraw_debug() to show all intermediate screen states. ]]) end function Screen:expect_unchanged(intermediate, waittime_ms, ignore_attrs) - waittime_ms = waittime_ms and waittime_ms or 100 -- Collect the current screen state. local kwargs = self:get_snapshot(nil, ignore_attrs) @@ -505,8 +652,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 @@ -537,16 +688,18 @@ 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 - minimal_timeout = timeout + if flags.unchanged then + minimal_timeout = flags.timeout or default_timeout_factor * 20 end assert(timeout >= minimal_timeout) local did_minimal_timeout = false local function notification_cb(method, args) - assert(method == 'redraw', string.format( - 'notification_cb: unexpected method (%s, args=%s)', method, inspect(args))) + assert( + method == 'redraw', + string.format('notification_cb: unexpected method (%s, args=%s)', method, inspect(args)) + ) did_flush = self:_redraw(args) if not did_flush then return @@ -557,12 +710,12 @@ function Screen:_wait(check, flags) intermediate_seen = true end - if not err then + if not err and (not flags.intermediate or intermediate_seen) then success_seen = true if did_minimal_timeout then self._session:stop() end - elseif success_seen and #args > 0 then + elseif err and success_seen and #args > 0 then success_seen = false failure_after_success = true -- print(inspect(args)) @@ -572,7 +725,7 @@ function Screen:_wait(check, flags) end local eof = run_session(self._session, flags.request_cb, notification_cb, nil, minimal_timeout) if not did_flush then - err = "no flush received" + err = 'no flush received' elseif not checked then err = check() if not err and flags.unchanged then @@ -583,12 +736,13 @@ function Screen:_wait(check, flags) if not success_seen and not eof then did_minimal_timeout = true - eof = run_session(self._session, flags.request_cb, notification_cb, nil, timeout-minimal_timeout) + eof = + run_session(self._session, flags.request_cb, notification_cb, nil, timeout - minimal_timeout) end local did_warn = false if warn_immediate and immediate_seen then - print([[ + print([[ warning: Screen test succeeded immediately. Try to avoid this unless the purpose of the test really requires it.]]) @@ -622,21 +776,24 @@ between asynchronous (feed(), nvim_input()) and synchronous API calls. did_warn = true end - if err then - if eof then err = err..'\n\n'..eof[2] end - busted.fail(err, 3) + if eof then + err = err .. '\n\n' .. eof[2] + end + busted.fail(err .. '\n\nSnapshot:\n' .. self:_print_snapshot(), 3) elseif did_warn then - if eof then print(eof[2]) end + if eof then + print(eof[2]) + end local tb = debug.traceback() local index = string.find(tb, '\n%s*%[C]') - print(string.sub(tb,1,index)) + print(string.sub(tb, 1, index)) end if flags.intermediate then - assert(intermediate_seen, "expected intermediate screen state before final screen state") + assert(intermediate_seen, 'expected intermediate screen state before final screen state') elseif flags.unchanged then - assert(not intermediate_seen, "expected screen state to be unchanged") + assert(not intermediate_seen, 'expected screen state to be unchanged') end end @@ -648,23 +805,31 @@ 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 -- print('--', inspect(update)) local method = update[1] for i = 2, #update do - local handler_name = '_handle_'..method + local handler_name = '_handle_' .. method + --- @type function local handler = self[handler_name] - assert(handler ~= nil, "missing handler: Screen:"..handler_name) + assert(handler ~= nil, 'missing handler: Screen:' .. handler_name) local status, res = pcall(handler, self, unpack(update[i])) if not status then - error(handler_name..' failed' - ..'\n payload: '..inspect(update) - ..'\n error: '..tostring(res)) + error( + handler_name + .. ' failed' + .. '\n payload: ' + .. inspect(update) + .. '\n error: ' + .. tostring(res) + ) end end - if k == #updates and method == "flush" then + if k == #updates and method == 'flush' then did_flush = true end end @@ -674,12 +839,15 @@ end function Screen:_handle_resize(width, height) self:_handle_grid_resize(1, width, height) self._scroll_region = { - top = 1, bot = height, left = 1, right = width + top = 1, + bot = height, + left = 1, + right = width, } self._grid = self._grids[1] end -local function min(x,y) +local function min(x, y) if x < y then return x else @@ -692,14 +860,14 @@ function Screen:_handle_grid_resize(grid, width, height) for _ = 1, height do local cols = {} for _ = 1, width do - table.insert(cols, {text = ' ', attrs = self._clear_attrs, hl_id = 0}) + table.insert(cols, { text = ' ', attrs = self._clear_attrs, hl_id = 0 }) end table.insert(rows, cols) end if grid > 1 and self._grids[grid] ~= nil then local old = self._grids[grid] - for i = 1, min(height,old.height) do - for j = 1, min(width,old.width) do + for i = 1, min(height, old.height) do + for j = 1, min(width, old.width) do rows[i][j] = old.rows[i][j] end end @@ -710,13 +878,12 @@ function Screen:_handle_grid_resize(grid, width, height) self._cursor.col = 1 end self._grids[grid] = { - rows=rows, - width=width, - height=height, + rows = rows, + width = width, + height = height, } end - function Screen:_handle_msg_set_pos(grid, row, scrolled, char) self.msg_grid = grid self.msg_grid_pos = row @@ -724,8 +891,7 @@ function Screen:_handle_msg_set_pos(grid, row, scrolled, char) self.msg_sep_char = char end -function Screen:_handle_flush() -end +function Screen:_handle_flush() end function Screen:_reset() -- TODO: generalize to multigrid later @@ -740,18 +906,20 @@ 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 - -- attr IDs are not stable, but their value should be - if item.attr_id ~= nil then - item.attr = self._attr_table[item.attr_id][1] - item.attr_id = nil - end - if item.attr_id_lm ~= nil then - item.attr_lm = self._attr_table[item.attr_id_lm][1] - item.attr_id_lm = nil - end + -- attr IDs are not stable, but their value should be + if item.attr_id ~= nil then + item.attr = self._attr_table[item.attr_id][1] + item.attr_id = nil + end + if item.attr_id_lm ~= nil then + item.attr_lm = self._attr_table[item.attr_id_lm][1] + item.attr_id_lm = nil + end end self._mode_info = mode_info end @@ -763,7 +931,10 @@ function Screen:_handle_clear() -- newer clients, to check we remain compatible with both kind of clients, -- ensure the scroll region is in a reset state. local expected_region = { - top = 1, bot = self._grid.height, left = 1, right = self._grid.width + top = 1, + bot = self._grid.height, + left = 1, + right = self._grid.width, } eq(expected_region, self._scroll_region) self:_handle_grid_clear(1) @@ -804,14 +975,24 @@ function Screen:_handle_win_pos(grid, win, startrow, startcol, width, height) startrow = startrow, startcol = startcol, width = width, - height = height + height = height, } self.float_pos[grid] = nil end -function Screen:_handle_win_viewport(grid, win, topline, botline, curline, curcol, linecount, scroll_delta) +function Screen:_handle_win_viewport( + grid, + win, + topline, + botline, + curline, + curcol, + linecount, + scroll_delta +) -- accumulate scroll delta - local last_scroll_delta = self.win_viewport[grid] and self.win_viewport[grid].sum_scroll_delta or 0 + local last_scroll_delta = self.win_viewport[grid] and self.win_viewport[grid].sum_scroll_delta + or 0 self.win_viewport[grid] = { win = win, topline = topline, @@ -819,18 +1000,18 @@ function Screen:_handle_win_viewport(grid, win, topline, botline, curline, curco curline = curline, curcol = curcol, linecount = linecount, - sum_scroll_delta = scroll_delta + last_scroll_delta + sum_scroll_delta = scroll_delta + last_scroll_delta, } end function Screen:_handle_win_float_pos(grid, ...) self.win_position[grid] = nil - self.float_pos[grid] = {...} + self.float_pos[grid] = { ... } end function Screen:_handle_win_external_pos(grid) self.win_position[grid] = nil - self.float_pos[grid] = {external=true} + self.float_pos[grid] = { external = true } end function Screen:_handle_win_hide(grid) @@ -846,7 +1027,7 @@ function Screen:_handle_win_extmark(grid, ...) if self._grid_win_extmarks[grid] == nil then self._grid_win_extmarks[grid] = {} end - table.insert(self._grid_win_extmarks[grid], {...}) + table.insert(self._grid_win_extmarks[grid], { ... }) end function Screen:_handle_busy_start() @@ -866,7 +1047,7 @@ function Screen:_handle_mouse_off() end function Screen:_handle_mode_change(mode, idx) - assert(mode == self._mode_info[idx+1].name) + assert(mode == self._mode_info[idx + 1].name) self.mode = mode end @@ -882,17 +1063,24 @@ function Screen:_handle_scroll(count) local bot = self._scroll_region.bot local left = self._scroll_region.left local right = self._scroll_region.right - self:_handle_grid_scroll(1, top-1, bot, left-1, right, count, 0) + 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 + 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 start = top stop = bot - rows @@ -921,11 +1109,13 @@ function Screen:_handle_grid_scroll(g, top, bot, left, right, rows, cols) end function Screen:_handle_hl_attr_define(id, rgb_attrs, cterm_attrs, info) - self._attr_table[id] = {rgb_attrs, cterm_attrs} + self._attr_table[id] = { rgb_attrs, cterm_attrs } self._hl_info[id] = 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 @@ -933,9 +1123,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) @@ -951,14 +1140,18 @@ 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) - local line = self._grids[grid].rows[row+1] - local colpos = col+1 + local line = self._grids[grid].rows[row + 1] + local colpos = col + 1 local hl_id = 0 - for _,item in ipairs(items) do - local text, hl_id_cell, count = unpack(item) + for _, item in ipairs(items) do + local text, hl_id_cell, count = item[1], item[2], item[3] if hl_id_cell ~= nil then hl_id = hl_id_cell end @@ -966,7 +1159,7 @@ function Screen:_handle_grid_line(grid, row, col, items) local cell = line[colpos] cell.text = text cell.hl_id = hl_id - colpos = colpos+1 + colpos = colpos + 1 end end end @@ -981,11 +1174,11 @@ end function Screen:_handle_default_colors_set(rgb_fg, rgb_bg, rgb_sp, cterm_fg, cterm_bg) self.default_colors = { - rgb_fg=rgb_fg, - rgb_bg=rgb_bg, - rgb_sp=rgb_sp, - cterm_fg=cterm_fg, - cterm_bg=cterm_bg + rgb_fg = rgb_fg, + rgb_bg = rgb_bg, + rgb_sp = rgb_sp, + cterm_fg = cterm_fg, + cterm_bg = cterm_bg, } end @@ -1021,8 +1214,12 @@ function Screen:_handle_option_set(name, value) self.options[name] = value end +function Screen:_handle_chdir(path) + self.pwd = vim.fs.normalize(path, { expand_env = false }) +end + function Screen:_handle_popupmenu_show(items, selected, row, col, grid) - self.popupmenu = {items=items, pos=selected, anchor={grid, row, col}} + self.popupmenu = { items = items, pos = selected, anchor = { grid, row, col } } end function Screen:_handle_popupmenu_select(selected) @@ -1034,9 +1231,15 @@ function Screen:_handle_popupmenu_hide() end function Screen:_handle_cmdline_show(content, pos, firstc, prompt, indent, level) - if firstc == '' then firstc = nil end - if prompt == '' then prompt = nil end - if indent == 0 then indent = nil end + if firstc == '' then + firstc = nil + end + if prompt == '' then + prompt = nil + end + if indent == 0 then + indent = nil + end -- check position is valid #10000 local len = 0 @@ -1045,8 +1248,13 @@ function Screen:_handle_cmdline_show(content, pos, firstc, prompt, indent, level end assert(pos <= len) - self.cmdline[level] = {content=content, pos=pos, firstc=firstc, - prompt=prompt, indent=indent} + self.cmdline[level] = { + content = content, + pos = pos, + firstc = firstc, + prompt = prompt, + indent = indent, + } end function Screen:_handle_cmdline_hide(level) @@ -1055,7 +1263,7 @@ end function Screen:_handle_cmdline_special_char(char, shift, level) -- cleared by next cmdline_show on the same level - self.cmdline[level].special = {char, shift} + self.cmdline[level].special = { char, shift } end function Screen:_handle_cmdline_pos(pos, level) @@ -1067,7 +1275,7 @@ function Screen:_handle_cmdline_block_show(block) end function Screen:_handle_cmdline_block_append(item) - self.cmdline_block[#self.cmdline_block+1] = item + self.cmdline_block[#self.cmdline_block + 1] = item end function Screen:_handle_cmdline_block_hide() @@ -1091,7 +1299,7 @@ function Screen:_handle_msg_show(kind, chunks, replace_last) if not replace_last or pos == 0 then pos = pos + 1 end - self.messages[pos] = {kind=kind, content=chunks} + self.messages[pos] = { kind = kind, content = chunks } end function Screen:_handle_msg_clear() @@ -1140,19 +1348,23 @@ function Screen:_row_repr(gridnr, rownr, attr_state, cursor) local has_windows = self._options.ext_multigrid and gridnr == 1 local row = self._grids[gridnr].rows[rownr] if has_windows and self.msg_grid and self.msg_grid_pos < rownr then - return '['..self.msg_grid..':'..string.rep('-',#row)..']' + return '[' .. self.msg_grid .. ':' .. string.rep('-', #row) .. ']' end while i <= #row do local did_window = false if has_windows then - for id,pos in pairs(self.win_position) do - if i-1 == pos.startcol and pos.startrow <= rownr-1 and rownr-1 < pos.startrow + pos.height then + for id, pos in pairs(self.win_position) do + if + i - 1 == pos.startcol + and pos.startrow <= rownr - 1 + and rownr - 1 < pos.startrow + pos.height + then if current_attr_id then -- close current attribute bracket table.insert(rv, '}') current_attr_id = nil end - table.insert(rv, '['..id..':'..string.rep('-',pos.width)..']') + table.insert(rv, '[' .. id .. ':' .. string.rep('-', pos.width) .. ']') i = i + pos.width did_window = true end @@ -1183,7 +1395,7 @@ function Screen:_row_repr(gridnr, rownr, attr_state, cursor) end -- return the line representation, but remove empty attribute brackets and -- trailing whitespace - return table.concat(rv, '')--:gsub('%s+$', '') + return table.concat(rv, '') --:gsub('%s+$', '') end function Screen:_extstate_repr(attr_state) @@ -1201,29 +1413,29 @@ function Screen:_extstate_repr(attr_state) local messages = {} for i, entry in ipairs(self.messages) do - messages[i] = {kind=entry.kind, content=self:_chunks_repr(entry.content, attr_state)} + messages[i] = { kind = entry.kind, content = self:_chunks_repr(entry.content, attr_state) } end local msg_history = {} for i, entry in ipairs(self.msg_history) do - msg_history[i] = {kind=entry[1], content=self:_chunks_repr(entry[2], attr_state)} + msg_history[i] = { kind = entry[1], content = self:_chunks_repr(entry[2], attr_state) } end local win_viewport = (next(self.win_viewport) and self.win_viewport) or nil return { - popupmenu=self.popupmenu, - cmdline=cmdline, - cmdline_block=cmdline_block, - wildmenu_items=self.wildmenu_items, - wildmenu_pos=self.wildmenu_pos, - messages=messages, - showmode=self:_chunks_repr(self.showmode, attr_state), - showcmd=self:_chunks_repr(self.showcmd, attr_state), - ruler=self:_chunks_repr(self.ruler, attr_state), - msg_history=msg_history, - float_pos=self.float_pos, - win_viewport=win_viewport, + popupmenu = self.popupmenu, + cmdline = cmdline, + cmdline_block = cmdline_block, + wildmenu_items = self.wildmenu_items, + wildmenu_pos = self.wildmenu_pos, + messages = messages, + showmode = self:_chunks_repr(self.showmode, attr_state), + showcmd = self:_chunks_repr(self.showcmd, attr_state), + ruler = self:_chunks_repr(self.ruler, attr_state), + msg_history = msg_history, + float_pos = self.float_pos, + win_viewport = win_viewport, } end @@ -1238,14 +1450,14 @@ function Screen:_chunks_repr(chunks, attr_state) attrs = hl end local attr_id = self:_get_attr_id(attr_state, attrs, hl) - repr_chunks[i] = {text, attr_id} + repr_chunks[i] = { text, attr_id } end return repr_chunks end -- Generates tests. Call it where Screen:expect() would be. Waits briefly, then -- dumps the current screen state in the form of Screen:expect(). --- Use snapshot_util({},true) to generate a text-only (no attributes) test. +-- Use snapshot_util({}) to generate a text-only (no attributes) test. -- -- @see Screen:redraw_debug() function Screen:snapshot_util(attrs, ignore, request_cb) @@ -1259,7 +1471,7 @@ function Screen:redraw_debug(attrs, ignore, timeout) assert(method == 'redraw') for _, update in ipairs(args) do -- mode_info_set is quite verbose, comment out the condition to debug it. - if update[1] ~= "mode_info_set" then + if update[1] ~= 'mode_info_set' then print(inspect(update)) end end @@ -1273,17 +1485,25 @@ 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 = {} - for igrid,grid in vim.spairs(self._grids) do + for igrid, grid in vim.spairs(self._grids) do if headers then - local suffix = "" - if igrid > 1 and self.win_position[igrid] == nil - and self.float_pos[igrid] == nil and self.msg_grid ~= igrid then - suffix = " (hidden)" + local suffix = '' + if + igrid > 1 + and self.win_position[igrid] == nil + and self.float_pos[igrid] == nil + and self.msg_grid ~= igrid + then + suffix = ' (hidden)' end - table.insert(rv, "## grid "..igrid..suffix) + table.insert(rv, '## grid ' .. igrid .. suffix) end local height = grid.height if igrid == self.msg_grid then @@ -1291,8 +1511,8 @@ function Screen:render(headers, attr_state, preview) end for i = 1, height do local cursor = self._cursor.grid == igrid and self._cursor.row == i - local prefix = (headers or preview) and " " or "" - table.insert(rv, prefix..self:_row_repr(igrid, i, attr_state, cursor).."|") + local prefix = (headers or preview) and ' ' or '' + table.insert(rv, prefix .. self:_row_repr(igrid, i, attr_state, cursor) .. '|') end end return rv @@ -1301,15 +1521,22 @@ end -- Returns the current screen state in the form of a screen:expect() -- keyword-args map. function Screen:get_snapshot(attrs, ignore) - attrs = attrs or self._default_attr_ids if ignore == nil then ignore = self._default_attr_ignore end local attr_state = { - ids = {}, - ignore = ignore, - mutable = true, -- allow _row_repr to add missing highlights + ids = {}, + ignore = ignore, + mutable = true, -- allow _row_repr to add missing highlights } + if attrs == nil then + attrs = self._default_attr_ids + elseif isempty(attrs) then + attrs = nil + attr_state.ids = nil + else + attr_state.modified = true + end if attrs ~= nil then for i, a in pairs(attrs) do @@ -1317,11 +1544,22 @@ function Screen:get_snapshot(attrs, ignore) end end if self._options.ext_linegrid then - attr_state.id_to_index = self:linegrid_check_attrs(attr_state.ids) + attr_state.id_to_index = self:linegrid_check_attrs(attr_state.ids or {}) end local lines = self:render(true, attr_state, true) + for i, row in ipairs(lines) do + local count = 1 + while i < #lines and lines[i + 1] == row do + count = count + 1 + table.remove(lines, i + 1) + end + if count > 1 then + lines[i] = lines[i] .. '*' .. count + end + end + local ext_state = self:_extstate_repr(attr_state) for k, v in pairs(ext_state) do if isempty(v) then @@ -1353,33 +1591,50 @@ local function fmt_ext_state(name, state) return item end end - if name == "win_viewport" then - local str = "{\n" - for k,v in pairs(state) do - str = (str.." ["..k.."] = {win = {id = "..v.win.id.."}, topline = " - ..v.topline..", botline = "..v.botline..", curline = "..v.curline - ..", curcol = "..v.curcol..", linecount = "..v.linecount..", sum_scroll_delta = "..v.sum_scroll_delta.."};\n") + if name == 'win_viewport' then + local str = '{\n' + for k, v in pairs(state) do + str = ( + str + .. ' [' + .. k + .. '] = {win = ' + .. v.win + .. ', topline = ' + .. v.topline + .. ', botline = ' + .. v.botline + .. ', curline = ' + .. v.curline + .. ', curcol = ' + .. v.curcol + .. ', linecount = ' + .. v.linecount + .. ', sum_scroll_delta = ' + .. v.sum_scroll_delta + .. '};\n' + ) end - return str .. "}" - elseif name == "float_pos" then - local str = "{\n" - for k,v in pairs(state) do - str = str.." ["..k.."] = {{id = "..v[1].id.."}" + return str .. '}' + elseif name == 'float_pos' then + local str = '{\n' + for k, v in pairs(state) do + str = str .. ' [' .. k .. '] = {' .. v[1] for i = 2, #v do - str = str..", "..inspect(v[i]) + str = str .. ', ' .. inspect(v[i]) end - str = str .. "};\n" + str = str .. '};\n' end - return str .. "}" + return str .. '}' else -- TODO(bfredl): improve formatting of more states - return inspect(state,{process=remove_all_metatables}) + return inspect(state, { process = remove_all_metatables }) end end -function Screen:print_snapshot(attrs, ignore) +function Screen:_print_snapshot(attrs, ignore) local kwargs, ext_state, attr_state = self:get_snapshot(attrs, ignore) - local attrstr = "" + local attrstr = '' if attr_state.modified then local attrstrs = {} for i, a in pairs(attr_state.ids) do @@ -1387,23 +1642,29 @@ function Screen:print_snapshot(attrs, ignore) if self._options.ext_linegrid then dict = self:_pprint_hlitem(a) else - dict = "{"..self:_pprint_attrs(a).."}" + dict = '{' .. self:_pprint_attrs(a) .. '}' end - local keyval = (type(i) == "number") and "["..tostring(i).."]" or i - table.insert(attrstrs, " "..keyval.." = "..dict..";") + local keyval = (type(i) == 'number') and '[' .. tostring(i) .. ']' or i + table.insert(attrstrs, ' ' .. keyval .. ' = ' .. dict .. ';') end - attrstr = (", attr_ids={\n"..table.concat(attrstrs, "\n").."\n}") + attrstr = (', attr_ids={\n' .. table.concat(attrstrs, '\n') .. '\n}') + elseif isempty(attrs) then + attrstr = ', attr_ids={}' end - print( "\nscreen:expect{grid=[[") - print(kwargs.grid) - io.stdout:write( "]]"..attrstr) + local result = 'screen:expect{grid=[[\n' .. kwargs.grid .. '\n]]' .. attrstr for _, k in ipairs(ext_keys) do - if ext_state[k] ~= nil and not (k == "win_viewport" and not self.options.ext_multigrid) then - io.stdout:write(", "..k.."="..fmt_ext_state(k, ext_state[k])) + if ext_state[k] ~= nil and not (k == 'win_viewport' and not self.options.ext_multigrid) then + result = result .. ', ' .. k .. '=' .. fmt_ext_state(k, ext_state[k]) end end - print("}\n") + result = result .. '}' + + return result +end + +function Screen:print_snapshot(attrs, ignore) + print('\n' .. self:_print_snapshot(attrs, ignore) .. '\n') io.stdout:flush() end @@ -1422,7 +1683,7 @@ function Screen:_insert_hl_id(attr_state, hl_id) else info[1] = {} for k, v in pairs(raw_info[1]) do - if k ~= "id" then + if k ~= 'id' then info[1][k] = v end end @@ -1432,9 +1693,9 @@ function Screen:_insert_hl_id(attr_state, hl_id) local entry = self._attr_table[hl_id] local attrval if self._rgb_cterm then - attrval = {entry[1], entry[2], info} -- unpack() doesn't work + attrval = { entry[1], entry[2], info } -- unpack() doesn't work elseif self._options.ext_hlstate then - attrval = {entry[1], info} + attrval = { entry[1], info } else attrval = self._options.rgb and entry[1] or entry[2] end @@ -1450,17 +1711,17 @@ function Screen:linegrid_check_attrs(attrs) local iinfo = self._hl_info[i] local matchinfo = {} if #iinfo > 1 then - for k,item in ipairs(iinfo) do + 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 + for k, v in pairs(attrs) do local attr, info, attr_rgb, attr_cterm if self._rgb_cterm then attr_rgb, attr_cterm, info = unpack(v) - attr = {attr_rgb, attr_cterm} + attr = { attr_rgb, attr_cterm } info = info or {} elseif self._options.ext_hlstate then attr, info = unpack(v) @@ -1472,12 +1733,12 @@ function Screen:linegrid_check_attrs(attrs) if #info == #matchinfo then local match = false if #info == 1 then - if self:_equal_info(info[1],matchinfo[1]) then + if self:_equal_info(info[1], matchinfo[1]) then match = true end else match = true - for j = 1,#info do + for j = 1, #info do if info[j] ~= matchinfo[j] then match = false end @@ -1489,32 +1750,34 @@ function Screen:linegrid_check_attrs(attrs) end end end - if self:_equal_attr_def(self._rgb_cterm and {{}, {}} or {}, def_attr) and #self._hl_info[i] == 0 then - id_to_index[i] = "" + if + self:_equal_attr_def(self._rgb_cterm and { {}, {} } or {}, def_attr) + and #self._hl_info[i] == 0 + then + id_to_index[i] = '' end end return id_to_index end - function Screen:_pprint_hlitem(item) - -- print(inspect(item)) - local multi = self._rgb_cterm or self._options.ext_hlstate - local cterm = (not self._rgb_cterm and not self._options.rgb) - local attrdict = "{"..self:_pprint_attrs(multi and item[1] or item, cterm).."}" - local attrdict2, hlinfo - local descdict = "" - if self._rgb_cterm then - attrdict2 = ", {"..self:_pprint_attrs(item[2], true).."}" - hlinfo = item[3] - else - attrdict2 = "" - hlinfo = item[2] - end - if self._options.ext_hlstate then - descdict = ", {"..self:_pprint_hlinfo(hlinfo).."}" - end - return (multi and "{" or "")..attrdict..attrdict2..descdict..(multi and "}" or "") + -- print(inspect(item)) + local multi = self._rgb_cterm or self._options.ext_hlstate + local cterm = (not self._rgb_cterm and not self._options.rgb) + local attrdict = '{' .. self:_pprint_attrs(multi and item[1] or item, cterm) .. '}' + local attrdict2, hlinfo + local descdict = '' + if self._rgb_cterm then + attrdict2 = ', {' .. self:_pprint_attrs(item[2], true) .. '}' + hlinfo = item[3] + else + attrdict2 = '' + hlinfo = item[2] + end + if self._options.ext_hlstate then + descdict = ', {' .. self:_pprint_hlinfo(hlinfo) .. '}' + end + return (multi and '{' or '') .. attrdict .. attrdict2 .. descdict .. (multi and '}' or '') end function Screen:_pprint_hlinfo(states) @@ -1522,37 +1785,37 @@ function Screen:_pprint_hlinfo(states) local items = {} for f, v in pairs(states[1]) do local desc = tostring(v) - if type(v) == type("") then - desc = '"'..desc..'"' + if type(v) == type('') then + desc = '"' .. desc .. '"' end - table.insert(items, f.." = "..desc) + table.insert(items, f .. ' = ' .. desc) end - return "{"..table.concat(items, ", ").."}" + return '{' .. table.concat(items, ', ') .. '}' else - return table.concat(states, ", ") + return table.concat(states, ', ') end end - function Screen:_pprint_attrs(attrs, cterm) - local items = {} - for f, v in pairs(attrs) do - local desc = tostring(v) - if f == "foreground" or f == "background" or f == "special" then - if Screen.colornames[v] ~= nil then - desc = "Screen.colors."..Screen.colornames[v] - elseif cterm then - desc = tostring(v) - else - desc = string.format("tonumber('0x%06x')",v) - end + local items = {} + for f, v in pairs(attrs) do + local desc = tostring(v) + if f == 'foreground' or f == 'background' or f == 'special' then + if Screen.colornames[v] ~= nil then + desc = 'Screen.colors.' .. Screen.colornames[v] + elseif cterm then + desc = tostring(v) + else + desc = string.format("tonumber('0x%06x')", v) end - table.insert(items, f.." = "..desc) end - return table.concat(items, ", ") + table.insert(items, f .. ' = ' .. desc) + end + return table.concat(items, ', ') end -local function backward_find_meaningful(tbl, from) -- luacheck: no unused +---@diagnostic disable-next-line: unused-local, unused-function +local function backward_find_meaningful(tbl, from) -- luacheck: no unused for i = from or #tbl, 1, -1 do if tbl[i] ~= ' ' then return i + 1 @@ -1568,7 +1831,7 @@ function Screen:_get_attr_id(attr_state, attrs, hl_id) if self._options.ext_linegrid then local id = attr_state.id_to_index[hl_id] - if id == "" then -- sentinel for empty it + if id == '' then -- sentinel for empty it return nil elseif id ~= nil then return id @@ -1579,7 +1842,7 @@ function Screen:_get_attr_id(attr_state, attrs, hl_id) return id end local kind = self._options.rgb and 1 or 2 - return "UNEXPECTED "..self:_pprint_attrs(self._attr_table[hl_id][kind]) + return 'UNEXPECTED ' .. self:_pprint_attrs(self._attr_table[hl_id][kind]) else if self:_equal_attrs(attrs, {}) then -- ignore this attrs @@ -1587,49 +1850,57 @@ function Screen:_get_attr_id(attr_state, attrs, hl_id) end for id, a in pairs(attr_state.ids) do if self:_equal_attrs(a, attrs) then - return id - end + return id + end end if attr_state.mutable then table.insert(attr_state.ids, attrs) attr_state.modified = true return #attr_state.ids end - return "UNEXPECTED "..self:_pprint_attrs(attrs) + return 'UNEXPECTED ' .. self:_pprint_attrs(attrs) end end function Screen:_equal_attr_def(a, b) if self._rgb_cterm then - return self:_equal_attrs(a[1],b[1]) and self:_equal_attrs(a[2],b[2]) + return self:_equal_attrs(a[1], b[1]) and self:_equal_attrs(a[2], b[2]) elseif self._options.rgb then - return self:_equal_attrs(a,b[1]) + return self:_equal_attrs(a, b[1]) else - return self:_equal_attrs(a,b[2]) + return self:_equal_attrs(a, b[2]) end 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.underdouble == b.underdouble and a.underdotted == b.underdotted and - a.underdashed == b.underdashed and a.italic == b.italic and - a.reverse == b.reverse and a.foreground == b.foreground and - a.background == b.background and a.special == b.special and a.blend == b.blend and - a.strikethrough == b.strikethrough and - a.fg_indexed == b.fg_indexed and a.bg_indexed == b.bg_indexed + return a.bold == b.bold + and a.standout == b.standout + and a.underline == b.underline + and a.undercurl == b.undercurl + and a.underdouble == b.underdouble + and a.underdotted == b.underdotted + and a.underdashed == b.underdashed + and a.italic == b.italic + and a.reverse == b.reverse + and a.foreground == b.foreground + and a.background == b.background + and a.special == b.special + and a.blend == b.blend + and a.strikethrough == b.strikethrough + and a.fg_indexed == b.fg_indexed + and a.bg_indexed == b.bg_indexed + and a.url == b.url 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 + 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 end - for i,a in pairs(attrs) do + for i, a in pairs(attrs) do if self:_equal_attrs(a, attr) then return i end |