diff options
Diffstat (limited to 'test/functional/ui/screen.lua')
-rw-r--r-- | test/functional/ui/screen.lua | 243 |
1 files changed, 137 insertions, 106 deletions
diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index 06a2ac3ca2..bf979e89f4 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -66,12 +66,12 @@ -- [1] = {reverse = true, bold = true}, -- [2] = {reverse = true} -- }) --- screen:set_default_attr_ignore( {{}, {bold=true, foreground=NonText}} ) -- -- To help write screen tests, see Screen:snapshot_util(). -- To debug screen tests, see Screen:redraw_debug(). local helpers = require('test.functional.helpers')(nil) +local busted = require('busted') local deepcopy = helpers.deepcopy local shallowcopy = helpers.shallowcopy local concat_tables = helpers.concat_tables @@ -158,6 +158,7 @@ function Screen.new(width, height) wildmenu_items = nil, wildmenu_selected = nil, win_position = {}, + win_viewport = {}, float_pos = {}, msg_grid = nil, msg_grid_pos = nil, @@ -169,12 +170,11 @@ function Screen.new(width, height) ruler = {}, hl_groups = {}, _default_attr_ids = nil, - _default_attr_ignore = nil, _mouse_enabled = true, _attrs = {}, - _hl_info = {}, + _hl_info = {[0]={}}, _attr_table = {[0]={{},{}}}, - _clear_attrs = {}, + _clear_attrs = nil, _new_attrs = false, _width = width, _height = height, @@ -202,12 +202,8 @@ function Screen:get_default_attr_ids() return deepcopy(self._default_attr_ids) end -function Screen:set_default_attr_ignore(attr_ignore) - self._default_attr_ignore = attr_ignore -end - -function Screen:set_hlstate_cterm(val) - self._hlstate_cterm = val +function Screen:set_rgb_cterm(val) + self._rgb_cterm = val end function Screen:attach(options, session) @@ -223,7 +219,7 @@ function Screen:attach(options, session) self._session = session self._options = options - self._clear_attrs = (options.ext_linegrid and {{},{}}) or {} + self._clear_attrs = (not options.ext_linegrid) and {} or nil self:_handle_resize(self._width, self._height) self.uimeths.attach(self._width, self._height, options) if self._options.rgb == nil then @@ -259,13 +255,13 @@ end -- canonical order of ext keys, used to generate asserts local ext_keys = { 'popupmenu', 'cmdline', 'cmdline_block', 'wildmenu_items', 'wildmenu_pos', - 'messages', 'showmode', 'showcmd', 'ruler', 'float_pos', + 'messages', '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, attr_ignore]) +-- screen:expect(grid, [attr_ids]) -- screen:expect(condition) -- or keyword args (supports more options): -- screen:expect{grid=[[...]], cmdline={...}, condition=function() ... end} @@ -274,7 +270,7 @@ local ext_keys = { -- grid: Expected screen state (string). Each line represents a screen -- row. Last character of each row (typically "|") is stripped. -- Common indentation is stripped. --- Lines containing only "{IGNORE}|" are skipped. +-- "{MATCH:x}|" lines are 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 @@ -282,8 +278,6 @@ local ext_keys = { -- attributes in the final state are an error. -- Use screen:set_default_attr_ids() to define attributes for many -- expect() calls. --- attr_ignore: Ignored text attributes, or `true` to ignore all. By default --- nothing is ignored. -- 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 @@ -318,13 +312,13 @@ local ext_keys = { -- cmdline_block: Expected ext_cmdline block (for function definitions) -- wildmenu_items: Expected items for ext_wildmenu -- wildmenu_pos: Expected position for ext_wildmenu -function Screen:expect(expected, attr_ids, attr_ignore, ...) +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 or attr_ignore ~= nil)) - local is_key = {grid=true, attr_ids=true, attr_ignore=true, condition=true, + assert(not (attr_ids ~= nil)) + local is_key = {grid=true, attr_ids=true, condition=true, any=true, mode=true, unchanged=true, intermediate=true, reset=true, timeout=true, request_cb=true, hl_groups=true} for _, v in ipairs(ext_keys) do @@ -337,14 +331,13 @@ function Screen:expect(expected, attr_ids, attr_ignore, ...) end grid = expected.grid attr_ids = expected.attr_ids - attr_ignore = expected.attr_ignore condition = expected.condition assert(not (expected.any ~= nil and grid ~= nil)) elseif type(expected) == "string" then grid = expected expected = {} elseif type(expected) == "function" then - assert(not (attr_ids ~= nil or attr_ignore ~= nil)) + assert(not (attr_ids ~= nil)) condition = expected expected = {} else @@ -361,10 +354,9 @@ function Screen:expect(expected, attr_ids, attr_ignore, ...) end local attr_state = { ids = attr_ids or self._default_attr_ids, - ignore = attr_ignore or self._default_attr_ignore, } - if self._options.ext_hlstate then - attr_state.id_to_index = self:hlstate_check_attrs(attr_state.ids or {}) + 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() @@ -375,8 +367,8 @@ function Screen:expect(expected, attr_ids, attr_ignore, ...) end end - if self._options.ext_hlstate and self._new_attrs then - attr_state.id_to_index = self:hlstate_check_attrs(attr_state.ids or {}) + if self._options.ext_linegrid and self._new_attrs then + attr_state.id_to_index = self:linegrid_check_attrs(attr_state.ids or {}) end local actual_rows = self:render(not expected.any, attr_state) @@ -399,9 +391,10 @@ function Screen:expect(expected, attr_ids, attr_ignore, ...) err_msg = "Expected screen height " .. #expected_rows .. ' differs from actual height ' .. #actual_rows .. '.' end - for i = 1, #expected_rows do - msg_expected_rows[i] = expected_rows[i] - if expected_rows[i] ~= actual_rows[i] and expected_rows[i] ~= "{IGNORE}|" then + for i, row in ipairs(expected_rows) do + msg_expected_rows[i] = row + local m = (row ~= actual_rows[i] and row:match('{MATCH:(.*)}') or nil) + if row ~= actual_rows[i] and (not m or not actual_rows[i]:match(m)) then msg_expected_rows[i] = '*' .. msg_expected_rows[i] if i <= #actual_rows then actual_rows[i] = '*' .. actual_rows[i] @@ -429,6 +422,9 @@ screen:redraw_debug() to show all intermediate screen states. ]]) if expected.mode ~= nil then extstate.mode = self.mode end + if expected.win_viewport == nil then + extstate.win_viewport = nil + end -- Convert assertion errors into invalid screen state descriptions. for _, k in ipairs(concat_tables(ext_keys, {'mode'})) do @@ -584,7 +580,7 @@ asynchronous (feed(), nvim_input()) and synchronous API calls. if err then - assert(false, err) + busted.fail(err, 3) elseif did_warn then local tb = debug.traceback() local index = string.find(tb, '\n%s*%[C]') @@ -614,17 +610,12 @@ function Screen:_redraw(updates) for i = 2, #update do local handler_name = '_handle_'..method local handler = self[handler_name] - if handler ~= nil then - 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)) - end - else - assert(self._on_event, - "Add Screen:"..handler_name.." or call Screen:set_on_event_handler") - self._on_event(method, update[i]) + 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)) end end if k == #updates and method == "flush" then @@ -634,10 +625,6 @@ function Screen:_redraw(updates) return did_flush end -function Screen:set_on_event_handler(callback) - self._on_event = callback -end - function Screen:_handle_resize(width, height) self:_handle_grid_resize(1, width, height) self._scroll_region = { @@ -743,6 +730,7 @@ function Screen:_handle_grid_destroy(grid) self._grids[grid] = nil if self._options.ext_multigrid then self.win_position[grid] = nil + self.win_viewport[grid] = nil end end @@ -763,14 +751,24 @@ function Screen:_handle_grid_cursor_goto(grid, row, col) end function Screen:_handle_win_pos(grid, win, startrow, startcol, width, height) - self.win_position[grid] = { - win = win, - startrow = startrow, - startcol = startcol, - width = width, - height = height - } - self.float_pos[grid] = nil + self.win_position[grid] = { + win = win, + startrow = startrow, + startcol = startcol, + width = width, + height = height + } + self.float_pos[grid] = nil +end + +function Screen:_handle_win_viewport(grid, win, topline, botline, curline, curcol) + self.win_viewport[grid] = { + win = win, + topline = topline, + botline = botline, + curline = curline, + curcol = curcol + } end function Screen:_handle_win_float_pos(grid, ...) @@ -898,19 +896,16 @@ function Screen:_handle_grid_line(grid, row, col, items) assert(self._options.ext_linegrid) local line = self._grids[grid].rows[row+1] local colpos = col+1 - local hl = self._clear_attrs local hl_id = 0 for _,item in ipairs(items) do local text, hl_id_cell, count = unpack(item) if hl_id_cell ~= nil then hl_id = hl_id_cell - hl = self._attr_table[hl_id] end for _ = 1, (count or 1) do local cell = line[colpos] cell.text = text cell.hl_id = hl_id - cell.attrs = hl colpos = colpos+1 end end @@ -1070,6 +1065,7 @@ function Screen:_clear_row_section(grid, rownum, startcol, stopcol, invalid) for i = startcol, stopcol do row[i].text = (invalid and '�' or ' ') row[i].attrs = self._clear_attrs + row[i].hl_id = 0 end end @@ -1100,11 +1096,7 @@ function Screen:_row_repr(gridnr, rownr, attr_state, cursor) end if not did_window then - local attrs = row[i].attrs - if self._options.ext_linegrid then - attrs = attrs[(self._options.rgb and 1) or 2] - end - local attr_id = self:_get_attr_id(attr_state, attrs, row[i].hl_id) + local attr_id = self:_get_attr_id(attr_state, row[i].attrs, row[i].hl_id) if current_attr_id and attr_id ~= current_attr_id then -- close current attribute bracket table.insert(rv, '}') @@ -1153,6 +1145,8 @@ function Screen:_extstate_repr(attr_state) messages[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, @@ -1164,7 +1158,8 @@ function Screen:_extstate_repr(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 + float_pos=self.float_pos, + win_viewport=win_viewport, } end @@ -1239,10 +1234,6 @@ function Screen:render(headers, attr_state, preview) return rv end -local remove_all_metatables = function(item, path) - if path[#path] ~= inspect.METATABLE then return item end -end - -- Returns the current screen state in the form of a screen:expect() -- keyword-args map. function Screen:get_snapshot(attrs, ignore) @@ -1261,8 +1252,8 @@ function Screen:get_snapshot(attrs, ignore) attr_state.ids[i] = a end end - if self._options.ext_hlstate then - attr_state.id_to_index = self:hlstate_check_attrs(attr_state.ids) + if self._options.ext_linegrid then + attr_state.id_to_index = self:linegrid_check_attrs(attr_state.ids) end local lines = self:render(true, attr_state, true) @@ -1292,6 +1283,26 @@ function Screen:get_snapshot(attrs, ignore) return kwargs, ext_state, attr_state end +local function fmt_ext_state(name, state) + 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.."},\n") + end + return str .. "}" + else + -- TODO(bfredl): improve formatting of more states + local function remove_all_metatables(item, path) + if path[#path] ~= inspect.METATABLE then + return item + end + end + return inspect(state,{process=remove_all_metatables}) + end +end + function Screen:print_snapshot(attrs, ignore) local kwargs, ext_state, attr_state = self:get_snapshot(attrs, ignore) local attrstr = "" @@ -1299,8 +1310,8 @@ function Screen:print_snapshot(attrs, ignore) local attrstrs = {} for i, a in pairs(attr_state.ids) do local dict - if self._options.ext_hlstate then - dict = self:_pprint_hlstate(a) + if self._options.ext_linegrid then + dict = self:_pprint_hlitem(a) else dict = "{"..self:_pprint_attrs(a).."}" end @@ -1314,9 +1325,8 @@ function Screen:print_snapshot(attrs, ignore) print(kwargs.grid) io.stdout:write( "]]"..attrstr) for _, k in ipairs(ext_keys) do - if ext_state[k] ~= nil then - -- TODO(bfredl): improve formatting - io.stdout:write(", "..k.."="..inspect(ext_state[k],{process=remove_all_metatables})) + 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])) end end print("}\n") @@ -1328,37 +1338,41 @@ function Screen:_insert_hl_id(attr_state, hl_id) return attr_state.id_to_index[hl_id] end local raw_info = self._hl_info[hl_id] - local info = {} - if #raw_info > 1 then - for i, item in ipairs(raw_info) do - info[i] = self:_insert_hl_id(attr_state, item.id) - end - else - info[1] = {} - for k, v in pairs(raw_info[1]) do - if k ~= "id" then - info[1][k] = v + local info = nil + if self._options.ext_hlstate then + info = {} + if #raw_info > 1 then + for i, item in ipairs(raw_info) do + info[i] = self:_insert_hl_id(attr_state, item.id) + end + else + info[1] = {} + for k, v in pairs(raw_info[1]) do + if k ~= "id" then + info[1][k] = v + end end end end local entry = self._attr_table[hl_id] local attrval - if self._hlstate_cterm then + if self._rgb_cterm then attrval = {entry[1], entry[2], info} -- unpack() doesn't work - else + elseif self._options.ext_hlstate then attrval = {entry[1], info} + else + attrval = self._options.rgb and entry[1] or entry[2] end - table.insert(attr_state.ids, attrval) attr_state.id_to_index[hl_id] = #attr_state.ids return #attr_state.ids end -function Screen:hlstate_check_attrs(attrs) +function Screen:linegrid_check_attrs(attrs) local id_to_index = {} - for i = 1,#self._attr_table do + for i, def_attr in pairs(self._attr_table) do local iinfo = self._hl_info[i] local matchinfo = {} if #iinfo > 1 then @@ -1370,13 +1384,17 @@ function Screen:hlstate_check_attrs(attrs) end for k,v in pairs(attrs) do local attr, info, attr_rgb, attr_cterm - if self._hlstate_cterm then + if self._rgb_cterm then attr_rgb, attr_cterm, info = unpack(v) attr = {attr_rgb, attr_cterm} - else + info = info or {} + elseif self._options.ext_hlstate then attr, info = unpack(v) + else + attr = v + info = {} end - if self:_equal_attr_def(attr, self._attr_table[i]) then + if self:_equal_attr_def(attr, def_attr) then if #info == #matchinfo then local match = false if #info == 1 then @@ -1397,24 +1415,32 @@ function Screen:hlstate_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] = "" + end end return id_to_index end -function Screen:_pprint_hlstate(item) +function Screen:_pprint_hlitem(item) -- print(inspect(item)) - local attrdict = "{"..self:_pprint_attrs(item[1]).."}, " + 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 - if self._hlstate_cterm then - attrdict2 = "{"..self:_pprint_attrs(item[2]).."}, " + local descdict = "" + if self._rgb_cterm then + attrdict2 = ", {"..self:_pprint_attrs(item[2], true).."}" hlinfo = item[3] else attrdict2 = "" hlinfo = item[2] end - local descdict = "{"..self:_pprint_hlinfo(hlinfo).."}" - return "{"..attrdict..attrdict2..descdict.."}" + 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) @@ -1434,13 +1460,15 @@ function Screen:_pprint_hlinfo(states) end -function Screen:_pprint_attrs(attrs) +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 @@ -1464,9 +1492,11 @@ function Screen:_get_attr_id(attr_state, attrs, hl_id) return end - if self._options.ext_hlstate then + if self._options.ext_linegrid then local id = attr_state.id_to_index[hl_id] - if id ~= nil or hl_id == 0 then + if id == "" then -- sentinel for empty it + return nil + elseif id ~= nil then return id end if attr_state.mutable then @@ -1476,9 +1506,7 @@ function Screen:_get_attr_id(attr_state, attrs, hl_id) end return "UNEXPECTED "..self:_pprint_attrs(self._attr_table[hl_id][1]) else - if self:_equal_attrs(attrs, {}) or - attr_state.ignore == true or - self:_attr_index(attr_state.ignore, attrs) ~= nil then + if self:_equal_attrs(attrs, {}) then -- ignore this attrs return nil end @@ -1497,10 +1525,12 @@ function Screen:_get_attr_id(attr_state, attrs, hl_id) end function Screen:_equal_attr_def(a, b) - if self._hlstate_cterm then + if self._rgb_cterm then return self:_equal_attrs(a[1],b[1]) and self:_equal_attrs(a[2],b[2]) - else + elseif self._options.rgb then return self:_equal_attrs(a,b[1]) + else + return self:_equal_attrs(a,b[2]) end end @@ -1510,7 +1540,8 @@ function Screen:_equal_attrs(a, b) 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 + a.strikethrough == b.strikethrough and + a.fg_indexed == b.fg_indexed and a.bg_indexed == b.bg_indexed end function Screen:_equal_info(a, b) |