neovim/test/functional/ui/hlstate_spec.lua
Justin M. Keyes 5792546777
refactor(tests): rename terminal/testutil.lua => testterm.lua #30372
This module is generally used by any tests that need the full Nvim TUI
instead of `screen.lua`. Thus it should live in `functional/` instead of
in `functional/terminal/`.
2024-09-15 03:28:14 -07:00

593 lines
20 KiB
Lua

local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local Screen = require('test.functional.ui.screen')
local tt = require('test.functional.testterm')
local clear, insert = n.clear, n.insert
local command = n.command
local api = n.api
local testprg = n.testprg
local skip = t.skip
local is_os = t.is_os
describe('ext_hlstate detailed highlights', function()
local screen
before_each(function()
clear()
command('syntax on')
command('hi VertSplit gui=reverse')
screen = Screen.new(40, 8)
screen:attach({ ext_hlstate = true })
end)
after_each(function()
screen:detach()
end)
it('work with combined UI and syntax highlights', function()
insert([[
these are some lines
with colorful text]])
api.nvim_buf_add_highlight(0, -1, 'String', 0, 10, 14)
api.nvim_buf_add_highlight(0, -1, 'Statement', 1, 5, -1)
command('/th co')
screen:expect(
[[
these are {1:some} lines |
^wi{2:th }{4:co}{3:lorful text} |
{5:~ }|*5
{8:search hit BOTTOM, continuing at TOP}{7: }|
]],
{
[1] = {
{ foreground = Screen.colors.Magenta },
{ { hi_name = 'Constant', kind = 'syntax' } },
},
[2] = {
{ background = Screen.colors.Yellow },
{ { hi_name = 'Search', ui_name = 'Search', kind = 'ui' } },
},
[3] = {
{ bold = true, foreground = Screen.colors.Brown },
{ { hi_name = 'Statement', kind = 'syntax' } },
},
[4] = {
{ bold = true, background = Screen.colors.Yellow, foreground = Screen.colors.Brown },
{ 3, 2 },
},
[5] = {
{ bold = true, foreground = Screen.colors.Blue1 },
{ { hi_name = 'NonText', ui_name = 'EndOfBuffer', kind = 'ui' } },
},
[6] = {
{ foreground = Screen.colors.Red },
{ { hi_name = 'WarningMsg', ui_name = 'WarningMsg', kind = 'ui' } },
},
[7] = { {}, { { hi_name = 'MsgArea', ui_name = 'MsgArea', kind = 'ui' } } },
[8] = { { foreground = Screen.colors.Red }, { 7, 6 } },
}
)
end)
it('work with cleared UI highlights', function()
screen:set_default_attr_ids({
[1] = { {}, { { hi_name = 'Normal', ui_name = 'WinSeparator', kind = 'ui' } } },
[2] = {
{ bold = true, foreground = Screen.colors.Blue1 },
{ { hi_name = 'NonText', ui_name = 'EndOfBuffer', kind = 'ui' } },
},
[3] = {
{ bold = true, reverse = true },
{ { hi_name = 'StatusLine', ui_name = 'StatusLine', kind = 'ui' } },
},
[4] = {
{ reverse = true },
{ { hi_name = 'StatusLineNC', ui_name = 'StatusLineNC', kind = 'ui' } },
},
[5] = { {}, { { hi_name = 'StatusLine', ui_name = 'StatusLine', kind = 'ui' } } },
[6] = { {}, { { hi_name = 'StatusLineNC', ui_name = 'StatusLineNC', kind = 'ui' } } },
[7] = { {}, { { hi_name = 'MsgArea', ui_name = 'MsgArea', kind = 'ui' } } },
})
command('hi clear WinSeparator')
command('vsplit')
screen:expect([[
^ {1:│} |
{2:~ }{1:│}{2:~ }|*5
{3:[No Name] }{4:[No Name] }|
{7: }|
]])
command('hi clear StatusLine | hi clear StatuslineNC')
screen:expect([[
^ {1:│} |
{2:~ }{1:│}{2:~ }|*5
{5:[No Name] }{6:[No Name] }|
{7: }|
]])
-- redrawing is done even if visible highlights didn't change
command('wincmd w')
screen:expect([[
{1:│}^ |
{2:~ }{1:│}{2:~ }|*5
{6:[No Name] }{5:[No Name] }|
{7: }|
]])
end)
it('work with window-local highlights', function()
screen:set_default_attr_ids({
[1] = {
{ foreground = Screen.colors.Brown },
{ { hi_name = 'LineNr', ui_name = 'LineNr', kind = 'ui' } },
},
[2] = {
{ bold = true, foreground = Screen.colors.Blue1 },
{ { hi_name = 'NonText', ui_name = 'EndOfBuffer', kind = 'ui' } },
},
[3] = {
{ bold = true, reverse = true },
{ { hi_name = 'StatusLine', ui_name = 'StatusLine', kind = 'ui' } },
},
[4] = {
{ reverse = true },
{ { hi_name = 'StatusLineNC', ui_name = 'StatusLineNC', kind = 'ui' } },
},
[5] = {
{ background = Screen.colors.Red, foreground = Screen.colors.Grey100 },
{ { hi_name = 'ErrorMsg', ui_name = 'LineNr', kind = 'ui' } },
},
[6] = {
{ bold = true, reverse = true },
{ { hi_name = 'Normal', ui_name = 'Normal', kind = 'ui' } },
},
[7] = { { foreground = Screen.colors.Brown, bold = true, reverse = true }, { 6, 1 } },
[8] = { { foreground = Screen.colors.Blue1, bold = true, reverse = true }, { 6, 14 } },
[9] = {
{ bold = true, foreground = Screen.colors.Brown },
{ { hi_name = 'NormalNC', ui_name = 'NormalNC', kind = 'ui' } },
},
[10] = { { bold = true, foreground = Screen.colors.Brown }, { 9, 1 } },
[11] = { { bold = true, foreground = Screen.colors.Blue1 }, { 9, 14 } },
[12] = { {}, { { hi_name = 'MsgArea', ui_name = 'MsgArea', kind = 'ui' } } },
[13] = {
{ background = Screen.colors.Red1, foreground = Screen.colors.Gray100 },
{ { ui_name = 'LineNr', kind = 'ui', hi_name = 'LineNr' } },
},
[14] = {
{ bold = true, foreground = Screen.colors.Blue },
{ { ui_name = 'EndOfBuffer', kind = 'ui', hi_name = 'EndOfBuffer' } },
},
})
command('set number')
command('split')
-- NormalNC is not applied if not set, to avoid spurious redraws
screen:expect([[
{1: 1 }^ |
{2:~ }|*2
{3:[No Name] }|
{1: 1 } |
{2:~ }|
{4:[No Name] }|
{12: }|
]])
command('set winhl=LineNr:ErrorMsg')
screen:expect {
grid = [[
{13: 1 }^ |
{14:~ }|*2
{3:[No Name] }|
{1: 1 } |
{2:~ }|
{4:[No Name] }|
{12: }|
]],
}
command('set winhl=Normal:MsgSeparator,NormalNC:Statement')
screen:expect([[
{7: 1 }{6:^ }|
{8:~ }|*2
{3:[No Name] }|
{1: 1 } |
{2:~ }|
{4:[No Name] }|
{12: }|
]])
command('wincmd w')
screen:expect([[
{10: 1 }{9: }|
{11:~ }|*2
{4:[No Name] }|
{1: 1 }^ |
{2:~ }|
{3:[No Name] }|
{12: }|
]])
end)
it('work with :terminal', function()
skip(is_os('win'))
screen:set_default_attr_ids({
[1] = { {}, { { hi_name = 'TermCursorNC', ui_name = 'TermCursorNC', kind = 'ui' } } },
[2] = { { foreground = tonumber('0x00ccff'), fg_indexed = true }, { { kind = 'term' } } },
[3] = {
{ bold = true, foreground = tonumber('0x00ccff'), fg_indexed = true },
{
{ kind = 'term' },
},
},
[4] = { { foreground = tonumber('0x00ccff'), fg_indexed = true }, { 2, 1 } },
[5] = { { foreground = tonumber('0x40ffff'), fg_indexed = true }, { { kind = 'term' } } },
[6] = { { foreground = tonumber('0x40ffff'), fg_indexed = true }, { 5, 1 } },
[7] = { {}, { { hi_name = 'MsgArea', ui_name = 'MsgArea', kind = 'ui' } } },
})
command(("enew | call termopen(['%s'])"):format(testprg('tty-test')))
screen:expect([[
^tty ready |
{1: } |
|*5
{7: }|
]])
tt.feed_data('x ')
tt.set_fg(45)
tt.feed_data('y ')
tt.set_bold()
tt.feed_data('z\n')
-- TODO(bfredl): check if this distinction makes sense
if is_os('win') then
screen:expect([[
^tty ready |
x {5:y z} |
{1: } |
|*4
{7: }|
]])
else
screen:expect([[
^tty ready |
x {2:y }{3:z} |
{1: } |
|*4
{7: }|
]])
end
tt.feed_termcode('[A')
tt.feed_termcode('[2C')
if is_os('win') then
screen:expect([[
^tty ready |
x {6:y}{5: z} |
|*5
{7: }|
]])
else
screen:expect([[
^tty ready |
x {4:y}{2: }{3:z} |
|*5
{7: }|
]])
end
end)
it('can use independent cterm and rgb colors', function()
-- tell test module to save all attributes (doesn't change nvim options)
screen:set_rgb_cterm(true)
screen:set_default_attr_ids({
[1] = {
{ bold = true, foreground = Screen.colors.Blue1 },
{ foreground = 12 },
{ { hi_name = 'NonText', ui_name = 'EndOfBuffer', kind = 'ui' } },
},
[2] = {
{ reverse = true, foreground = Screen.colors.Red },
{ foreground = 10, italic = true },
{ { hi_name = 'NonText', ui_name = 'EndOfBuffer', kind = 'ui' } },
},
[3] = { {}, {}, { { hi_name = 'MsgArea', ui_name = 'MsgArea', kind = 'ui' } } },
})
screen:expect([[
^ |
{1:~ }|*6
{3: }|
]])
command('hi NonText guifg=Red gui=reverse ctermfg=Green cterm=italic')
screen:expect([[
^ |
{2:~ }|*6
{3: }|
]])
end)
it('combines deleted extmark highlights', function()
insert([[
line1
line2
line3
line4
line5
line6]])
screen:expect {
grid = [[
line1 |
line2 |
line3 |
line4 |
line5 |
line^6 |
{1:~ }|
{2: }|
]],
attr_ids = {
[1] = {
{ foreground = Screen.colors.Blue, bold = true },
{ { ui_name = 'EndOfBuffer', hi_name = 'NonText', kind = 'ui' } },
},
[2] = { {}, { { ui_name = 'MsgArea', hi_name = 'MsgArea', kind = 'ui' } } },
},
}
local ns = api.nvim_create_namespace('test')
local add_indicator = function(line, col)
api.nvim_buf_set_extmark(0, ns, line, col, {
hl_mode = 'combine',
priority = 2,
right_gravity = false,
virt_text = { { '|', 'Delimiter' } },
virt_text_win_col = 0,
virt_text_pos = 'overlay',
})
end
add_indicator(1, 0)
add_indicator(2, 0)
add_indicator(3, 0)
add_indicator(4, 0)
screen:expect {
grid = [[
line1 |
{1:|} line2 |
{1:|} line3 |
{1:|} line4 |
{1:|} line5 |
line^6 |
{2:~ }|
{3: }|
]],
attr_ids = {
[1] = {
{ foreground = Screen.colors.SlateBlue },
{ { hi_name = 'Special', kind = 'syntax' } },
},
[2] = {
{ bold = true, foreground = Screen.colors.Blue },
{ { ui_name = 'EndOfBuffer', kind = 'ui', hi_name = 'NonText' } },
},
[3] = { {}, { { ui_name = 'MsgArea', kind = 'ui', hi_name = 'MsgArea' } } },
},
}
n.feed('3ggV2jd')
--screen:redraw_debug()
screen:expect {
grid = [[
line1 |
{1:|} line2 |
{2:^|}ine6 |
{3:~ }|*4
{4:3 fewer lines }|
]],
attr_ids = {
[1] = {
{ foreground = Screen.colors.SlateBlue },
{ { kind = 'syntax', hi_name = 'Special' } },
},
[2] = { { foreground = Screen.colors.SlateBlue }, { 1, 1, 1 } },
[3] = {
{ bold = true, foreground = Screen.colors.Blue },
{ { kind = 'ui', ui_name = 'EndOfBuffer', hi_name = 'NonText' } },
},
[4] = { {}, { { kind = 'ui', ui_name = 'MsgArea', hi_name = 'MsgArea' } } },
},
}
end)
it('removes deleted extmark highlights with invalidate', function()
insert([[
line1
line2
line3
line4
line5
line6]])
screen:expect {
grid = [[
line1 |
line2 |
line3 |
line4 |
line5 |
line^6 |
{1:~ }|
{2: }|
]],
attr_ids = {
[1] = {
{ foreground = Screen.colors.Blue, bold = true },
{ { ui_name = 'EndOfBuffer', hi_name = 'NonText', kind = 'ui' } },
},
[2] = { {}, { { ui_name = 'MsgArea', hi_name = 'MsgArea', kind = 'ui' } } },
},
}
local ns = api.nvim_create_namespace('test')
local add_indicator = function(line, col)
api.nvim_buf_set_extmark(0, ns, line, col, {
hl_mode = 'combine',
priority = 2,
right_gravity = false,
virt_text = { { '|', 'Delimiter' } },
virt_text_win_col = 0,
virt_text_pos = 'overlay',
invalidate = true,
})
end
add_indicator(1, 0)
add_indicator(2, 0)
add_indicator(3, 0)
add_indicator(4, 0)
screen:expect {
grid = [[
line1 |
{1:|} line2 |
{1:|} line3 |
{1:|} line4 |
{1:|} line5 |
line^6 |
{2:~ }|
{3: }|
]],
attr_ids = {
[1] = {
{ foreground = Screen.colors.SlateBlue },
{ { hi_name = 'Special', kind = 'syntax' } },
},
[2] = {
{ bold = true, foreground = Screen.colors.Blue },
{ { ui_name = 'EndOfBuffer', kind = 'ui', hi_name = 'NonText' } },
},
[3] = { {}, { { ui_name = 'MsgArea', kind = 'ui', hi_name = 'MsgArea' } } },
},
}
n.feed('3ggV2jd')
--screen:redraw_debug()
screen:expect {
grid = [[
line1 |
{1:|} line2 |
^line6 |
{2:~ }|*4
{3:3 fewer lines }|
]],
attr_ids = {
[1] = {
{ foreground = Screen.colors.SlateBlue },
{ { kind = 'syntax', hi_name = 'Special' } },
},
[2] = {
{ foreground = Screen.colors.Blue, bold = true },
{ { kind = 'ui', ui_name = 'EndOfBuffer', hi_name = 'NonText' } },
},
[3] = { {}, { { kind = 'ui', ui_name = 'MsgArea', hi_name = 'MsgArea' } } },
},
}
end)
it('does not hang when combining too many highlights', function()
local num_lines = 500
insert('first line\n')
for _ = 1, num_lines do
insert([[
line
]])
end
insert('last line')
n.feed('gg')
screen:expect {
grid = [[
^first line |
line |*6
{1: }|
]],
attr_ids = {
[1] = { {}, { { kind = 'ui', hi_name = 'MsgArea', ui_name = 'MsgArea' } } },
},
}
local ns = api.nvim_create_namespace('test')
local add_indicator = function(line, col)
api.nvim_buf_set_extmark(0, ns, line, col, {
hl_mode = 'combine',
priority = 2,
right_gravity = false,
virt_text = { { '|', 'Delimiter' } },
virt_text_win_col = 0,
virt_text_pos = 'overlay',
})
end
for i = 1, num_lines do
add_indicator(i, 0)
end
screen:expect {
grid = [[
^first line |
{1:|} line |*6
{2: }|
]],
attr_ids = {
[1] = {
{ foreground = Screen.colors.SlateBlue },
{ { kind = 'syntax', hi_name = 'Special' } },
},
[2] = { {}, { { kind = 'ui', ui_name = 'MsgArea', hi_name = 'MsgArea' } } },
},
}
n.feed(string.format('3ggV%ijd', num_lines - 2))
--screen:redraw_debug(nil, nil, 100000)
local expected_ids = {}
for i = 1, num_lines - 1 do
expected_ids[i] = 1
end
screen:expect {
grid = string.format(
[[
first line |
{1:|} line |
{2:^|}ast line |
{3:~ }|*4
{4:%-40s}|
]],
tostring(num_lines - 1) .. ' fewer lines'
),
attr_ids = {
[1] = {
{ foreground = Screen.colors.SlateBlue },
{ { kind = 'syntax', hi_name = 'Special' } },
},
[2] = { { foreground = Screen.colors.SlateBlue }, expected_ids },
[3] = {
{ foreground = Screen.colors.Blue, bold = true },
{ { kind = 'ui', hi_name = 'NonText', ui_name = 'EndOfBuffer' } },
},
[4] = { {}, { { kind = 'ui', hi_name = 'MsgArea', ui_name = 'MsgArea' } } },
},
timeout = 100000,
}
end)
end)