aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/api/buffer.c6
-rw-r--r--src/nvim/api/private/helpers.c34
-rw-r--r--src/nvim/api/vim.c95
-rw-r--r--src/nvim/api/window.c4
-rw-r--r--src/nvim/buffer.c2
-rw-r--r--src/nvim/change.c6
-rw-r--r--src/nvim/cursor.c2
-rw-r--r--src/nvim/diff.c4
-rw-r--r--src/nvim/edit.c6
-rw-r--r--src/nvim/ex_cmds.c6
-rw-r--r--src/nvim/ex_docmd.c4
-rw-r--r--src/nvim/ex_getln.c2
-rw-r--r--src/nvim/extmark_defs.h5
-rw-r--r--src/nvim/fold.c2
-rw-r--r--src/nvim/globals.h4
-rw-r--r--src/nvim/highlight.c213
-rw-r--r--src/nvim/highlight_defs.h15
-rw-r--r--src/nvim/lua/executor.c11
-rw-r--r--src/nvim/main.c2
-rw-r--r--src/nvim/map.c18
-rw-r--r--src/nvim/map.h2
-rw-r--r--src/nvim/menu.c2
-rw-r--r--src/nvim/message.c4
-rw-r--r--src/nvim/mouse.c4
-rw-r--r--src/nvim/move.c41
-rw-r--r--src/nvim/normal.c16
-rw-r--r--src/nvim/ops.c2
-rw-r--r--src/nvim/option.c8
-rw-r--r--src/nvim/popupmnu.c2
-rw-r--r--src/nvim/quickfix.c2
-rw-r--r--src/nvim/screen.c57
-rw-r--r--src/nvim/search.c2
-rw-r--r--src/nvim/spell.c4
-rw-r--r--src/nvim/syntax.c92
-rw-r--r--src/nvim/tag.c2
-rw-r--r--src/nvim/terminal.c4
-rw-r--r--src/nvim/types.h2
-rw-r--r--src/nvim/ui_compositor.c1
-rw-r--r--src/nvim/undo.c2
-rw-r--r--src/nvim/window.c49
-rw-r--r--test/functional/helpers.lua8
-rw-r--r--test/functional/ui/decorations_spec.lua142
-rw-r--r--test/functional/ui/screen.lua4
43 files changed, 690 insertions, 203 deletions
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index cad4c8314f..4569ebc713 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -974,9 +974,9 @@ void nvim_buf_delete(Buffer buffer, Dictionary opts, Error *err)
String k = opts.items[i].key;
Object v = opts.items[i].value;
if (strequal("force", k.data)) {
- force = api_coerce_to_bool(v, "force", false, err);
+ force = api_object_to_bool(v, "force", false, err);
} else if (strequal("unload", k.data)) {
- unload = api_coerce_to_bool(v, "unload", false, err);
+ unload = api_object_to_bool(v, "unload", false, err);
} else {
api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data);
return;
@@ -1441,7 +1441,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
goto error;
}
} else if (strequal("ephemeral", k.data)) {
- ephemeral = api_coerce_to_bool(*v, "ephemeral", false, err);
+ ephemeral = api_object_to_bool(*v, "ephemeral", false, err);
if (ERROR_SET(err)) {
goto error;
}
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 981d41ae6e..a9b1676879 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -1625,11 +1625,8 @@ free_exit:
/// @param what The name of the object, used for error message
/// @param nil_value What to return if the type is nil.
/// @param err Set if there was an error in converting to a bool
-bool api_coerce_to_bool(
- Object obj,
- const char *what,
- bool nil_value,
- Error *err)
+bool api_object_to_bool(Object obj, const char *what,
+ bool nil_value, Error *err)
{
if (obj.type == kObjectTypeBoolean) {
return obj.data.boolean;
@@ -1654,3 +1651,30 @@ const char *describe_ns(NS ns_id)
})
return "(UNKNOWN PLUGIN)";
}
+
+DecorationProvider *get_provider(NS ns_id, bool force)
+{
+ ssize_t i;
+ for (i = 0; i < (ssize_t)kv_size(decoration_providers); i++) {
+ DecorationProvider *item = &kv_A(decoration_providers, i);
+ if (item->ns_id == ns_id) {
+ return item;
+ } else if (item->ns_id > ns_id) {
+ break;
+ }
+ }
+
+ if (!force) {
+ return NULL;
+ }
+
+ for (ssize_t j = (ssize_t)kv_size(decoration_providers)-1; j >= i; j++) {
+ // allocates if needed:
+ (void)kv_a(decoration_providers, (size_t)j+1);
+ kv_A(decoration_providers, (size_t)j+1) = kv_A(decoration_providers, j);
+ }
+ DecorationProvider *item = &kv_a(decoration_providers, (size_t)i);
+ *item = DECORATION_PROVIDER_INIT(ns_id);
+
+ return item;
+}
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 876b052a8e..24d82ccd0f 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -199,6 +199,69 @@ Integer nvim_get_hl_id_by_name(String name)
return syn_check_group((const char_u *)name.data, (int)name.size);
}
+Dictionary nvim__get_hl_defs(Integer ns_id, Error *err)
+{
+ if (ns_id == 0) {
+ return get_global_hl_defs();
+ }
+ abort();
+}
+
+/// Set a highlight group.
+///
+/// @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|.
+/// @param[out] err Error details, if any
+///
+/// TODO: ns_id = 0, should modify :highlight namespace
+/// TODO val should take update vs reset flag
+void nvim_set_hl(Integer ns_id, String name, Dictionary val, Error *err)
+ FUNC_API_SINCE(7)
+{
+ int hl_id = syn_check_group( (char_u *)(name.data), (int)name.size);
+ int link_id = -1;
+
+ HlAttrs attrs = dict2hlattrs(val, true, &link_id, err);
+ if (!ERROR_SET(err)) {
+ ns_hl_def((NS)ns_id, hl_id, attrs, link_id);
+ }
+}
+
+/// Set active namespace for highlights.
+///
+/// NB: this function can be called from async contexts, but the
+/// semantics are not yet well-defined. To start with
+/// |nvim_set_decoration_provider| on_win and on_line callbacks
+/// are explicitly allowed to change the namespace during a redraw cycle.
+///
+/// @param ns_id the namespace to activate
+/// @param[out] err Error details, if any
+void nvim_set_hl_ns(Integer ns_id, Error *err)
+ FUNC_API_SINCE(7)
+ FUNC_API_FAST
+{
+ if (ns_id >= 0) {
+ ns_hl_active = (NS)ns_id;
+ }
+
+ // TODO(bfredl): this is a little bit hackish. Eventually we want a standard
+ // 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) {
+ multiqueue_put(main_loop.events, on_redraw_event, 0);
+ }
+ ns_hl_changed = true;
+}
+
+static void on_redraw_event(void **argv)
+ FUNC_API_NOEXPORT
+{
+ redraw_all_later(NOT_VALID);
+}
+
+
/// Sends input-keys to Nvim, subject to various quirks controlled by `mode`
/// flags. This is a blocking call, unlike |nvim_input()|.
///
@@ -1477,7 +1540,7 @@ void nvim_unsubscribe(uint64_t channel_id, String event)
Integer nvim_get_color_by_name(String name)
FUNC_API_SINCE(1)
{
- return name_to_color((char_u *)name.data);
+ return name_to_color(name.data);
}
/// Returns a map of color names and RGB values.
@@ -2610,33 +2673,6 @@ void nvim__screenshot(String path)
ui_call_screenshot(path);
}
-static DecorationProvider *get_provider(NS ns_id, bool force)
-{
- ssize_t i;
- for (i = 0; i < (ssize_t)kv_size(decoration_providers); i++) {
- DecorationProvider *item = &kv_A(decoration_providers, i);
- if (item->ns_id == ns_id) {
- return item;
- } else if (item->ns_id > ns_id) {
- break;
- }
- }
-
- if (!force) {
- return NULL;
- }
-
- for (ssize_t j = (ssize_t)kv_size(decoration_providers)-1; j >= i; j++) {
- // allocates if needed:
- (void)kv_a(decoration_providers, (size_t)j+1);
- kv_A(decoration_providers, (size_t)j+1) = kv_A(decoration_providers, j);
- }
- DecorationProvider *item = &kv_a(decoration_providers, (size_t)i);
- *item = DECORATION_PROVIDER_INIT(ns_id);
-
- return item;
-}
-
static void clear_provider(DecorationProvider *p)
{
NLUA_CLEAR_REF(p->redraw_start);
@@ -2695,7 +2731,7 @@ void nvim_set_decoration_provider(Integer ns_id, DictionaryOf(LuaRef) opts,
clear_provider(p);
// regardless of what happens, it seems good idea to redraw
- redraw_later(NOT_VALID); // TODO(bfredl): too soon?
+ redraw_all_later(NOT_VALID); // TODO(bfredl): too soon?
struct {
const char *name;
@@ -2706,6 +2742,7 @@ void nvim_set_decoration_provider(Integer ns_id, DictionaryOf(LuaRef) opts,
{ "on_win", &p->redraw_win },
{ "on_line", &p->redraw_line },
{ "on_end", &p->redraw_end },
+ { "_on_hl_def", &p->hl_def },
{ NULL, NULL },
};
diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c
index ba43bc6cb2..f09a03f592 100644
--- a/src/nvim/api/window.c
+++ b/src/nvim/api/window.c
@@ -142,7 +142,7 @@ void nvim_win_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err)
// make sure cursor is in visible range even if win != curwin
update_topline_win(win);
- redraw_win_later(win, VALID);
+ redraw_later(win, VALID);
}
/// Gets the window height
@@ -471,7 +471,7 @@ void nvim_win_set_config(Window window, Dictionary config, Error *err)
if (!win_new_float(win, fconfig, err)) {
return;
}
- redraw_later(NOT_VALID);
+ redraw_later(win, NOT_VALID);
} else {
win_config_float(win, fconfig);
win->w_pos_changed = true;
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 8f631ae13b..9cd2fd4beb 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -1623,7 +1623,7 @@ void enter_buffer(buf_T *buf)
}
curbuf->b_last_used = time(NULL);
- redraw_later(NOT_VALID);
+ redraw_later(curwin, NOT_VALID);
}
// Change to the directory of the current buffer.
diff --git a/src/nvim/change.c b/src/nvim/change.c
index be52750c44..9ee987b45d 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -294,7 +294,7 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume,
// change.
if (wp->w_p_rnu
|| (wp->w_p_cul && lnum <= wp->w_last_cursorline)) {
- redraw_win_later(wp, SOME_VALID);
+ redraw_later(wp, SOME_VALID);
}
}
}
@@ -348,7 +348,7 @@ void changed_bytes(linenr_T lnum, colnr_T col)
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_p_diff && wp != curwin) {
- redraw_win_later(wp, VALID);
+ redraw_later(wp, VALID);
wlnum = diff_lnum_win(lnum, wp);
if (wlnum > 0) {
changedOneline(wp->w_buffer, wlnum);
@@ -475,7 +475,7 @@ changed_lines(
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_p_diff && wp != curwin) {
- redraw_win_later(wp, VALID);
+ redraw_later(wp, VALID);
wlnum = diff_lnum_win(lnum, wp);
if (wlnum > 0) {
changed_lines_buf(wp->w_buffer, wlnum,
diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c
index 036ae32589..d3ffab1759 100644
--- a/src/nvim/cursor.c
+++ b/src/nvim/cursor.c
@@ -482,7 +482,7 @@ bool leftcol_changed(void)
if (retval)
curwin->w_set_curswant = true;
- redraw_later(NOT_VALID);
+ redraw_later(curwin, NOT_VALID);
return retval;
}
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index 3de5fc49bd..b9c293f6c8 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -643,7 +643,7 @@ void diff_redraw(bool dofold)
if (!wp->w_p_diff) {
continue;
}
- redraw_win_later(wp, SOME_VALID);
+ redraw_later(wp, SOME_VALID);
if (dofold && foldmethodIsDiff(wp)) {
foldUpdateAll(wp);
}
@@ -1415,7 +1415,7 @@ void diff_win_options(win_T *wp, int addbuf)
if (addbuf) {
diff_buf_add(wp->w_buffer);
}
- redraw_win_later(wp, NOT_VALID);
+ redraw_later(wp, NOT_VALID);
}
/// Set options not to show diffs. For the current window or all windows.
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index ac0e6cc9f6..3e62ed9036 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -8571,7 +8571,7 @@ static void ins_up(
if (old_topline != curwin->w_topline
|| old_topfill != curwin->w_topfill
)
- redraw_later(VALID);
+ redraw_later(curwin, VALID);
start_arrow(&tpos);
can_cindent = true;
} else {
@@ -8619,7 +8619,7 @@ static void ins_down(
if (old_topline != curwin->w_topline
|| old_topfill != curwin->w_topfill
)
- redraw_later(VALID);
+ redraw_later(curwin, VALID);
start_arrow(&tpos);
can_cindent = true;
} else {
@@ -9013,7 +9013,7 @@ static int ins_ctrl_ey(int tc)
scrolldown_clamp();
else
scrollup_clamp();
- redraw_later(VALID);
+ redraw_later(curwin, VALID);
} else {
c = ins_copychar(curwin->w_cursor.lnum + (c == Ctrl_Y ? -1 : 1));
if (c != NUL) {
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 3669cbbd2d..41137bf2db 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -3721,8 +3721,8 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
update_topline();
validate_cursor();
update_screen(SOME_VALID);
- highlight_match = FALSE;
- redraw_later(SOME_VALID);
+ highlight_match = false;
+ redraw_later(curwin, SOME_VALID);
curwin->w_p_fen = save_p_fen;
if (msg_row == Rows - 1)
@@ -5751,7 +5751,7 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr,
}
xfree(str);
- redraw_later(SOME_VALID);
+ redraw_later(curwin, SOME_VALID);
win_enter(save_curwin, false); // Return to original window
update_topline();
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 211791c19d..59e9f96c3a 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -7379,7 +7379,7 @@ static void ex_syncbind(exarg_T *eap)
else
scrolldown(-y, TRUE);
curwin->w_scbind_pos = topline;
- redraw_later(VALID);
+ redraw_later(curwin, VALID);
cursor_correct();
curwin->w_redr_status = TRUE;
}
@@ -8504,7 +8504,7 @@ static void ex_pedit(exarg_T *eap)
if (curwin != curwin_save && win_valid(curwin_save)) {
// Return cursor to where we were
validate_cursor();
- redraw_later(VALID);
+ redraw_later(curwin, VALID);
win_enter(curwin_save, true);
}
g_do_tagpreview = 0;
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index d67e9b2d7e..53feffd2d7 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -6483,7 +6483,7 @@ static int open_cmdwin(void)
ccline.redraw_state = kCmdRedrawNone;
ui_call_cmdline_hide(ccline.level);
}
- redraw_later(SOME_VALID);
+ redraw_later(curwin, SOME_VALID);
// Save the command line info, can be used recursively.
save_cmdline(&save_ccline);
diff --git a/src/nvim/extmark_defs.h b/src/nvim/extmark_defs.h
index f5ca0ebbb0..382aaf6ed3 100644
--- a/src/nvim/extmark_defs.h
+++ b/src/nvim/extmark_defs.h
@@ -50,10 +50,13 @@ typedef struct {
LuaRef redraw_win;
LuaRef redraw_line;
LuaRef redraw_end;
+ LuaRef hl_def;
+ int hl_valid;
} DecorationProvider;
#define DECORATION_PROVIDER_INIT(ns_id) (DecorationProvider) \
{ ns_id, false, LUA_NOREF, LUA_NOREF, \
- LUA_NOREF, LUA_NOREF, LUA_NOREF }
+ LUA_NOREF, LUA_NOREF, LUA_NOREF, \
+ LUA_NOREF, -1 }
#endif // NVIM_EXTMARK_DEFS_H
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index 197aedabec..57001f77ed 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -862,7 +862,7 @@ void foldUpdateAfterInsert(void)
void foldUpdateAll(win_T *win)
{
win->w_foldinvalid = true;
- redraw_win_later(win, NOT_VALID);
+ redraw_later(win, NOT_VALID);
}
// foldMoveTo() {{{2
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 2db8689a56..7f3e4a7dfc 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -92,6 +92,10 @@ EXTERN struct nvim_stats_s {
EXTERN int Rows INIT(= DFLT_ROWS); // nr of rows in the screen
EXTERN int Columns INIT(= DFLT_COLS); // nr of columns in the screen
+EXTERN NS ns_hl_active INIT(= 0); // current ns that defines highlights
+EXTERN bool ns_hl_changed INIT(= false); // highlight need update
+
+
// We use 64-bit file functions here, if available. E.g. ftello() returns
// off_t instead of long, which helps if long is 32 bit and off_t is 64 bit.
// We assume that when fseeko() is available then ftello() is too.
diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c
index 262afba07a..8403615166 100644
--- a/src/nvim/highlight.c
+++ b/src/nvim/highlight.c
@@ -14,6 +14,7 @@
#include "nvim/ui.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
+#include "nvim/lua/executor.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "highlight.c.generated.h"
@@ -28,12 +29,16 @@ static Map(int, int) *combine_attr_entries;
static Map(int, int) *blend_attr_entries;
static Map(int, int) *blendthrough_attr_entries;
+/// highlight entries private to a namespace
+static Map(ColorKey, ColorItem) *ns_hl;
+
void highlight_init(void)
{
attr_entry_ids = map_new(HlEntry, int)();
combine_attr_entries = map_new(int, int)();
blend_attr_entries = map_new(int, int)();
blendthrough_attr_entries = map_new(int, int)();
+ ns_hl = map_new(ColorKey, ColorItem)();
// index 0 is no attribute, add dummy entry:
kv_push(attr_entries, ((HlEntry){ .attr = HLATTRS_INIT, .kind = kHlUnknown,
@@ -129,21 +134,114 @@ void ui_send_all_hls(UI *ui)
}
/// Get attribute code for a syntax group.
-int hl_get_syn_attr(int idx, HlAttrs at_en)
+int hl_get_syn_attr(int ns_id, int idx, HlAttrs at_en)
{
// TODO(bfredl): should we do this unconditionally
if (at_en.cterm_fg_color != 0 || at_en.cterm_bg_color != 0
|| at_en.rgb_fg_color != -1 || at_en.rgb_bg_color != -1
|| at_en.rgb_sp_color != -1 || at_en.cterm_ae_attr != 0
- || at_en.rgb_ae_attr != 0) {
+ || at_en.rgb_ae_attr != 0 || ns_id != 0) {
return get_attr_entry((HlEntry){ .attr = at_en, .kind = kHlSyntax,
- .id1 = idx, .id2 = 0 });
+ .id1 = idx, .id2 = ns_id });
} else {
// If all the fields are cleared, clear the attr field back to default value
return 0;
}
}
+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)
+{
+ DecorationProvider *p = get_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 };
+ map_put(ColorKey, ColorItem)(ns_hl, colored_key(ns_id, hl_id), it);
+}
+
+int ns_get_hl(NS ns_id, int hl_id, bool link)
+{
+ static int recursive = 0;
+
+ if (ns_id < 0) {
+ if (ns_hl_active <= 0) {
+ return -1;
+ }
+ ns_id = ns_hl_active;
+ }
+
+ DecorationProvider *p = get_provider(ns_id, true);
+ ColorItem it = map_get(ColorKey, ColorItem)(ns_hl, colored_key(ns_id, hl_id));
+ // TODO(bfredl): map_ref true even this?
+ bool valid_cache = it.version >= p->hl_valid;
+
+ if (!valid_cache && p->hl_def != LUA_NOREF && !recursive) {
+ FIXED_TEMP_ARRAY(args, 3);
+ args.items[0] = INTEGER_OBJ((Integer)ns_id);
+ args.items[1] = STRING_OBJ(cstr_to_string((char *)syn_id2name(hl_id)));
+ args.items[2] = BOOLEAN_OBJ(link);
+ // TODO(bfredl): preload the "global" attr dict?
+
+ Error err = ERROR_INIT;
+ recursive++;
+ Object ret = nlua_call_ref(p->hl_def, "hl_def", args, true, &err);
+ recursive--;
+
+ // TODO(bfredl): or "inherit", combine with global value?
+ bool fallback = true;
+ int tmp = false;
+ HlAttrs attrs = HLATTRS_INIT;
+ if (ret.type == kObjectTypeDictionary) {
+ Dictionary dict = ret.data.dictionary;
+ fallback = false;
+ attrs = dict2hlattrs(dict, true, &it.link_id, &err);
+ for (size_t i = 0; i < dict.size; i++) {
+ char *key = dict.items[i].key.data;
+ Object val = dict.items[i].value;
+ bool truthy = api_object_to_bool(val, key, false, &err);
+
+ if (strequal(key, "fallback")) {
+ fallback = truthy;
+ } else if (strequal(key, "temp")) {
+ tmp = truthy;
+ }
+ }
+ if (it.link_id >= 0) {
+ fallback = true;
+ }
+ }
+
+ 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);
+ }
+
+ if (link) {
+ return it.attr_id >= 0 ? -1 : it.link_id;
+ } else {
+ return it.attr_id;
+ }
+}
+
+
+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;
+}
+
/// Get attribute code for a builtin highlight group.
///
/// The final syntax group could be modified by hi-link or 'winhighlight'.
@@ -204,6 +302,17 @@ void update_window_hl(win_T *wp, bool invalid)
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((const char_u *)S_LEN("Normal"));
+ int ns_attr = ns_get_hl(-1, normality, 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);
@@ -275,6 +384,7 @@ void clear_hl_tables(bool reinit)
map_free(int, int)(combine_attr_entries);
map_free(int, int)(blend_attr_entries);
map_free(int, int)(blendthrough_attr_entries);
+ map_free(ColorKey, ColorItem)(ns_hl);
}
}
@@ -663,6 +773,103 @@ Dictionary hlattrs2dict(HlAttrs ae, bool use_rgb)
return hl;
}
+HlAttrs dict2hlattrs(Dictionary dict, bool use_rgb, int *link_id, Error *err)
+{
+ HlAttrs hlattrs = HLATTRS_INIT;
+
+ int32_t fg = -1, bg = -1, sp = -1;
+ int16_t mask = 0;
+ for (size_t i = 0; i < dict.size; i++) {
+ char *key = dict.items[i].key.data;
+ Object val = dict.items[i].value;
+
+ struct {
+ const char *name;
+ int16_t flag;
+ } flags[] = {
+ { "bold", HL_BOLD },
+ { "standout", HL_STANDOUT },
+ { "underline", HL_UNDERLINE },
+ { "undercurl", HL_UNDERCURL },
+ { "italic", HL_ITALIC },
+ { "reverse", HL_INVERSE },
+ { NULL, 0 },
+ };
+
+ int j;
+ for (j = 0; flags[j].name; j++) {
+ if (strequal(flags[j].name, key)) {
+ if (api_object_to_bool(val, key, false, err)) {
+ mask = mask | flags[j].flag;
+ }
+ break;
+ }
+ }
+
+ struct {
+ const char *name;
+ const char *shortname;
+ int *dest;
+ } colors[] = {
+ { "foreground", "fg", &fg },
+ { "background", "bg", &bg },
+ { "special", "sp", &sp },
+ { NULL, NULL, NULL },
+ };
+
+ int k;
+ for (k = 0; (!flags[j].name) && colors[k].name; k++) {
+ if (strequal(colors[k].name, key) || strequal(colors[k].shortname, key)) {
+ if (val.type == kObjectTypeInteger) {
+ *colors[k].dest = (int)val.data.integer;
+ } else if (val.type == kObjectTypeString) {
+ String str = val.data.string;
+ // TODO(bfredl): be more fancy with "bg", "fg" etc
+ if (str.size) {
+ *colors[k].dest = name_to_color(str.data);
+ }
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "'%s' must be string or integer", key);
+ }
+ break;
+ }
+ }
+
+
+ if (flags[j].name || colors[k].name) {
+ // handled above
+ } else if (link_id && strequal(key, "link")) {
+ if (val.type == kObjectTypeString) {
+ String str = val.data.string;
+ *link_id = syn_check_group((const char_u *)str.data, (int)str.size);
+ } else if (val.type == kObjectTypeInteger) {
+ // TODO(bfredl): validate range?
+ *link_id = (int)val.data.integer;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "'link' must be string or integer");
+ }
+ }
+
+ if (ERROR_SET(err)) {
+ return hlattrs; // error set, caller should not use retval
+ }
+ }
+
+ if (use_rgb) {
+ hlattrs.rgb_ae_attr = mask;
+ hlattrs.rgb_bg_color = bg;
+ hlattrs.rgb_fg_color = fg;
+ hlattrs.rgb_sp_color = sp;
+ } else {
+ hlattrs.cterm_ae_attr = mask;
+ hlattrs.cterm_bg_color = bg == -1 ? cterm_normal_bg_color : bg + 1;
+ hlattrs.cterm_fg_color = fg == -1 ? cterm_normal_fg_color : fg + 1;
+ }
+
+ return hlattrs;
+}
Array hl_inspect(int attr)
{
Array ret = ARRAY_DICT_INIT;
diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h
index 36f3181674..6a5c593ab1 100644
--- a/src/nvim/highlight_defs.h
+++ b/src/nvim/highlight_defs.h
@@ -4,6 +4,7 @@
#inc