commit 444c195edb33571cc0b653d4e2ef0fd3519cf0fd
parent 886efcb8535e77c685f73d62fa9138f59121c241
Author: glepnir <glephunter@gmail.com>
Date: Thu, 12 Feb 2026 12:29:57 +0800
fix(highlight): blend underline check and use struct key for attr cache
Problem: hl_blend_attrs() through-blend path only checked HL_UNDERLINE | HL_UNDERCURL
for sp_color blending, missing underdotted, combine/blend attr cache used (a << 16) + b
as map key which could overflow if attr IDs ever exceed 16 bits.
Solution: Use HL_UNDERLINE_MASK in the through-blend path to match all underline
styles. Use uint64_t with (a << 32) | b as map key via HlAttrKey macro so each
attr ID gets its full 32-bit range. Use HLATTRS_INIT in hl_get_underline() to
avoid duplicating default field values.
Diffstat:
4 files changed, 32 insertions(+), 34 deletions(-)
diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c
@@ -37,9 +37,9 @@
static bool hlstate_active = false;
static Set(HlEntry) attr_entries = SET_INIT;
-static Map(int, int) combine_attr_entries = MAP_INIT;
-static Map(int, int) blend_attr_entries = MAP_INIT;
-static Map(int, int) blendthrough_attr_entries = MAP_INIT;
+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;
#define attr_entry(i) attr_entries.keys[i]
@@ -468,18 +468,11 @@ int win_bg_attr(win_T *wp)
/// Gets HL_UNDERLINE highlight.
int hl_get_underline(void)
{
+ HlAttrs attrs = HLATTRS_INIT;
+ attrs.cterm_ae_attr = (int16_t)HL_UNDERLINE;
+ attrs.rgb_ae_attr = (int16_t)HL_UNDERLINE;
return get_attr_entry((HlEntry){
- .attr = (HlAttrs){
- .cterm_ae_attr = (int16_t)HL_UNDERLINE,
- .cterm_fg_color = 0,
- .cterm_bg_color = 0,
- .rgb_ae_attr = (int16_t)HL_UNDERLINE,
- .rgb_fg_color = -1,
- .rgb_bg_color = -1,
- .rgb_sp_color = -1,
- .hl_blend = -1,
- .url = -1,
- },
+ .attr = attrs,
.kind = kHlUI,
.id1 = 0,
.id2 = 0,
@@ -541,9 +534,9 @@ void clear_hl_tables(bool reinit)
if (reinit) {
set_clear(HlEntry, &attr_entries);
highlight_init();
- map_clear(int, &combine_attr_entries);
- map_clear(int, &blend_attr_entries);
- map_clear(int, &blendthrough_attr_entries);
+ map_clear(uint64_t, &combine_attr_entries);
+ map_clear(uint64_t, &blend_attr_entries);
+ map_clear(uint64_t, &blendthrough_attr_entries);
set_clear(cstr_t, &urls);
memset(highlight_attr_last, -1, sizeof(highlight_attr_last));
highlight_attr_set_all();
@@ -551,9 +544,9 @@ void clear_hl_tables(bool reinit)
screen_invalidate_highlights();
} else {
set_destroy(HlEntry, &attr_entries);
- map_destroy(int, &combine_attr_entries);
- map_destroy(int, &blend_attr_entries);
- map_destroy(int, &blendthrough_attr_entries);
+ map_destroy(uint64_t, &combine_attr_entries);
+ map_destroy(uint64_t, &blend_attr_entries);
+ map_destroy(uint64_t, &blendthrough_attr_entries);
map_destroy(ColorKey, &ns_hls);
set_destroy(cstr_t, &urls);
}
@@ -561,8 +554,8 @@ void clear_hl_tables(bool reinit)
void hl_invalidate_blends(void)
{
- map_clear(int, &blend_attr_entries);
- map_clear(int, &blendthrough_attr_entries);
+ map_clear(uint64_t, &blend_attr_entries);
+ map_clear(uint64_t, &blendthrough_attr_entries);
highlight_changed();
update_window_hl(curwin, true);
}
@@ -591,9 +584,8 @@ int hl_combine_attr(int char_attr, int prim_attr)
return char_attr;
}
- // TODO(bfredl): could use a struct for clearer intent.
- int combine_tag = (char_attr << 16) + prim_attr;
- int id = map_get(int, int)(&combine_attr_entries, combine_tag);
+ uint64_t combine_tag = HlAttrKey(char_attr, prim_attr);
+ int id = map_get(uint64_t, int)(&combine_attr_entries, combine_tag);
if (id > 0) {
return id;
}
@@ -654,7 +646,7 @@ int hl_combine_attr(int char_attr, int prim_attr)
id = get_attr_entry((HlEntry){ .attr = new_en, .kind = kHlCombine,
.id1 = char_attr, .id2 = prim_attr });
if (id > 0) {
- map_put(int, int)(&combine_attr_entries, combine_tag, id);
+ map_put(uint64_t, int)(&combine_attr_entries, combine_tag, id);
}
return id;
@@ -709,11 +701,11 @@ int hl_blend_attrs(int back_attr, int front_attr, bool *through)
return front_attr;
}
- int combine_tag = (back_attr << 16) + front_attr;
- Map(int, int) *map = (*through
- ? &blendthrough_attr_entries
- : &blend_attr_entries);
- int id = map_get(int, int)(map, combine_tag);
+ uint64_t combine_tag = HlAttrKey(back_attr, front_attr);
+ Map(uint64_t, int) *map = (*through
+ ? &blendthrough_attr_entries
+ : &blend_attr_entries);
+ int id = map_get(uint64_t, int)(map, combine_tag);
if (id > 0) {
return id;
}
@@ -725,8 +717,8 @@ int hl_blend_attrs(int back_attr, int front_attr, bool *through)
if (*through) {
cattrs = battrs;
cattrs.rgb_fg_color = rgb_blend(ratio, battrs.rgb_fg_color, fattrs.rgb_bg_color);
- // Only apply special colors when the foreground attribute has an underline or undercurl.
- if (fattrs_raw.rgb_ae_attr & (HL_UNDERLINE | HL_UNDERCURL)) {
+ // Only apply special colors when the foreground attribute has an underline style.
+ if (fattrs_raw.rgb_ae_attr & HL_UNDERLINE_MASK) {
cattrs.rgb_sp_color = rgb_blend(ratio, battrs.rgb_sp_color, fattrs.rgb_bg_color);
} else {
cattrs.rgb_sp_color = -1;
@@ -764,7 +756,7 @@ int hl_blend_attrs(int back_attr, int front_attr, bool *through)
id = get_attr_entry((HlEntry){ .attr = cattrs, .kind = kind,
.id1 = back_attr, .id2 = front_attr });
if (id > 0) {
- map_put(int, int)(map, combine_tag, id);
+ map_put(uint64_t, int)(map, combine_tag, id);
}
return id;
}
diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h
@@ -163,6 +163,8 @@ typedef struct {
} ColorKey;
#define ColorKey(n, s) (ColorKey) { .ns_id = (int)(n), .syn_id = (s) }
+#define HlAttrKey(a, b) ((uint64_t)(uint32_t)(a) << 32 | (uint32_t)(b))
+
typedef struct {
int attr_id;
int link_id;
diff --git a/src/nvim/map.c b/src/nvim/map.c
@@ -158,6 +158,9 @@ void mh_clear(MapHash *h)
#define VAL_NAME(x) quasiquote(x, uint64_t)
#include "nvim/map_value_impl.c.h"
#undef VAL_NAME
+#define VAL_NAME(x) quasiquote(x, int)
+#include "nvim/map_value_impl.c.h"
+#undef VAL_NAME
#undef KEY_NAME
#define KEY_NAME(x) x##int64_t
diff --git a/src/nvim/map_defs.h b/src/nvim/map_defs.h
@@ -161,6 +161,7 @@ MAP_DECLS(uint32_t, ptr_t)
MAP_DECLS(uint64_t, ptr_t)
MAP_DECLS(uint64_t, ssize_t)
MAP_DECLS(uint64_t, uint64_t)
+MAP_DECLS(uint64_t, int)
MAP_DECLS(int64_t, int64_t)
MAP_DECLS(int64_t, ptr_t)
MAP_DECLS(uint32_t, uint32_t)