local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local thelpers = require('test.functional.terminal.helpers') local feed, clear = helpers.feed, helpers.clear local api = helpers.api local testprg, command = helpers.testprg, helpers.command local nvim_prog_abs = helpers.nvim_prog_abs local eq, eval = helpers.eq, helpers.eval local fn = helpers.fn local nvim_set = helpers.nvim_set local is_os = helpers.is_os local skip = helpers.skip describe(':terminal highlight', function() local screen before_each(function() clear() screen = Screen.new(50, 7) screen:set_default_attr_ids({ [1] = { foreground = 45 }, [2] = { background = 46 }, [3] = { foreground = 45, background = 46 }, [4] = { bold = true, italic = true, underline = true, strikethrough = true }, [5] = { bold = true }, [6] = { foreground = 12 }, [7] = { bold = true, reverse = true }, [8] = { background = 11 }, [9] = { foreground = 130 }, [10] = { reverse = true }, [11] = { background = 11 }, [12] = { bold = true, underdouble = true }, [13] = { italic = true, undercurl = true }, }) screen:attach({ rgb = false }) command(("enew | call termopen(['%s'])"):format(testprg('tty-test'))) feed('i') screen:expect([[ tty ready | {10: } | |*4 {5:-- TERMINAL --} | ]]) end) local function descr(title, attr_num, set_attrs_fn) local function sub(s) local str = s:gsub('NUM', attr_num) return str end describe(title, function() before_each(function() set_attrs_fn() thelpers.feed_data('text') thelpers.clear_attrs() thelpers.feed_data('text') end) local function pass_attrs() skip(is_os('win')) screen:expect(sub([[ tty ready | {NUM:text}text{10: } | |*4 {5:-- TERMINAL --} | ]])) end it('will pass the corresponding attributes', pass_attrs) it('will pass the corresponding attributes on scrollback', function() skip(is_os('win')) pass_attrs() local lines = {} for i = 1, 8 do table.insert(lines, 'line' .. tostring(i)) end table.insert(lines, '') thelpers.feed_data(lines) screen:expect([[ line4 | line5 | line6 | line7 | line8 | {10: } | {5:-- TERMINAL --} | ]]) feed('gg') screen:expect(sub([[ ^tty ready | {NUM:text}textline1 | line2 | line3 | line4 | line5 | | ]])) end) end) end descr('foreground', 1, function() thelpers.set_fg(45) end) descr('background', 2, function() thelpers.set_bg(46) end) descr('foreground and background', 3, function() thelpers.set_fg(45) thelpers.set_bg(46) end) descr('bold, italics, underline and strikethrough', 4, function() thelpers.set_bold() thelpers.set_italic() thelpers.set_underline() thelpers.set_strikethrough() end) descr('bold and underdouble', 12, function() thelpers.set_bold() thelpers.set_underdouble() end) descr('italics and undercurl', 13, function() thelpers.set_italic() thelpers.set_undercurl() end) end) it(':terminal highlight has lower precedence than editor #9964', function() clear() local screen = Screen.new(30, 4) screen:set_default_attr_ids({ -- "Normal" highlight emitted by the child nvim process. N_child = { foreground = tonumber('0x4040ff'), background = tonumber('0xffff40'), fg_indexed = true, bg_indexed = true, }, -- "Search" highlight in the parent nvim process. S = { background = Screen.colors.Green, italic = true, foreground = Screen.colors.Red }, -- "Question" highlight in the parent nvim process. -- note: bg is indexed as it comes from the (cterm) child, while fg isn't as it comes from (rgb) parent Q = { background = tonumber('0xffff40'), bold = true, foreground = Screen.colors.SeaGreen4, bg_indexed = true, }, }) screen:attach({ rgb = true }) -- Child nvim process in :terminal (with cterm colors). fn.termopen({ nvim_prog_abs(), '-n', '-u', 'NORC', '-i', 'NONE', '--cmd', nvim_set .. ' notermguicolors', '+hi Normal ctermfg=Blue ctermbg=Yellow', '+norm! ichild nvim', '+norm! oline 2', }, { env = { VIMRUNTIME = os.getenv('VIMRUNTIME'), }, }) screen:expect([[ {N_child:^child nvim }| {N_child:line 2 }| {N_child: }| | ]]) command('hi Search gui=italic guifg=Red guibg=Green cterm=italic ctermfg=Red ctermbg=Green') feed('/nvim') screen:expect([[ {N_child:child }{S:^nvim}{N_child: }| {N_child:line 2 }| {N_child: }| /nvim | ]]) command('syntax keyword Question line') screen:expect([[ {N_child:child }{S:^nvim}{N_child: }| {Q:line}{N_child: 2 }| {N_child: }| /nvim | ]]) end) describe(':terminal highlight forwarding', function() local screen before_each(function() clear() screen = Screen.new(50, 7) screen:set_rgb_cterm(true) screen:set_default_attr_ids({ [1] = { { reverse = true }, { reverse = true } }, [2] = { { bold = true }, { bold = true } }, [3] = { { fg_indexed = true, foreground = tonumber('0xe0e000') }, { foreground = 3 } }, [4] = { { foreground = tonumber('0xff8000') }, {} }, }) screen:attach() command(("enew | call termopen(['%s'])"):format(testprg('tty-test'))) feed('i') screen:expect([[ tty ready | {1: } | |*4 {2:-- TERMINAL --} | ]]) end) it('will handle cterm and rgb attributes', function() skip(is_os('win')) thelpers.set_fg(3) thelpers.feed_data('text') thelpers.feed_termcode('[38:2:255:128:0m') thelpers.feed_data('color') thelpers.clear_attrs() thelpers.feed_data('text') screen:expect { grid = [[ tty ready | {3:text}{4:color}text{1: } | |*4 {2:-- TERMINAL --} | ]], } end) end) describe(':terminal highlight with custom palette', function() local screen before_each(function() clear() screen = Screen.new(50, 7) screen:set_default_attr_ids({ [1] = { foreground = tonumber('0x123456') }, -- no fg_indexed when overridden [2] = { foreground = 12 }, [3] = { bold = true, reverse = true }, [5] = { background = 11 }, [6] = { foreground = 130 }, [7] = { reverse = true }, [8] = { background = 11 }, [9] = { bold = true }, }) screen:attach({ rgb = true }) api.nvim_set_var('terminal_color_3', '#123456') command(("enew | call termopen(['%s'])"):format(testprg('tty-test'))) feed('i') screen:expect([[ tty ready | {7: } | |*4 {9:-- TERMINAL --} | ]]) end) it('will use the custom color', function() skip(is_os('win')) thelpers.set_fg(3) thelpers.feed_data('text') thelpers.clear_attrs() thelpers.feed_data('text') screen:expect([[ tty ready | {1:text}text{7: } | |*4 {9:-- TERMINAL --} | ]]) end) end) describe('synIDattr()', function() local screen before_each(function() clear() screen = Screen.new(50, 7) command('highlight Normal ctermfg=252 guifg=#ff0000 guibg=Black') -- Salmon #fa8072 Maroon #800000 command( 'highlight Keyword ctermfg=79 guifg=Salmon guisp=Maroon cterm=strikethrough gui=strikethrough' ) end) it('returns cterm-color if RGB-capable UI is _not_ attached', function() eq('252', eval('synIDattr(hlID("Normal"), "fg")')) eq('252', eval('synIDattr(hlID("Normal"), "fg#")')) eq('', eval('synIDattr(hlID("Normal"), "bg")')) eq('', eval('synIDattr(hlID("Normal"), "bg#")')) eq('79', eval('synIDattr(hlID("Keyword"), "fg")')) eq('79', eval('synIDattr(hlID("Keyword"), "fg#")')) eq('', eval('synIDattr(hlID("Keyword"), "sp")')) eq('', eval('synIDattr(hlID("Keyword"), "sp#")')) end) it('returns gui-color if "gui" arg is passed', function() eq('Black', eval('synIDattr(hlID("Normal"), "bg", "gui")')) eq('Maroon', eval('synIDattr(hlID("Keyword"), "sp", "gui")')) end) it('returns gui-color if RGB-capable UI is attached', function() screen:attach({ rgb = true }) eq('#ff0000', eval('synIDattr(hlID("Normal"), "fg")')) eq('Black', eval('synIDattr(hlID("Normal"), "bg")')) eq('Salmon', eval('synIDattr(hlID("Keyword"), "fg")')) eq('Maroon', eval('synIDattr(hlID("Keyword"), "sp")')) end) it('returns #RRGGBB value for fg#/bg#/sp#', function() screen:attach({ rgb = true }) eq('#ff0000', eval('synIDattr(hlID("Normal"), "fg#")')) eq('#000000', eval('synIDattr(hlID("Normal"), "bg#")')) eq('#fa8072', eval('synIDattr(hlID("Keyword"), "fg#")')) eq('#800000', eval('synIDattr(hlID("Keyword"), "sp#")')) end) it('returns color number if non-GUI', function() screen:attach({ rgb = false }) eq('252', eval('synIDattr(hlID("Normal"), "fg")')) eq('79', eval('synIDattr(hlID("Keyword"), "fg")')) end) it('returns "1" if group has given highlight attribute', function() local hl_attrs = { 'underline', 'undercurl', 'underdouble', 'underdotted', 'underdashed', 'strikethrough', } for _, hl_attr in ipairs(hl_attrs) do local context = 'using ' .. hl_attr .. ' attr' command('highlight Keyword cterm=' .. hl_attr .. ' gui=' .. hl_attr) eq('', eval('synIDattr(hlID("Normal"), "' .. hl_attr .. '")'), context) eq('1', eval('synIDattr(hlID("Keyword"), "' .. hl_attr .. '")'), context) eq('1', eval('synIDattr(hlID("Keyword"), "' .. hl_attr .. '", "gui")'), context) end end) end) describe('fg/bg special colors', function() local screen before_each(function() clear() screen = Screen.new(50, 7) command('highlight Normal ctermfg=145 ctermbg=16 guifg=#ff0000 guibg=Black') command('highlight Visual ctermfg=bg ctermbg=fg guifg=bg guibg=fg guisp=bg') end) it('resolve to "Normal" values', function() eq(eval('synIDattr(hlID("Normal"), "bg")'), eval('synIDattr(hlID("Visual"), "fg")')) eq(eval('synIDattr(hlID("Normal"), "bg#")'), eval('synIDattr(hlID("Visual"), "fg#")')) eq(eval('synIDattr(hlID("Normal"), "fg")'), eval('synIDattr(hlID("Visual"), "bg")')) eq(eval('synIDattr(hlID("Normal"), "fg#")'), eval('synIDattr(hlID("Visual"), "bg#")')) eq('bg', eval('synIDattr(hlID("Visual"), "fg", "gui")')) eq('bg', eval('synIDattr(hlID("Visual"), "fg#", "gui")')) eq('fg', eval('synIDattr(hlID("Visual"), "bg", "gui")')) eq('fg', eval('synIDattr(hlID("Visual"), "bg#", "gui")')) eq('bg', eval('synIDattr(hlID("Visual"), "sp", "gui")')) eq('bg', eval('synIDattr(hlID("Visual"), "sp#", "gui")')) end) it('resolve to "Normal" values in RGB-capable UI', function() screen:attach({ rgb = true }) eq('bg', eval('synIDattr(hlID("Visual"), "fg")')) eq(eval('synIDattr(hlID("Normal"), "bg#")'), eval('synIDattr(hlID("Visual"), "fg#")')) eq('fg', eval('synIDattr(hlID("Visual"), "bg")')) eq(eval('synIDattr(hlID("Normal"), "fg#")'), eval('synIDattr(hlID("Visual"), "bg#")')) eq('bg', eval('synIDattr(hlID("Visual"), "sp")')) eq(eval('synIDattr(hlID("Normal"), "bg#")'), eval('synIDattr(hlID("Visual"), "sp#")')) end) it('resolve after the "Normal" group is modified', function() screen:attach({ rgb = true }) local new_guibg = '#282c34' local new_guifg = '#abb2bf' command('highlight Normal guifg=' .. new_guifg .. ' guibg=' .. new_guibg) eq(new_guibg, eval('synIDattr(hlID("Visual"), "fg#")')) eq(new_guifg, eval('synIDattr(hlID("Visual"), "bg#")')) eq(new_guibg, eval('synIDattr(hlID("Visual"), "sp#")')) end) end)