feat(ui): allow to set the highlight namespace per window

- reimplement 'winhl' in terms of highlight namespaces
- check for EOF in screen tests (to indicate a likely crash)
This commit is contained in:
bfredl
2022-07-25 10:16:33 +02:00
parent f7cfca49d6
commit d879331b0d
30 changed files with 596 additions and 244 deletions

View File

@@ -32,7 +32,9 @@ static Map(int, int) blend_attr_entries = MAP_INIT;
static Map(int, int) blendthrough_attr_entries = MAP_INIT;
/// highlight entries private to a namespace
static Map(ColorKey, ColorItem) ns_hl;
static Map(ColorKey, ColorItem) ns_hls;
typedef int NSHlAttr[HLF_COUNT + 1];
static PMap(handle_T) ns_hl_attr;
void highlight_init(void)
{
@@ -147,42 +149,46 @@ int hl_get_syn_attr(int ns_id, int idx, HlAttrs at_en)
void ns_hl_def(NS ns_id, int hl_id, HlAttrs attrs, int link_id, Dict(highlight) *dict)
{
if ((attrs.rgb_ae_attr & HL_DEFAULT)
&& map_has(ColorKey, ColorItem)(&ns_hl, ColorKey(ns_id, hl_id))) {
return;
}
if (ns_id == 0) {
assert(dict);
// set in global (':highlight') namespace
set_hl_group(hl_id, attrs, dict, link_id);
return;
}
if ((attrs.rgb_ae_attr & HL_DEFAULT)
&& map_has(ColorKey, ColorItem)(&ns_hls, ColorKey(ns_id, hl_id))) {
return;
}
DecorProvider *p = get_decor_provider(ns_id, true);
int attr_id = link_id > 0 ? -1 : hl_get_syn_attr(ns_id, hl_id, attrs);
ColorItem it = { .attr_id = attr_id,
.link_id = link_id,
.version = p->hl_valid,
.is_default = (attrs.rgb_ae_attr & HL_DEFAULT) };
map_put(ColorKey, ColorItem)(&ns_hl, ColorKey(ns_id, hl_id), it);
.is_default = (attrs.rgb_ae_attr & HL_DEFAULT),
.link_global = (attrs.rgb_ae_attr & HL_GLOBAL) };
map_put(ColorKey, ColorItem)(&ns_hls, ColorKey(ns_id, hl_id), it);
p->hl_cached = false;
}
int ns_get_hl(NS ns_id, int hl_id, bool link, bool nodefault)
int ns_get_hl(NS *ns_hl, int hl_id, bool link, bool nodefault)
{
static int recursive = 0;
if (ns_id < 0) {
if (*ns_hl < 0) {
if (ns_hl_active <= 0) {
return -1;
}
ns_id = ns_hl_active;
*ns_hl = ns_hl_active;
}
DecorProvider *p = get_decor_provider(ns_id, true);
ColorItem it = map_get(ColorKey, ColorItem)(&ns_hl, ColorKey(ns_id, hl_id));
// TODO(bfredl): map_ref true even this?
bool valid_cache = it.version >= p->hl_valid;
int ns_id = *ns_hl;
if (!valid_cache && p->hl_def != LUA_NOREF && !recursive) {
DecorProvider *p = get_decor_provider(ns_id, true);
ColorItem it = map_get(ColorKey, ColorItem)(&ns_hls, ColorKey(ns_id, hl_id));
// TODO(bfredl): map_ref true even this?
bool valid_item = it.version >= p->hl_valid;
if (!valid_item && p->hl_def != LUA_NOREF && !recursive) {
MAXSIZE_TEMP_ARRAY(args, 3);
ADD_C(args, INTEGER_OBJ((Integer)ns_id));
ADD_C(args, STRING_OBJ(cstr_to_string((char *)syn_id2name(hl_id))));
@@ -215,44 +221,76 @@ int ns_get_hl(NS ns_id, int hl_id, bool link, bool nodefault)
it.attr_id = fallback ? -1 : hl_get_syn_attr((int)ns_id, hl_id, attrs);
it.version = p->hl_valid - tmp;
it.is_default = attrs.rgb_ae_attr & HL_DEFAULT;
map_put(ColorKey, ColorItem)(&ns_hl, ColorKey(ns_id, hl_id), it);
it.link_global = attrs.rgb_ae_attr & HL_GLOBAL;
map_put(ColorKey, ColorItem)(&ns_hls, ColorKey(ns_id, hl_id), it);
valid_item = true;
}
if (it.is_default && nodefault) {
if ((it.is_default && nodefault) || !valid_item) {
return -1;
}
if (link) {
return it.attr_id >= 0 ? 0 : it.link_id;
if (it.attr_id >= 0) {
return 0;
} else {
if (it.link_global) {
*ns_hl = 0;
}
return it.link_id;
}
} else {
return it.attr_id;
}
}
bool hl_check_ns(void)
{
int ns = 0;
if (ns_hl_fast > 0) {
ns = ns_hl_fast;
} else if (ns_hl_win >= 0) {
ns = ns_hl_win;
} else {
ns = ns_hl_global;
}
if (ns_hl_active == ns) {
return false;
}
ns_hl_active = ns;
hl_attr_active = highlight_attr;
if (ns > 0) {
update_ns_hl(ns);
NSHlAttr *hl_def = (NSHlAttr *)pmap_get(handle_T)(&ns_hl_attr, ns);
if (hl_def) {
hl_attr_active = *hl_def;
}
}
need_highlight_changed = true;
return true;
}
/// prepare for drawing window `wp` or global elements if NULL
///
/// Note: pum should be drawn in the context of the current window!
bool win_check_ns_hl(win_T *wp)
{
if (ns_hl_changed) {
highlight_changed();
if (wp) {
update_window_hl(wp, true);
}
ns_hl_changed = false;
return true;
}
return false;
ns_hl_win = wp ? wp->w_ns_hl : -1;
return hl_check_ns();
}
/// Get attribute code for a builtin highlight group.
///
/// The final syntax group could be modified by hi-link or 'winhighlight'.
int hl_get_ui_attr(int idx, int final_id, bool optional)
int hl_get_ui_attr(int ns_id, int idx, int final_id, bool optional)
{
HlAttrs attrs = HLATTRS_INIT;
bool available = false;
if (final_id > 0) {
int syn_attr = syn_id2attr(final_id);
if (syn_attr != 0) {
int syn_attr = syn_ns_id2attr(ns_id, final_id, optional);
if (syn_attr > 0) {
attrs = syn_attr2entry(syn_attr);
available = true;
}
@@ -265,7 +303,7 @@ int hl_get_ui_attr(int idx, int final_id, bool optional)
if (pum_drawn()) {
must_redraw_pum = true;
}
} else if (idx == HLF_MSG) {
} else if (idx == HLF_MSG && ns_id == -1) {
msg_grid.blending = attrs.hl_blend > -1;
}
@@ -278,6 +316,21 @@ int hl_get_ui_attr(int idx, int final_id, bool optional)
void update_window_hl(win_T *wp, bool invalid)
{
int ns_id = wp->w_ns_hl;
update_ns_hl(ns_id);
if (ns_id != wp->w_ns_hl_active) {
wp->w_ns_hl_active = ns_id;
wp->w_ns_hl_attr = *(NSHlAttr *)pmap_get(handle_T)(&ns_hl_attr, ns_id);
if (!wp->w_ns_hl_attr) {
// No specific highlights, use the defaults.
wp->w_ns_hl_attr = highlight_attr;
}
}
int *hl_def = wp->w_ns_hl_attr;
if (!wp->w_hl_needs_update && !invalid) {
return;
}
@@ -285,34 +338,17 @@ void update_window_hl(win_T *wp, bool invalid)
// If a floating window is blending it always have a named
// wp->w_hl_attr_normal group. HL_ATTR(HLF_NFLOAT) is always named.
bool has_blend = wp->w_floating && wp->w_p_winbl != 0;
// determine window specific background set in 'winhighlight'
bool float_win = wp->w_floating && !wp->w_float_config.external;
if (wp != curwin && wp->w_hl_ids[HLF_INACTIVE] != 0) {
wp->w_hl_attr_normal = hl_get_ui_attr(HLF_INACTIVE,
wp->w_hl_ids[HLF_INACTIVE],
!has_blend);
} else if (float_win && wp->w_hl_ids[HLF_NFLOAT] != 0) {
wp->w_hl_attr_normal = hl_get_ui_attr(HLF_NFLOAT,
wp->w_hl_ids[HLF_NFLOAT], !has_blend);
} else if (wp->w_hl_id_normal != 0) {
wp->w_hl_attr_normal = hl_get_ui_attr(-1, wp->w_hl_id_normal, !has_blend);
if (float_win && hl_def[HLF_NFLOAT] != 0) {
wp->w_hl_attr_normal = hl_def[HLF_NFLOAT];
} else if (hl_def[HLF_COUNT] > 0) {
wp->w_hl_attr_normal = hl_def[HLF_COUNT];
} else {
wp->w_hl_attr_normal = float_win ? HL_ATTR(HLF_NFLOAT) : 0;
}
// NOOOO! You cannot just pretend that "Normal" is just like any other
// syntax group! It needs at least 10 layers of special casing! Noooooo!
//
// haha, theme engine go brrr
int normality = syn_check_group(S_LEN("Normal"));
int ns_attr = ns_get_hl(-1, normality, false, false);
if (ns_attr > 0) {
// TODO(bfredl): hantera NormalNC and so on
wp->w_hl_attr_normal = ns_attr;
}
// if blend= attribute is not set, 'winblend' value overrides it.
if (wp->w_floating && wp->w_p_winbl > 0) {
HlEntry entry = kv_A(attr_entries, wp->w_hl_attr_normal);
@@ -322,28 +358,13 @@ void update_window_hl(win_T *wp, bool invalid)
}
}
if (wp != curwin && wp->w_hl_ids[HLF_INACTIVE] == 0) {
wp->w_hl_attr_normal = hl_combine_attr(HL_ATTR(HLF_INACTIVE),
wp->w_hl_attr_normal);
}
for (int hlf = 0; hlf < HLF_COUNT; hlf++) {
int attr;
if (wp->w_hl_ids[hlf] != 0) {
attr = hl_get_ui_attr(hlf, wp->w_hl_ids[hlf], false);
} else {
attr = HL_ATTR(hlf);
}
wp->w_hl_attrs[hlf] = attr;
}
wp->w_float_config.shadow = false;
if (wp->w_floating && wp->w_float_config.border) {
for (int i = 0; i < 8; i++) {
int attr = wp->w_hl_attrs[HLF_BORDER];
int attr = hl_def[HLF_BORDER];
if (wp->w_float_config.border_hl_ids[i]) {
attr = hl_get_ui_attr(HLF_BORDER, wp->w_float_config.border_hl_ids[i],
false);
attr = hl_get_ui_attr(ns_id, HLF_BORDER,
wp->w_float_config.border_hl_ids[i], false);
HlAttrs a = syn_attr2entry(attr);
if (a.hl_blend) {
wp->w_float_config.shadow = true;
@@ -355,6 +376,65 @@ void update_window_hl(win_T *wp, bool invalid)
// shadow might cause blending
check_blending(wp);
// TODO(bfredl): this a bit ad-hoc. move it from highlight ns logic to 'winhl'
// implementation?
if (hl_def[HLF_INACTIVE] == 0) {
wp->w_hl_attr_normalnc = hl_combine_attr(HL_ATTR(HLF_INACTIVE),
wp->w_hl_attr_normal);
} else {
wp->w_hl_attr_normalnc = hl_def[HLF_INACTIVE];
}
}
void update_ns_hl(int ns_id)
{
if (ns_id <= 0) {
return;
}
DecorProvider *p = get_decor_provider(ns_id, true);
if (p->hl_cached) {
return;
}
NSHlAttr **alloc = (NSHlAttr **)pmap_ref(handle_T)(&ns_hl_attr, ns_id, true);
if (*alloc == NULL) {
*alloc = xmalloc(sizeof(**alloc));
}
int *hl_attrs = **alloc;
for (int hlf = 0; hlf < (int)HLF_COUNT; hlf++) {
int id = syn_check_group(hlf_names[hlf], STRLEN(hlf_names[hlf]));
bool optional = (hlf == HLF_INACTIVE || hlf == HLF_NFLOAT);
hl_attrs[hlf] = hl_get_ui_attr(ns_id, hlf, id, optional);
}
// NOOOO! You cannot just pretend that "Normal" is just like any other
// syntax group! It needs at least 10 layers of special casing! Noooooo!
//
// haha, tema engine go brrr
int normality = syn_check_group(S_LEN("Normal"));
hl_attrs[HLF_COUNT] = hl_get_ui_attr(ns_id, -1, normality, true);
// hl_get_ui_attr might have invalidated the decor provider
p = get_decor_provider(ns_id, true);
p->hl_cached = true;
}
int win_bg_attr(win_T *wp)
{
if (ns_hl_fast < 0) {
int local = (wp == curwin) ? wp->w_hl_attr_normal : wp->w_hl_attr_normalnc;
if (local) {
return local;
}
}
if (wp == curwin || hl_attr_active[HLF_INACTIVE] == 0) {
return hl_attr_active[HLF_COUNT];
} else {
return hl_attr_active[HLF_INACTIVE];
}
}
/// Gets HL_UNDERLINE highlight.
@@ -403,7 +483,7 @@ void clear_hl_tables(bool reinit)
map_destroy(int, int)(&combine_attr_entries);
map_destroy(int, int)(&blend_attr_entries);
map_destroy(int, int)(&blendthrough_attr_entries);
map_destroy(ColorKey, ColorItem)(&ns_hl);
map_destroy(ColorKey, ColorItem)(&ns_hls);
}
}
@@ -852,7 +932,6 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e
CHECK_FLAG(dict, mask, strikethrough, , HL_STRIKETHROUGH);
CHECK_FLAG(dict, mask, nocombine, , HL_NOCOMBINE);
CHECK_FLAG(dict, mask, default, _, HL_DEFAULT);
CHECK_FLAG(dict, mask, global, , HL_GLOBAL);
if (HAS_KEY(dict->fg)) {
fg = object_to_color(dict->fg, "fg", true, err);
@@ -895,14 +974,21 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e
return hlattrs;
}
if (HAS_KEY(dict->link)) {
if (HAS_KEY(dict->link) || HAS_KEY(dict->global_link)) {
if (link_id) {
*link_id = object_to_hl_id(dict->link, "link", err);
if (HAS_KEY(dict->global_link)) {
*link_id = object_to_hl_id(dict->global_link, "link", err);
mask |= HL_GLOBAL;
} else {
*link_id = object_to_hl_id(dict->link, "link", err);
}
if (ERROR_SET(err)) {
return hlattrs;
}
} else {
api_set_error(err, kErrorTypeValidation, "Invalid Key: 'link'");
api_set_error(err, kErrorTypeValidation, "Invalid Key: '%s'",
HAS_KEY(dict->global_link) ? "global_link" : "link");
}
}