test(messages/cmdline_spec): convert highlight IDs to name and format (#34845)

Problem:  Hardcoded highlight IDs for ext_messages/cmdline output need
          to be adjusted everytime a builtin highlight group is added.
Solution: Store a global map of default highlights through nvim_get_hl()
          and fetch missing (custom) highlight groups through synIDattr().
          Use more compact formatting for screen:expect().
This commit is contained in:
luukvbaal
2025-07-09 11:33:19 +02:00
committed by GitHub
parent 3c9484b550
commit 3a3484be29
6 changed files with 1037 additions and 1631 deletions

View File

@ -749,8 +749,8 @@ This UI extension delegates presentation of the |cmdline| (except 'wildmenu').
For command-line 'wildmenu' UI events, activate |ui-popupmenu|.
["cmdline_show", content, pos, firstc, prompt, indent, level, hl_id] ~
content: List of [attrs, string]
[[{}, "t"], [attrs, "est"], ...]
content: List of [attrs, string, hl_id]
[[{}, "t", hl_id], [attrs, "est", hl_id], ...]
Triggered when the cmdline is displayed or changed.
The `content` is the full content that should be displayed in the

View File

@ -3503,26 +3503,29 @@ static void ui_ext_cmdline_show(CmdlineInfo *line)
}
char *buf = arena_alloc(&arena, len, false);
memset(buf, '*', len);
Array item = arena_array(&arena, 2);
Array item = arena_array(&arena, 3);
ADD_C(item, INTEGER_OBJ(0));
ADD_C(item, STRING_OBJ(cbuf_as_string(buf, len)));
ADD_C(item, INTEGER_OBJ(0));
ADD_C(content, ARRAY_OBJ(item));
} else if (kv_size(line->last_colors.colors)) {
content = arena_array(&arena, kv_size(line->last_colors.colors));
for (size_t i = 0; i < kv_size(line->last_colors.colors); i++) {
CmdlineColorChunk chunk = kv_A(line->last_colors.colors, i);
Array item = arena_array(&arena, 2);
Array item = arena_array(&arena, 3);
ADD_C(item, INTEGER_OBJ(chunk.hl_id == 0 ? 0 : syn_id2attr(chunk.hl_id)));
assert(chunk.end >= chunk.start);
ADD_C(item, STRING_OBJ(cbuf_as_string(line->cmdbuff + chunk.start,
(size_t)(chunk.end - chunk.start))));
ADD_C(item, INTEGER_OBJ(chunk.hl_id));
ADD_C(content, ARRAY_OBJ(item));
}
} else {
Array item = arena_array(&arena, 2);
Array item = arena_array(&arena, 3);
ADD_C(item, INTEGER_OBJ(0));
ADD_C(item, CSTR_AS_OBJ(line->cmdbuff));
ADD_C(item, INTEGER_OBJ(0));
content = arena_array(&arena, 1);
ADD_C(content, ARRAY_OBJ(item));
}
@ -3549,6 +3552,7 @@ void ui_ext_cmdline_block_append(size_t indent, const char *line)
Array item = ARRAY_DICT_INIT;
ADD(item, INTEGER_OBJ(0));
ADD(item, CSTR_AS_OBJ(buf));
ADD(item, INTEGER_OBJ(0));
Array content = ARRAY_DICT_INIT;
ADD(content, ARRAY_OBJ(item));
ADD(cmdline_block, ARRAY_OBJ(content));

View File

@ -191,11 +191,7 @@ describe('vim.ui_attach', function()
^1 |
{1:~ }|*4
]],
cmdline = { {
content = { { '' } },
firstc = ':',
pos = 0,
} },
cmdline = { { content = { { '' } }, firstc = ':', pos = 0 } },
})
feed('version<CR>')
screen:expect({
@ -203,7 +199,6 @@ describe('vim.ui_attach', function()
^2 |
{1:~ }|*4
]],
cmdline = { { abort = false } },
condition = function()
eq('list_cmd', screen.messages[1].kind)
screen.messages = {} -- Ignore the build dependent :version content
@ -216,28 +211,12 @@ describe('vim.ui_attach', function()
{1:~ }|*4
]],
cmdline = {
{
content = { { '' } },
hl_id = 10,
pos = 0,
prompt = '[Y]es, (N)o, (C)ancel: ',
},
},
messages = {
{
content = { { '\nSave changes?\n', 6, 10 } },
kind = 'confirm',
},
{ content = { { '' } }, hl = 'MoreMsg', pos = 0, prompt = '[Y]es, (N)o, (C)ancel: ' },
},
messages = { { content = { { '\nSave changes?\n', 6, 'MoreMsg' } }, kind = 'confirm' } },
})
feed('n')
screen:expect({
grid = [[
^4 |
{1:~ }|*4
]],
cmdline = { { abort = false } },
})
screen:expect_unchanged()
end)
it("preserved 'incsearch/command' screen state after :redraw from ext_cmdline", function()
@ -257,37 +236,31 @@ describe('vim.ui_attach', function()
]])
-- Updates a cmdline window
feed(':cmdline')
screen:expect({
grid = [[
cmdline |
{2:cmdline [+] }|
fooba^r |
{3:[No Name] [+] }|
|
]],
})
screen:expect([[
cmdline |
{2:cmdline [+] }|
fooba^r |
{3:[No Name] [+] }|
|
]])
-- Does not clear 'incsearch' highlighting
feed('<Esc>/foo')
screen:expect({
grid = [[
foo |
{2:cmdline [+] }|
{2:foo}ba^r |
{3:[No Name] [+] }|
|
]],
})
screen:expect([[
foo |
{2:cmdline [+] }|
{2:foo}ba^r |
{3:[No Name] [+] }|
|
]])
-- Shows new cmdline state during 'inccommand'
feed('<Esc>:%s/bar/baz')
screen:expect({
grid = [[
%s/bar/baz |
{2:cmdline [+] }|
foo{10:ba^z} |
{3:[No Name] [+] }|
|
]],
})
screen:expect([[
%s/bar/baz |
{2:cmdline [+] }|
foo{10:ba^z} |
{3:[No Name] [+] }|
|
]])
end)
it('msg_show in fast context', function()
@ -314,10 +287,9 @@ describe('vim.ui_attach', function()
lled in a fast event context |
{1:~ }|
]],
cmdline = { { abort = false } },
messages = {
{
content = { { 'E122: Function Foo already exists, add ! to replace it', 9, 6 } },
content = { { 'E122: Function Foo already exists, add ! to replace it', 9, 'ErrorMsg' } },
history = true,
kind = 'emsg',
},

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -81,7 +81,8 @@ end
--- @field cmdline table<integer,table>
--- @field cmdline_hide_level integer?
--- @field cmdline_block table[]
--- @field hl_groups table<string,integer>
--- @field hl_groups table<string,integer> Highlight group to attr ID map
--- @field hl_names table<integer,string> Highlight ID to group map
--- @field messages table<integer,table>
--- @field private _cursor {grid:integer,row:integer,col:integer}
--- @field private _grids table<integer,test.functional.ui.screen.Grid>
@ -154,6 +155,11 @@ local function _init_colors()
[29] = { foreground = Screen.colors.SlateBlue, bold = true },
[30] = { background = Screen.colors.Red },
}
Screen._global_hl_names = {}
for group in pairs(n.api.nvim_get_hl(0, {})) do
Screen._global_hl_names[n.api.nvim_get_hl_id_by_name(group)] = group
end
end
--- @class test.functional.ui.screen.Opts
@ -210,6 +216,7 @@ function Screen.new(width, height, options, session)
showcmd = {},
ruler = {},
hl_groups = {},
hl_names = vim.deepcopy(Screen._global_hl_names),
_default_attr_ids = nil,
mouse_enabled = true,
_attrs = {},
@ -1343,12 +1350,12 @@ function Screen:_handle_cmdline_show(content, pos, firstc, prompt, indent, level
firstc = firstc,
prompt = prompt,
indent = indent,
hl_id = prompt and hl_id,
hl = hl_id,
}
end
function Screen:_handle_cmdline_hide(level, abort)
self.cmdline[level] = { abort = abort }
self.cmdline[level] = abort and { abort = abort } or nil
self.cmdline_hide_level = level
end
@ -1485,6 +1492,13 @@ function Screen:_row_repr(gridnr, rownr, attr_state, cursor)
return table.concat(rv, '') --:gsub('%s+$', '')
end
local function hl_id_to_name(self, id)
if id and id > 0 and not self.hl_names[id] then
self.hl_names[id] = n.fn.synIDattr(id, 'name')
end
return id and self.hl_names[id] or nil
end
function Screen:_extstate_repr(attr_state)
local cmdline = {}
for i, entry in pairs(self.cmdline) do
@ -1492,6 +1506,7 @@ function Screen:_extstate_repr(attr_state)
if entry.content ~= nil then
entry.content = self:_chunks_repr(entry.content, attr_state)
end
entry.hl = hl_id_to_name(self, entry.hl)
cmdline[i] = entry
end
@ -1552,7 +1567,8 @@ 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, attr_id and id or nil }
repr_chunks[i] = { text, attr_id }
repr_chunks[i][#repr_chunks[i] + 1] = hl_id_to_name(self, id)
end
return repr_chunks
end