decorations: allow nvim_set_hl to break existing links

also add `default` flag to NOT break existing links/defs
This commit is contained in:
Björn Linse
2020-11-08 11:36:47 +01:00
parent 480b04122e
commit a4986ab47f
8 changed files with 114 additions and 22 deletions

View File

@@ -212,6 +212,9 @@ Dictionary nvim__get_hl_defs(Integer ns_id, Error *err)
/// @param ns_id number of namespace for this highlight
/// @param name highlight group name, like ErrorMsg
/// @param val highlight definiton map, like |nvim_get_hl_by_name|.
/// in addition the following keys are also recognized:
/// `default`: don't override existing definition,
/// like `hi default`
/// @param[out] err Error details, if any
///
/// TODO: ns_id = 0, should modify :highlight namespace
@@ -249,7 +252,7 @@ void nvim_set_hl_ns(Integer ns_id, Error *err)
// event path for redraws caused by "fast" events. This could tie in with
// better throttling of async events causing redraws, such as non-batched
// nvim_buf_set_extmark calls from async contexts.
if (!updating_screen && !ns_hl_changed) {
if (!provider_active && !ns_hl_changed) {
multiqueue_put(main_loop.events, on_redraw_event, 0);
}
ns_hl_changed = true;

View File

@@ -58,6 +58,7 @@ typedef struct {
EXTERN kvec_t(DecorProvider) decor_providers INIT(= KV_INITIAL_VALUE);
EXTERN DecorState decor_state INIT(= { 0 });
EXTERN bool provider_active INIT(= false);
#define DECORATION_PROVIDER_INIT(ns_id) (DecorProvider) \
{ ns_id, false, LUA_NOREF, LUA_NOREF, \

View File

@@ -149,22 +149,22 @@ int hl_get_syn_attr(int ns_id, int idx, HlAttrs at_en)
}
}
static ColorKey colored_key(NS ns_id, int syn_id)
{
return (ColorKey){ .ns_id = (int)ns_id, .syn_id = syn_id };
}
void ns_hl_def(NS ns_id, int hl_id, HlAttrs attrs, int link_id)
{
DecorProvider *p = get_provider(ns_id, true);
if ((attrs.rgb_ae_attr & HL_DEFAULT)
&& map_has(ColorKey, ColorItem)(ns_hl, ColorKey(ns_id, hl_id))) {
return;
}
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 };
map_put(ColorKey, ColorItem)(ns_hl, colored_key(ns_id, hl_id), it);
.version = p->hl_valid,
.is_default = (attrs.rgb_ae_attr & HL_DEFAULT) };
map_put(ColorKey, ColorItem)(ns_hl, ColorKey(ns_id, hl_id), it);
}
int ns_get_hl(NS ns_id, int hl_id, bool link)
int ns_get_hl(NS ns_id, int hl_id, bool link, bool nodefault)
{
static int recursive = 0;
@@ -176,7 +176,7 @@ int ns_get_hl(NS ns_id, int hl_id, bool link)
}
DecorProvider *p = get_provider(ns_id, true);
ColorItem it = map_get(ColorKey, ColorItem)(ns_hl, colored_key(ns_id, hl_id));
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;
@@ -218,11 +218,16 @@ int ns_get_hl(NS ns_id, int hl_id, bool link)
it.attr_id = fallback ? -1 : hl_get_syn_attr((int)ns_id, hl_id, attrs);
it.version = p->hl_valid-tmp;
map_put(ColorKey, ColorItem)(ns_hl, colored_key(ns_id, hl_id), it);
it.is_default = attrs.rgb_ae_attr & HL_DEFAULT;
map_put(ColorKey, ColorItem)(ns_hl, ColorKey(ns_id, hl_id), it);
}
if (it.is_default && nodefault) {
return -1;
}
if (link) {
return it.attr_id >= 0 ? -1 : it.link_id;
return it.attr_id >= 0 ? 0 : it.link_id;
} else {
return it.attr_id;
}
@@ -307,7 +312,7 @@ void update_window_hl(win_T *wp, bool invalid)
//
// haha, theme engine go brrr
int normality = syn_check_group((const char_u *)S_LEN("Normal"));
int ns_attr = ns_get_hl(-1, normality, false);
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;
@@ -793,6 +798,8 @@ HlAttrs dict2hlattrs(Dictionary dict, bool use_rgb, int *link_id, Error *err)
{ "undercurl", HL_UNDERCURL },
{ "italic", HL_ITALIC },
{ "reverse", HL_INVERSE },
{ "default", HL_DEFAULT },
{ "global", HL_GLOBAL },
{ NULL, 0 },
};

View File

@@ -22,6 +22,8 @@ typedef enum {
HL_NOCOMBINE = 0x80,
HL_BG_INDEXED = 0x0100,
HL_FG_INDEXED = 0x0200,
HL_DEFAULT = 0x0400,
HL_GLOBAL = 0x0800,
} HlAttrFlags;
/// Stores a complete highlighting entry, including colors and attributes
@@ -188,13 +190,16 @@ typedef struct {
int ns_id;
int syn_id;
} ColorKey;
#define ColorKey(n, s) (ColorKey) { .ns_id = (int)(n), .syn_id = (s) }
typedef struct {
int attr_id;
int link_id;
int version;
bool is_default;
} ColorItem;
#define COLOR_ITEM_INITIALIZER { .attr_id = -1, .link_id = -1, .version = -1 }
#define COLOR_ITEM_INITIALIZER { .attr_id = -1, .link_id = -1, \
.version = -1, .is_default = false }
#endif // NVIM_HIGHLIGHT_DEFS_H

View File

@@ -173,7 +173,9 @@ static bool provider_invoke(NS ns_id, const char *name, LuaRef ref,
Error err = ERROR_INIT;
textlock++;
provider_active = true;
Object ret = nlua_call_ref(ref, name, args, true, &err);
provider_active = false;
textlock--;
if (!ERROR_SET(&err)

View File

@@ -7563,14 +7563,13 @@ static void syn_unadd_group(void)
/// @see syn_attr2entry
int syn_id2attr(int hl_id)
{
struct hl_group *sgp;
hl_id = syn_get_final_id(hl_id);
int attr = ns_get_hl(-1, hl_id, false);
struct hl_group *sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
int attr = ns_get_hl(-1, hl_id, false, sgp->sg_set);
if (attr >= 0) {
return attr;
}
sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
return sgp->sg_attr;
}
@@ -7583,7 +7582,6 @@ int syn_id2attr(int hl_id)
int syn_get_final_id(int hl_id)
{
int count;
struct hl_group *sgp;
if (hl_id > highlight_ga.ga_len || hl_id < 1)
return 0; /* Can be called from eval!! */
@@ -7593,19 +7591,20 @@ int syn_get_final_id(int hl_id)
* Look out for loops! Break after 100 links.
*/
for (count = 100; --count >= 0; ) {
struct hl_group *sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
// ACHTUNG: when using "tmp" attribute (no link) the function might be
// called twice. it needs be smart enough to remember attr only to
// syn_id2attr time
int check = ns_get_hl(-1, hl_id, true);
int check = ns_get_hl(-1, hl_id, true, sgp->sg_set);
if (check == 0) {
return 0; // how dare! it broke the link!
return hl_id; // how dare! it broke the link!
} else if (check > 0) {
hl_id = check;
continue;
}
sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len) {
break;
}

View File

@@ -2,6 +2,7 @@
#define NVIM_TYPES_H
#include <stdint.h>
#include <stdbool.h>
// dummy to pass an ACL to a function
typedef void *vim_acl_T;

View File

@@ -27,6 +27,7 @@ describe('decorations providers', function()
[9] = {reverse = true};
[10] = {italic = true, background = Screen.colors.Magenta};
[11] = {foreground = Screen.colors.Red, background = tonumber('0x005028')};
[12] = {foreground = tonumber('0x990000')};
}
end)
@@ -227,4 +228,77 @@ describe('decorations providers', function()
]]}
end)
it('can break an existing link', function()
insert(mulholland)
local ns1 = setup_provider()
exec [[
highlight OriginalGroup guifg='#990000'
highlight link LinkGroup OriginalGroup
]]
meths.buf_set_virtual_text(0, 0, 2, {{'- not red', 'LinkGroup'}}, {})
screen:expect{grid=[[
// just to see if there was an accident |
// on Mulholland Drive |
try_start(); {12:- not red} |
bufref_T save_buf; |
switch_buffer(&save_buf, buf); |
posp = getmark(mark, false); |
restore_buffer(&save_buf);^ |
|
]]}
meths.set_hl(ns1, 'LinkGroup', {fg = 'Blue'})
meths.set_hl_ns(ns1)
screen:expect{grid=[[
// just to see if there was an accident |
// on Mulholland Drive |
try_start(); {4:- not red} |
bufref_T save_buf; |
switch_buffer(&save_buf, buf); |
posp = getmark(mark, false); |
restore_buffer(&save_buf);^ |
|
]]}
end)
it("with 'default': do not break an existing link", function()
insert(mulholland)
local ns1 = setup_provider()
exec [[
highlight OriginalGroup guifg='#990000'
highlight link LinkGroup OriginalGroup
]]
meths.buf_set_virtual_text(0, 0, 2, {{'- not red', 'LinkGroup'}}, {})
screen:expect{grid=[[
// just to see if there was an accident |
// on Mulholland Drive |
try_start(); {12:- not red} |
bufref_T save_buf; |
switch_buffer(&save_buf, buf); |
posp = getmark(mark, false); |
restore_buffer(&save_buf);^ |
|
]]}
meths.set_hl(ns1, 'LinkGroup', {fg = 'Blue', default=true})
meths.set_hl_ns(ns1)
feed 'k'
screen:expect{grid=[[
// just to see if there was an accident |
// on Mulholland Drive |
try_start(); {12:- not red} |
bufref_T save_buf; |
switch_buffer(&save_buf, buf); |
posp = getmark(mark, false^); |
restore_buffer(&save_buf); |
|
]]}
end)
end)