aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/api/vim.c5
-rw-r--r--src/nvim/decoration.h1
-rw-r--r--src/nvim/highlight.c31
-rw-r--r--src/nvim/highlight_defs.h7
-rw-r--r--src/nvim/screen.c2
-rw-r--r--src/nvim/syntax.c15
-rw-r--r--src/nvim/types.h1
-rw-r--r--test/functional/ui/decorations_spec.lua74
8 files changed, 114 insertions, 22 deletions
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index cf822782d8..710885d295 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -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;
diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h
index 90fdc3dc43..c5941ae2fe 100644
--- a/src/nvim/decoration.h
+++ b/src/nvim/decoration.h
@@ -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, \
diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c
index 898ff4ebfe..b01cdde236 100644
--- a/src/nvim/highlight.c
+++ b/src/nvim/highlight.c
@@ -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 },
};
diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h
index 6a5c593ab1..2bda094d8e 100644
--- a/src/nvim/highlight_defs.h
+++ b/src/nvim/highlight_defs.h
@@ -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
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 8998f9037e..5f09912116 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -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)
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 5e54354ea8..e91d560284 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -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;
}
diff --git a/src/nvim/types.h b/src/nvim/types.h
index 17f7e16740..2dbeecbf6d 100644
--- a/src/nvim/types.h
+++ b/src/nvim/types.h
@@ -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;
diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua
index 4182090732..781fdf7203 100644
--- a/test/functional/ui/decorations_spec.lua
+++ b/test/functional/ui/decorations_spec.lua
@@ -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)