feat(api): nvim_set_hl can set "font" #37668

Problem: Cannot set highlight group fonts via API, only via :highlight
command.

Solution: Add font parameter in nvim_set_hl().
This commit is contained in:
glepnir
2026-04-12 23:19:40 +08:00
committed by GitHub
parent 37eb1b9979
commit 1033739b60
10 changed files with 106 additions and 5 deletions

View File

@@ -1575,6 +1575,8 @@ nvim_set_hl({ns_id}, {name}, {val}) *nvim_set_hl()*
• fg: color name or "#RRGGBB", see note.
• fg_indexed: boolean (default false) If true, fg is a
terminal palette index (0-255).
• font: GUI font name (string). Sets |highlight-font|. Use
"NONE" to clear.
• force: if true force update the highlight group when it
exists.
• italic: boolean

View File

@@ -106,7 +106,9 @@ The following new features were added.
API
todo
|vim.lsp.buf.declaration()|, |vim.lsp.buf.definition()|, |vim.lsp.buf.definition()|,
and |vim.lsp.buf.implementation()| now follows 'switchbuf'.
• |nvim_set_hl()| supports "font" key.
BUILD

View File

@@ -2240,6 +2240,7 @@ function vim.api.nvim_set_decoration_provider(ns_id, opts) end
--- - dim: boolean
--- - fg: color name or "#RRGGBB", see note.
--- - fg_indexed: boolean (default false) If true, fg is a terminal palette index (0-255).
--- - font: GUI font name (string). Sets `highlight-font`. Use "NONE" to clear.
--- - force: if true force update the highlight group when it exists.
--- - italic: boolean
--- - link: Name of highlight group to link to. `:hi-link`

View File

@@ -337,6 +337,7 @@ error('Cannot require a meta file')
--- @field force? boolean
--- @field update? boolean
--- @field url? string
--- @field font? string
--- @class vim.api.keyset.highlight_cterm
--- @field bold? boolean

View File

@@ -208,6 +208,7 @@ typedef struct {
Boolean force;
Boolean update;
String url;
String font;
} Dict(highlight);
typedef struct {

View File

@@ -156,6 +156,7 @@ DictAs(get_hl_info) nvim_get_hl(Integer ns_id, Dict(get_highlight) *opts, Arena
/// - dim: boolean
/// - fg: color name or "#RRGGBB", see note.
/// - fg_indexed: boolean (default false) If true, fg is a terminal palette index (0-255).
/// - font: GUI font name (string). Sets |highlight-font|. Use "NONE" to clear.
/// - force: if true force update the highlight group when it exists.
/// - italic: boolean
/// - link: Name of highlight group to link to. |:hi-link|

View File

@@ -41,6 +41,8 @@ static Map(uint64_t, int) combine_attr_entries = MAP_INIT;
static Map(uint64_t, int) blend_attr_entries = MAP_INIT;
static Map(uint64_t, int) blendthrough_attr_entries = MAP_INIT;
static Set(cstr_t) urls = SET_INIT;
/// Interned font names, referenced by index in HlAttrs.font.
static Set(cstr_t) fonts = SET_INIT;
#define attr_entry(i) attr_entries.keys[i]
@@ -536,6 +538,37 @@ const char *hl_get_url(uint32_t index)
return urls.keys[index];
}
// Intern a font name and return its stable index for use in HlAttrs.font.
///
/// @param font_name The font name to add
/// @return Font index, or -1 if font_name is NULL or empty
int32_t hl_add_font_idx(const char *font_name)
{
if (font_name == NULL || *font_name == '\0') {
return -1;
}
MHPutStatus status;
uint32_t k = set_put_idx(cstr_t, &fonts, font_name, &status);
if (status != kMHExisting) {
fonts.keys[k] = xstrdup(font_name);
}
return (int32_t)k;
}
/// Get a font name by its index.
///
/// @param index Font index
/// @return Font name, or NULL if index is invalid
const char *hl_get_font(int32_t index)
{
if (index < 0 || !fonts.keys) {
return NULL;
}
return fonts.keys[index];
}
/// Get attribute code for forwarded :terminal highlights.
int hl_get_term_attr(HlAttrs *aep)
{
@@ -551,6 +584,11 @@ void clear_hl_tables(bool reinit)
xfree((void *)url);
});
const char *font = NULL;
set_foreach(&fonts, font, {
xfree((void *)font);
});
if (reinit) {
set_clear(HlEntry, &attr_entries);
highlight_init();
@@ -558,6 +596,7 @@ void clear_hl_tables(bool reinit)
map_clear(uint64_t, &blend_attr_entries);
map_clear(uint64_t, &blendthrough_attr_entries);
set_clear(cstr_t, &urls);
set_clear(cstr_t, &fonts);
memset(highlight_attr_last, -1, sizeof(highlight_attr_last));
highlight_attr_set_all();
highlight_changed();
@@ -569,6 +608,7 @@ void clear_hl_tables(bool reinit)
map_destroy(uint64_t, &blendthrough_attr_entries);
map_destroy(ColorKey, &ns_hls);
set_destroy(cstr_t, &urls);
set_destroy(cstr_t, &fonts);
}
}
@@ -1021,6 +1061,10 @@ void hlattrs2dict(Dict *hl, Dict *hl_attrs, HlAttrs ae, bool use_rgb, bool short
if (ae.hl_blend > -1 && (use_rgb || !short_keys)) {
PUT_C(*hl, "blend", INTEGER_OBJ(ae.hl_blend));
}
const char *font = hl_get_font(ae.font);
if (font != NULL) {
PUT_C(*hl, "font", STRING_OBJ(cstr_as_string(font)));
}
}
HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, HlAttrs *base, Error *err)
@@ -1195,6 +1239,13 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, HlAttrs
hlattrs.cterm_ae_attr = mask;
}
if (HAS_KEY_X(dict, font)) {
String str = dict->font;
if (str.size > 0 && STRICMP(str.data, "NONE") != 0) {
hlattrs.font = hl_add_font_idx(str.data);
}
}
return hlattrs;
#undef HAS_KEY_X
}

View File

@@ -45,6 +45,7 @@ typedef struct {
int16_t cterm_fg_color, cterm_bg_color;
int32_t hl_blend;
int32_t url;
int32_t font;
} HlAttrs;
#define HLATTRS_INIT (HlAttrs) { \
@@ -57,6 +58,7 @@ typedef struct {
.cterm_bg_color = 0, \
.hl_blend = -1, \
.url = -1, \
.font = -1, \
}
/// Values for index in highlight_attr[].

View File

@@ -112,6 +112,7 @@ typedef struct {
int sg_rgb_sp_idx; ///< RGB special color index
int sg_blend; ///< blend level (0-100 inclusive), -1 if unset
char *sg_font; ///< font name, NULL if not set
int sg_parent; ///< parent of @nested.group
} HlGroup;
@@ -1339,8 +1340,16 @@ void do_highlight(const char *line, const bool forceit, const bool init)
hl_table[idx].sg_gui = attr;
}
}
} else if (strcmp(key, "FONT") == 0) {
// in non-GUI fonts are simply ignored
} else if (strcmp(key, "FONT") == 0 && (!init || !(hl_table[idx].sg_set & SG_GUI))) {
if (!init) {
hl_table[idx].sg_set |= SG_GUI;
}
if (hl_table[idx].sg_font != NULL) {
XFREE_CLEAR(hl_table[idx].sg_font);
}
if (strcmp(arg, "NONE") != 0) {
hl_table[idx].sg_font = xstrdup(arg);
}
} else if (strcmp(key, "CTERMFG") == 0 || strcmp(key, "CTERMBG") == 0) {
if (!init || !(hl_table[idx].sg_set & SG_CTERM)) {
if (!init) {
@@ -1574,6 +1583,9 @@ static void highlight_clear(int idx)
hl_table[idx].sg_rgb_bg_idx = kColorIdxNone;
hl_table[idx].sg_rgb_sp_idx = kColorIdxNone;
hl_table[idx].sg_blend = -1;
if (hl_table[idx].sg_font != NULL) {
XFREE_CLEAR(hl_table[idx].sg_font);
}
// Restore default link and context if they exist. Otherwise clears.
hl_table[idx].sg_link = hl_table[idx].sg_deflink;
// Since we set the default link, set the location to where the default
@@ -1619,8 +1631,9 @@ static void highlight_list_one(const int id)
didh = highlight_list_arg(id, didh, LIST_STRING, 0,
coloridx_to_name(sgp->sg_rgb_sp_idx, sgp->sg_rgb_sp, hexbuf), "guisp");
didh = highlight_list_arg(id, didh, LIST_INT,
sgp->sg_blend + 1, NULL, "blend");
didh = highlight_list_arg(id, didh, LIST_INT, sgp->sg_blend + 1, NULL, "blend");
didh = highlight_list_arg(id, didh, LIST_STRING, 0, sgp->sg_font, "font");
if (sgp->sg_link && !got_int) {
syn_list_header(didh, 0, id, true);
@@ -1948,6 +1961,10 @@ static void set_hl_attr(int idx)
at_en.rgb_bg_color = sgp->sg_rgb_bg_idx != kColorIdxNone ? sgp->sg_rgb_bg : -1;
at_en.rgb_sp_color = sgp->sg_rgb_sp_idx != kColorIdxNone ? sgp->sg_rgb_sp : -1;
at_en.hl_blend = sgp->sg_blend;
// Convert font name to index
if (sgp->sg_font != NULL) {
at_en.font = hl_add_font_idx(sgp->sg_font);
}
sgp->sg_attr = hl_get_syn_attr(0, idx + 1, at_en);

View File

@@ -334,6 +334,29 @@ describe('API: set highlight', function()
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()