Files
neovim/test/functional/api/highlight_spec.lua
Evgeni Chasnovski 7219b816ea fix(lsp): limit number of created highlight groups (#39133)
* fix(api): allow silencing "Too many highlight groups" error

Problem: Using Lua's `vim.api.nvim_set_hl(0, 'New', {...})` can fail if
  there are too many existing highlight groups. However, this error can
  not be silenced with `pcall`.

Solution: Make it possible to silence in `nvim_set_hl` and
  `nvim_get_hl_id_by_name`.

* fix(lsp): limit number of groups created by `document_color()`

Problem: A file can contain many string colors that would be highlighted
  by an LSP server. If this number crosses 19999 (maximum number of
  allowed highlight groups), there are general issues with creating
  other highlight groups, which can break functionality outside of
  `vim.lsp.document_color`.

Solution: Limit number of highlight groups that are created by
  `vim.lsp.document_color` to 10000 (half of allowed maximum).
  This is not a 100% solution (since there can exist more than 10000
  other highlight groups), but explicitly checking number of groups is
  slow and 10000 should (hopefully) be enough for most use cases.
2026-04-17 17:16:11 -07:00

773 lines
25 KiB
Lua

local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local clear, eq, neq = n.clear, t.eq, t.neq
local command = n.command
local exec_capture = n.exec_capture
local api = n.api
local fn = n.fn
local pcall_err = t.pcall_err
local ok = t.ok
local assert_alive = n.assert_alive
describe('API: set highlight', function()
local highlight_color = {
fg = tonumber('0xff0000'),
bg = tonumber('0x0032aa'),
ctermfg = 8,
ctermbg = 15,
}
local highlight1 = {
bg = highlight_color.bg,
fg = highlight_color.fg,
bold = true,
italic = true,
}
local highlight2_config = {
ctermbg = highlight_color.ctermbg,
ctermfg = highlight_color.ctermfg,
underline = true,
reverse = true,
}
local highlight2_result = {
ctermbg = highlight_color.ctermbg,
ctermfg = highlight_color.ctermfg,
underline = true,
reverse = true,
}
local highlight3_config = {
bg = highlight_color.bg,
fg = highlight_color.fg,
ctermbg = highlight_color.ctermbg,
ctermfg = highlight_color.ctermfg,
bold = true,
italic = true,
reverse = true,
underdashed = true,
strikethrough = true,
altfont = true,
dim = true,
blink = true,
conceal = true,
overline = true,
cterm = {
italic = true,
reverse = true,
strikethrough = true,
altfont = true,
dim = true,
blink = true,
conceal = true,
overline = true,
nocombine = true,
},
}
local highlight3_result_gui = {
bg = highlight_color.bg,
fg = highlight_color.fg,
bold = true,
italic = true,
reverse = true,
underdashed = true,
strikethrough = true,
altfont = true,
dim = true,
blink = true,
conceal = true,
overline = true,
}
local highlight3_result_cterm = {
ctermbg = highlight_color.ctermbg,
ctermfg = highlight_color.ctermfg,
italic = true,
reverse = true,
strikethrough = true,
altfont = true,
dim = true,
blink = true,
conceal = true,
overline = true,
nocombine = true,
}
local function get_ns()
local ns = api.nvim_create_namespace('Test_set_hl')
api.nvim_set_hl_ns(ns)
return ns
end
---@param expect table<string, any>
---@param result table<string, any>
---@param cterm? boolean
local function match(expect, result, cterm)
for k, v in pairs(expect) do
eq(v, cterm and result.cterm[k] or result[k])
end
end
before_each(clear)
it('validation', function()
eq(
"Invalid 'blend': out of range",
pcall_err(api.nvim_set_hl, 0, 'Test_hl3', { fg = '#FF00FF', blend = 999 })
)
eq(
"Invalid 'blend': expected Integer, got Array",
pcall_err(api.nvim_set_hl, 0, 'Test_hl3', { fg = '#FF00FF', blend = {} })
)
-- 'url' is rejected. #38162
eq("Invalid key: 'url'", pcall_err(api.nvim_set_hl, 0, 'Test', { url = 'https://example.com' }))
assert_alive()
end)
it('can set gui highlight', function()
local ns = get_ns()
api.nvim_set_hl(ns, 'Test_hl', highlight1)
match(highlight1, api.nvim_get_hl(ns, { name = 'Test_hl' }))
end)
it('can set cterm highlight', function()
local ns = get_ns()
api.nvim_set_hl(ns, 'Test_hl', highlight2_config)
match(highlight2_result, api.nvim_get_hl(ns, { name = 'Test_hl' }))
end)
it('can set empty cterm attr', function()
local ns = get_ns()
api.nvim_set_hl(ns, 'Test_hl', { cterm = {} })
eq({}, api.nvim_get_hl(ns, { name = 'Test_hl' }))
end)
it('cterm attr defaults to gui attr', function()
local ns = get_ns()
api.nvim_set_hl(ns, 'Test_hl', highlight1)
match({
bold = true,
italic = true,
}, api.nvim_get_hl(ns, { name = 'Test_hl' }))
end)
it('can overwrite attr for cterm #test', function()
local ns = get_ns()
api.nvim_set_hl(ns, 'Test_hl', highlight3_config)
match(highlight3_result_gui, api.nvim_get_hl(ns, { name = 'Test_hl' }))
match(highlight3_result_cterm, api.nvim_get_hl(ns, { name = 'Test_hl' }), true)
end)
it('only allows one underline attribute #22371', function()
local ns = get_ns()
api.nvim_set_hl(ns, 'Test_hl', {
underdouble = true,
underdotted = true,
cterm = {
underline = true,
undercurl = true,
},
})
local result = api.nvim_get_hl(ns, { name = 'Test_hl' })
match({ undercurl = true }, result, true)
match({ underdotted = true }, result)
end)
it('can set all underline cterm attributes #31385', function()
local ns = get_ns()
local attrs = { 'underline', 'undercurl', 'underdouble', 'underdotted', 'underdashed' }
for _, attr in ipairs(attrs) do
api.nvim_set_hl(ns, 'Test_' .. attr, { cterm = { [attr] = true } })
match({ [attr] = true }, api.nvim_get_hl(ns, { name = 'Test_' .. attr }), true)
end
end)
it('can set a highlight in the global namespace', function()
api.nvim_set_hl(0, 'Test_hl', highlight2_config)
eq(
'Test_hl xxx cterm=underline,reverse ctermfg=8 ctermbg=15 gui=underline,reverse',
exec_capture('highlight Test_hl')
)
api.nvim_set_hl(0, 'Test_hl', { background = highlight_color.bg })
eq('Test_hl xxx guibg=#0032aa', exec_capture('highlight Test_hl'))
api.nvim_set_hl(0, 'Test_hl2', highlight3_config)
eq(
'Test_hl2 xxx cterm=italic,reverse,strikethrough,altfont,dim,blink,conceal,overline,nocombine ctermfg=8 ctermbg=15 gui=bold,underdashed,italic,reverse,strikethrough,altfont,dim,blink,conceal,overline guifg=#ff0000 guibg=#0032aa',
exec_capture('highlight Test_hl2')
)
-- Colors are stored with the name they are defined, but
-- with canonical casing
api.nvim_set_hl(0, 'Test_hl3', { bg = 'reD', fg = 'bLue' })
eq('Test_hl3 xxx guifg=Blue guibg=Red', exec_capture('highlight Test_hl3'))
end)
it('can modify a highlight in the global namespace', function()
api.nvim_set_hl(0, 'Test_hl3', { bg = 'red', fg = 'blue' })
eq('Test_hl3 xxx guifg=Blue guibg=Red', exec_capture('highlight Test_hl3'))
api.nvim_set_hl(0, 'Test_hl3', { bg = 'red' })
eq('Test_hl3 xxx guibg=Red', exec_capture('highlight Test_hl3'))
api.nvim_set_hl(0, 'Test_hl3', { ctermbg = 9, ctermfg = 12 })
eq('Test_hl3 xxx ctermfg=12 ctermbg=9', exec_capture('highlight Test_hl3'))
api.nvim_set_hl(0, 'Test_hl3', { ctermbg = 'red', ctermfg = 'blue' })
eq('Test_hl3 xxx ctermfg=12 ctermbg=9', exec_capture('highlight Test_hl3'))
api.nvim_set_hl(0, 'Test_hl3', { ctermbg = 9 })
eq('Test_hl3 xxx ctermbg=9', exec_capture('highlight Test_hl3'))
eq(
"Invalid highlight color: 'redd'",
pcall_err(api.nvim_set_hl, 0, 'Test_hl3', { fg = 'redd' })
)
eq(
"Invalid highlight color: 'bleu'",
pcall_err(api.nvim_set_hl, 0, 'Test_hl3', { ctermfg = 'bleu' })
)
api.nvim_set_hl(0, 'Test_hl3', { fg = '#FF00FF' })
eq('Test_hl3 xxx guifg=#ff00ff', exec_capture('highlight Test_hl3'))
eq(
"Invalid highlight color: '#FF00FF'",
pcall_err(api.nvim_set_hl, 0, 'Test_hl3', { ctermfg = '#FF00FF' })
)
for _, fg_val in ipairs { nil, 'NONE', 'nOnE', '', -1 } do
api.nvim_set_hl(0, 'Test_hl3', { fg = fg_val })
eq('Test_hl3 xxx cleared', exec_capture('highlight Test_hl3'))
end
api.nvim_set_hl(0, 'Test_hl3', { fg = '#FF00FF', blend = 50 })
eq('Test_hl3 xxx guifg=#ff00ff blend=50', exec_capture('highlight Test_hl3'))
end)
it("correctly sets 'Normal' internal properties", function()
-- Normal has some special handling internally. #18024
api.nvim_set_hl(0, 'Normal', { fg = '#000083', bg = '#0000F3' })
eq({ fg = 131, bg = 243 }, api.nvim_get_hl(0, { name = 'Normal' }))
end)
it('does not segfault on invalid group name #20009', function()
eq(
'Vim:E5248: Invalid character in group name',
pcall_err(api.nvim_set_hl, 0, 'foo bar', { bold = true })
)
assert_alive()
end)
it('can be silenced if there are too many groups #38930', function()
local n_groups = vim.tbl_count(api.nvim_get_hl(0, {}))
local has_fail = false
for i = n_groups + 1, 20000 do
local _, msg = pcall(api.nvim_set_hl, 0, 'New_' .. i, { fg = '#000000' })
local is_fail = type(msg) == 'string'
and msg:find('Too many highlight and syntax groups$') ~= nil
has_fail = has_fail or is_fail
end
eq('', exec_capture('messages'))
eq(true, has_fail)
end)
it('update=true sets only specified keys', function()
api.nvim_set_hl(0, 'TestGroup', { fg = '#ff0000', bg = '#0000ff', bold = true })
api.nvim_set_hl(0, 'TestGroup', { bg = '#00ff00', update = true })
local hl = api.nvim_get_hl(0, { name = 'TestGroup' })
eq(tonumber('0xff0000'), hl.fg)
eq(tonumber('0x00ff00'), hl.bg)
eq(true, hl.bold)
api.nvim_set_hl(0, 'TestGroup', { bold = false, update = true })
hl = api.nvim_get_hl(0, { name = 'TestGroup' })
eq(nil, hl.bold)
eq(tonumber('0xff0000'), hl.fg)
api.nvim_set_hl(0, 'TestGroup', { italic = true })
hl = api.nvim_get_hl(0, { name = 'TestGroup' })
eq(nil, hl.fg)
eq(nil, hl.bg)
eq(true, hl.italic)
local ns = api.nvim_create_namespace('test')
api.nvim_set_hl(ns, 'TestGroup', { fg = '#ff0000', italic = true })
api.nvim_set_hl(ns, 'TestGroup', { fg = '#00ff00', update = true })
hl = api.nvim_get_hl(ns, { name = 'TestGroup' })
eq(tonumber('0x00ff00'), hl.fg)
eq(true, hl.italic)
api.nvim_set_hl(0, 'NamedColor', { fg = 'red', bg = 'blue' })
api.nvim_set_hl(0, 'LinkedGroup', { link = 'NamedColor' })
api.nvim_set_hl(0, 'LinkedGroup', { bold = true, fg = 'green', update = true })
hl = api.nvim_get_hl(0, { name = 'LinkedGroup' })
eq(nil, hl.link)
eq(true, hl.bold)
eq(
'LinkedGroup xxx cterm=bold gui=bold guifg=Green guibg=Blue',
n.exec_capture('hi LinkedGroup')
)
api.nvim_set_hl(0, 'LinkedGroup', { bg = '#121314', update = true })
eq(
'LinkedGroup xxx cterm=bold gui=bold guifg=Green guibg=#121314',
n.exec_capture('hi LinkedGroup')
)
-- underline style flags: false must not corrupt other styles
local unders = { 'underline', 'undercurl', 'underdouble', 'underdotted', 'underdashed' }
for _, a in ipairs(unders) do
for _, b in ipairs(unders) do
if a ~= b then
api.nvim_set_hl(0, 'TestGroup', { [a] = true, [b] = false })
hl = api.nvim_get_hl(0, { name = 'TestGroup' })
eq(true, hl[a])
eq(nil, hl[b])
end
end
end
api.nvim_set_hl(0, 'TestGroup', { underdouble = true, fg = '#ff0000', bold = true })
api.nvim_set_hl(0, 'TestGroup', { fg = '#00ff00', update = true })
hl = api.nvim_get_hl(0, { name = 'TestGroup' })
eq(true, hl.underdouble)
eq(true, hl.bold)
eq(65280, hl.fg)
api.nvim_set_hl(0, 'TestGroup', { underdashed = true, update = true })
hl = api.nvim_get_hl(0, { name = 'TestGroup' })
eq(true, hl.underdashed)
eq(nil, hl.underdouble)
api.nvim_set_hl(0, 'TestGroup', { underdouble = true, bold = true })
api.nvim_set_hl(0, 'TestGroup', { underdashed = false, update = true })
hl = api.nvim_get_hl(0, { name = 'TestGroup' })
eq(true, hl.underdouble)
api.nvim_set_hl(0, 'TestGroup', { underdouble = false, update = true })
hl = api.nvim_get_hl(0, { name = 'TestGroup' })
eq(nil, hl.underdouble)
eq(true, hl.bold)
end)
it('can set font', function()
local ns = api.nvim_create_namespace('test_font')
api.nvim_set_hl(ns, 'TestFont', { fg = '#ff0000', font = 'Courier New 10' })
local hl = api.nvim_get_hl(ns, { name = 'TestFont' })
eq('Courier New 10', hl.font)
eq(16711680, hl.fg)
api.nvim_set_hl(ns, 'TestFont', { font = 'Monaco' })
hl = api.nvim_get_hl(ns, { name = 'TestFont' })
eq('Monaco', hl.font)
-- Clear font with "NONE"
api.nvim_set_hl(ns, 'TestFont', { font = 'NONE' })
hl = api.nvim_get_hl(ns, { name = 'TestFont' })
eq(nil, hl.font)
-- global namespace
api.nvim_set_hl(0, 'TestFontGlobal', { bg = '#00ff00', font = 'JetBrains Mono' })
hl = api.nvim_get_hl(0, { name = 'TestFontGlobal' })
eq('JetBrains Mono', hl.font)
eq(65280, hl.bg)
end)
end)
describe('API: get highlight', function()
local highlight_color = {
fg = tonumber('0xff0000'),
bg = tonumber('0x0032aa'),
ctermfg = 8,
ctermbg = 15,
}
local highlight1 = {
bg = highlight_color.bg,
fg = highlight_color.fg,
bold = true,
italic = true,
cterm = { bold = true, italic = true },
}
local highlight2 = {
ctermbg = highlight_color.ctermbg,
ctermfg = highlight_color.ctermfg,
underline = true,
reverse = true,
cterm = { underline = true, reverse = true },
}
local highlight3_config = {
bg = highlight_color.bg,
fg = highlight_color.fg,
ctermbg = highlight_color.ctermbg,
ctermfg = highlight_color.ctermfg,
bold = true,
italic = true,
reverse = true,
underdashed = true,
strikethrough = true,
altfont = true,
dim = true,
blink = true,
conceal = true,
overline = true,
cterm = {
italic = true,
reverse = true,
strikethrough = true,
altfont = true,
dim = true,
blink = true,
conceal = true,
overline = true,
nocombine = true,
},
}
local highlight3_result = {
bg = highlight_color.bg,
fg = highlight_color.fg,
ctermbg = highlight_color.ctermbg,
ctermfg = highlight_color.ctermfg,
bold = true,
italic = true,
reverse = true,
underdashed = true,
strikethrough = true,
altfont = true,
dim = true,
blink = true,
conceal = true,
overline = true,
cterm = {
italic = true,
nocombine = true,
reverse = true,
strikethrough = true,
altfont = true,
dim = true,
blink = true,
conceal = true,
overline = true,
},
}
local function get_ns()
-- Test namespace filtering behavior
local ns2 = api.nvim_create_namespace('Another_namespace')
api.nvim_set_hl(ns2, 'Test_hl', { ctermfg = 23 })
api.nvim_set_hl(ns2, 'Test_another_hl', { link = 'Test_hl' })
api.nvim_set_hl(ns2, 'Test_hl_link', { link = 'Test_another_hl' })
api.nvim_set_hl(ns2, 'Test_another_hl_link', { link = 'Test_hl_link' })
local ns = api.nvim_create_namespace('Test_set_hl')
api.nvim_set_hl_ns(ns)
return ns
end
before_each(clear)
it('validation', function()
eq(
"Invalid 'name': expected String, got Integer",
pcall_err(api.nvim_get_hl, 0, { name = 177 })
)
eq('Highlight id out of bounds', pcall_err(api.nvim_get_hl, 0, { name = 'Test set hl' }))
end)
it('nvim_get_hl with create flag', function()
eq({}, api.nvim_get_hl(0, { name = 'Foo', create = false }))
eq(0, fn.hlexists('Foo'))
api.nvim_get_hl(0, { name = 'Bar', create = true })
eq(1, fn.hlexists('Bar'))
api.nvim_get_hl(0, { name = 'FooBar' })
eq(1, fn.hlexists('FooBar'))
end)
it('can get all highlights in current namespace', function()
local ns = get_ns()
api.nvim_set_hl(ns, 'Test_hl', { bg = '#B4BEFE' })
api.nvim_set_hl(ns, 'Test_hl_link', { link = 'Test_hl' })
eq({
Test_hl = {
bg = 11845374,
},
Test_hl_link = {
link = 'Test_hl',
},
}, api.nvim_get_hl(ns, {}))
end)
it('can get gui highlight', function()
local ns = get_ns()
api.nvim_set_hl(ns, 'Test_hl', highlight1)
eq(highlight1, api.nvim_get_hl(ns, { name = 'Test_hl' }))
end)
it('can get cterm highlight', function()
local ns = get_ns()
api.nvim_set_hl(ns, 'Test_hl', highlight2)
eq(highlight2, api.nvim_get_hl(ns, { name = 'Test_hl' }))
end)
it('can get empty cterm attr', function()
local ns = get_ns()
api.nvim_set_hl(ns, 'Test_hl', { cterm = {} })
eq({}, api.nvim_get_hl(ns, { name = 'Test_hl' }))
end)
it('cterm attr defaults to gui attr', function()
local ns = get_ns()
api.nvim_set_hl(ns, 'Test_hl', highlight1)
eq(highlight1, api.nvim_get_hl(ns, { name = 'Test_hl' }))
end)
it('can overwrite attr for cterm', function()
local ns = get_ns()
api.nvim_set_hl(ns, 'Test_hl', highlight3_config)
eq(highlight3_result, api.nvim_get_hl(ns, { name = 'Test_hl' }))
end)
it('only allows one underline attribute #22371', function()
local ns = get_ns()
api.nvim_set_hl(ns, 'Test_hl', {
underdouble = true,
underdotted = true,
cterm = {
underline = true,
undercurl = true,
},
})
eq(
{ underdotted = true, cterm = { undercurl = true } },
api.nvim_get_hl(ns, { name = 'Test_hl' })
)
end)
it('can get a highlight in the global namespace', function()
api.nvim_set_hl(0, 'Test_hl', highlight2)
eq(highlight2, api.nvim_get_hl(0, { name = 'Test_hl' }))
api.nvim_set_hl(0, 'Test_hl', { background = highlight_color.bg })
eq({
bg = 12970,
}, api.nvim_get_hl(0, { name = 'Test_hl' }))
api.nvim_set_hl(0, 'Test_hl2', highlight3_config)
eq(highlight3_result, api.nvim_get_hl(0, { name = 'Test_hl2' }))
-- Colors are stored with the name they are defined, but
-- with canonical casing
api.nvim_set_hl(0, 'Test_hl3', { bg = 'reD', fg = 'bLue' })
eq({
bg = 16711680,
fg = 255,
}, api.nvim_get_hl(0, { name = 'Test_hl3' }))
end)
it('nvim_get_hl by id', function()
local hl_id = api.nvim_get_hl_id_by_name('NewHighlight')
command(
'hi NewHighlight cterm=underline ctermbg=green guifg=red guibg=yellow guisp=blue gui=bold'
)
eq({
fg = 16711680,
bg = 16776960,
sp = 255,
bold = true,
ctermbg = 10,
cterm = { underline = true },
}, api.nvim_get_hl(0, { id = hl_id }))
-- Test 0 argument
eq('Highlight id out of bounds', pcall_err(api.nvim_get_hl, 0, { id = 0 }))
eq(
"Invalid 'id': expected Integer, got String",
pcall_err(api.nvim_get_hl, 0, { id = 'Test_set_hl' })
)
-- Test all highlight properties.
command(
'hi NewHighlight gui=underline,bold,italic,reverse,strikethrough,altfont,dim,blink,conceal,overline,nocombine'
)
eq({
fg = 16711680,
bg = 16776960,
sp = 255,
altfont = true,
blink = true,
bold = true,
conceal = true,
dim = true,
italic = true,
nocombine = true,
overline = true,
reverse = true,
strikethrough = true,
underline = true,
ctermbg = 10,
cterm = { underline = true },
}, api.nvim_get_hl(0, { id = hl_id }))
-- Test undercurl
command('hi NewHighlight gui=undercurl')
eq({
fg = 16711680,
bg = 16776960,
sp = 255,
undercurl = true,
ctermbg = 10,
cterm = { underline = true },
}, api.nvim_get_hl(0, { id = hl_id }))
end)
it('can correctly detect links', function()
command('hi String guifg=#a6e3a1 ctermfg=NONE')
command('hi link @string string')
command('hi link @string.cpp @string')
eq({ fg = 10937249 }, api.nvim_get_hl(0, { name = 'String' }))
eq({ link = 'String' }, api.nvim_get_hl(0, { name = '@string' }))
eq({ fg = 10937249 }, api.nvim_get_hl(0, { name = '@string.cpp', link = false }))
end)
it('can get all attributes for a linked group', function()
command('hi Bar guifg=red')
command('hi Foo guifg=#00ff00 gui=bold,underline')
command('hi! link Foo Bar')
eq(
{ link = 'Bar', fg = tonumber('00ff00', 16), bold = true, underline = true },
api.nvim_get_hl(0, { name = 'Foo', link = true })
)
end)
it('can set link as well as other attributes', function()
command('hi Bar guifg=red')
local hl = { link = 'Bar', fg = tonumber('00ff00', 16), bold = true, cterm = { bold = true } }
api.nvim_set_hl(0, 'Foo', hl)
eq(hl, api.nvim_get_hl(0, { name = 'Foo', link = true }))
end)
it('link_global resolves in global namespace', function()
local ns = api.nvim_create_namespace('hl_test')
api.nvim_set_hl(0, 'GlTarget', { fg = '#ff0000' })
api.nvim_set_hl(ns, 'GlSource', { link_global = fn.hlID('GlTarget') })
eq({ link = 'GlTarget' }, api.nvim_get_hl(ns, { name = 'GlSource' }))
-- when both link and link_global are given, link_global wins
api.nvim_set_hl(0, 'OtherTarget', { fg = '#00ff00' })
api.nvim_set_hl(
ns,
'GlSource',
{ link = fn.hlID('OtherTarget'), link_global = fn.hlID('GlTarget') }
)
eq({ link = 'GlTarget' }, api.nvim_get_hl(ns, { name = 'GlSource' }))
end)
it("doesn't contain unset groups", function()
local id = api.nvim_get_hl_id_by_name '@foobar.hubbabubba'
ok(id > 0)
local data = api.nvim_get_hl(0, {})
eq(nil, data['@foobar.hubbabubba'])
eq(nil, data['@foobar'])
command 'hi @foobar.hubbabubba gui=bold'
data = api.nvim_get_hl(0, {})
eq({ bold = true }, data['@foobar.hubbabubba'])
eq(nil, data['@foobar'])
-- @foobar.hubbabubba was explicitly cleared and thus shows up
-- but @foobar was never touched, and thus doesn't
command 'hi clear @foobar.hubbabubba'
data = api.nvim_get_hl(0, {})
eq({}, data['@foobar.hubbabubba'])
eq(nil, data['@foobar'])
end)
it('should return default flag', function()
api.nvim_set_hl(0, 'Tried', { fg = '#00ff00', default = true })
eq({ fg = tonumber('00ff00', 16), default = true }, api.nvim_get_hl(0, { name = 'Tried' }))
end)
it('should not output empty gui and cterm #23474', function()
api.nvim_set_hl(0, 'Foo', { default = true })
api.nvim_set_hl(0, 'Bar', { default = true, fg = '#ffffff' })
api.nvim_set_hl(0, 'FooBar', { default = true, fg = '#ffffff', cterm = { bold = true } })
api.nvim_set_hl(
0,
'FooBarA',
{ default = true, fg = '#ffffff', cterm = { bold = true, italic = true } }
)
eq('Foo xxx cleared', exec_capture('highlight Foo'))
eq({ default = true }, api.nvim_get_hl(0, { name = 'Foo' }))
eq('Bar xxx guifg=#ffffff', exec_capture('highlight Bar'))
eq('FooBar xxx cterm=bold guifg=#ffffff', exec_capture('highlight FooBar'))
eq('FooBarA xxx cterm=bold,italic guifg=#ffffff', exec_capture('highlight FooBarA'))
end)
it('can override exist highlight group by force #20323', function()
local white = tonumber('ffffff', 16)
local green = tonumber('00ff00', 16)
api.nvim_set_hl(0, 'Foo', { fg = white })
api.nvim_set_hl(0, 'Foo', { fg = green, force = true })
eq({ fg = green }, api.nvim_get_hl(0, { name = 'Foo' }))
api.nvim_set_hl(0, 'Bar', { link = 'Comment', default = true })
api.nvim_set_hl(0, 'Bar', { link = 'Foo', default = true, force = true })
eq({ link = 'Foo', default = true }, api.nvim_get_hl(0, { name = 'Bar' }))
end)
end)
describe('API: set/get highlight namespace', function()
before_each(clear)
it('set/get highlight namespace', function()
eq(0, api.nvim_get_hl_ns({}))
local ns = api.nvim_create_namespace('')
api.nvim_set_hl_ns(ns)
eq(ns, api.nvim_get_hl_ns({}))
end)
it('set/get window highlight namespace', function()
eq(-1, api.nvim_get_hl_ns({ winid = 0 }))
local ns = api.nvim_create_namespace('')
api.nvim_win_set_hl_ns(0, ns)
eq(ns, api.nvim_get_hl_ns({ winid = 0 }))
end)
it('setting namespace takes priority over &winhighlight', function()
local win = api.nvim_get_current_win()
eq(-1, api.nvim_get_hl_ns({ winid = win }))
command('set winhighlight=Visual:Search')
local winhl_ns = api.nvim_get_hl_ns({ winid = win })
neq(0, winhl_ns)
eq('Search', api.nvim_get_hl(winhl_ns, { name = 'Visual' }).link)
n.insert('foobar')
local ns = api.nvim_create_namespace('')
neq(winhl_ns, ns)
api.nvim_win_set_hl_ns(win, ns)
eq(ns, api.nvim_get_hl_ns({ winid = win }))
command('enew') -- Switching buffer keeps namespace. #30904
eq(ns, api.nvim_get_hl_ns({ winid = win }))
command('set winhighlight=')
eq(ns, api.nvim_get_hl_ns({ winid = win }))
-- Setting 'winhighlight' changes its namespace even when not using it.
command('set winhighlight=Visual:IncSearch')
eq('IncSearch', api.nvim_get_hl(winhl_ns, { name = 'Visual' }).link)
eq(ns, api.nvim_get_hl_ns({ winid = win }))
-- Setting 'winhighlight' works with global namespace. #37865
api.nvim_win_set_hl_ns(win, 0)
eq(0, api.nvim_get_hl_ns({ winid = win }))
command('set winhighlight=Visual:IncSearch')
eq('IncSearch', api.nvim_get_hl(winhl_ns, { name = 'Visual' }).link)
eq(winhl_ns, api.nvim_get_hl_ns({ winid = win }))
command('set winhighlight=')
eq(0, api.nvim_get_hl_ns({ winid = win }))
-- 'winhighlight' keeps the same namespace.
api.nvim_win_set_hl_ns(win, winhl_ns)
eq(winhl_ns, api.nvim_get_hl_ns({ winid = win }))
command('set winhighlight=Visual:Substitute')
eq('Substitute', api.nvim_get_hl(winhl_ns, { name = 'Visual' }).link)
eq(winhl_ns, api.nvim_get_hl_ns({ winid = win }))
end)
end)