// highlight.c: low level code for UI and syntax highlighting
#include <assert.h>
#include <inttypes.h>
#include <lauxlib.h>
#include <string.h>
#include "nvim/api/keysets_defs.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/dispatch.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/private/validate.h"
#include "nvim/api/ui.h"
#include "nvim/decoration_provider.h"
#include "nvim/drawscreen.h"
#include "nvim/gettext.h"
#include "nvim/globals.h"
#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/lua/executor.h"
#include "nvim/macros_defs.h"
#include "nvim/map_defs.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/option.h"
#include "nvim/popupmenu.h"
#include "nvim/strings.h"
#include "nvim/types_defs.h"
#include "nvim/ui.h"
#include "nvim/vim_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "highlight.c.generated.h"
#endif
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;
#define attr_entry(i) attr_entries.keys[i]
/// highlight entries private to a namespace
static Map(ColorKey, ColorItem) ns_hls;
typedef int NSHlAttr[HLF_COUNT + 1];
static PMap(int) ns_hl_attr;
void highlight_init(void)
{
// index 0 is no attribute, add dummy entry:
set_put(HlEntry, &attr_entries, ((HlEntry){ .attr = HLATTRS_INIT, .kind = kHlInvalid,
.id1 = 0, .id2 = 0 }));
}
/// @return true if hl table was reset
bool highlight_use_hlstate(void)
{
if (hlstate_active) {
return false;
}
hlstate_active = true;
// hl tables must now be rebuilt.
clear_hl_tables(true);
return true;
}
/// Return the attr number for a set of colors and font, and optionally
/// a semantic description (see ext_hlstate documentation).
/// Add a new entry to the attr_entries array if the combination is new.
/// @return 0 for error.
static int get_attr_entry(HlEntry entry)
{
bool retried = false;
if (!hlstate_active) {
// This information will not be used, erase it and reduce the table size.
entry.kind = kHlUnknown;
entry.id1 = 0;
entry.id2 = 0;
}
retry: {}
MHPutStatus status;
uint32_t k = set_put_idx(HlEntry, &attr_entries, entry, &status);
if (status == kMHExisting) {
return (int)k;
}
static bool recursive = false;
if (set_size(&attr_entries) > MAX_TYPENR) {
// Running out of attribute entries! remove all attributes, and
// compute new ones for all groups.
// When called recursively, we are really out of numbers.
if (recursive || retried) {
emsg(_("E424: Too many different highlighting attributes in use"));
return 0;
}
recursive = true;
clear_hl_tables(true);
recursive = false;
if (entry.kind == kHlCombine) {
// This entry is now invalid, don't put it
return 0;
}
retried = true;
goto retry;
}
// new attr id, send event to remote ui:s
int id = (int)k;
Array inspect = hl_inspect(id);
// Note: internally we don't distinguish between cterm and rgb attributes,
// remote_ui_hl_attr_define will however.
ui_call_hl_attr_define(id, entry.attr, entry.attr, inspect);
api_free_array(inspect);
return id;
}
/// When a UI connects, we need to send it the table of highlights used so far.
void ui_send_all_hls(UI *ui)
{
for (size_t i = 1; i < set_size(&attr_entries); i++) {
Array inspect = hl_inspect((int)i);
HlAttrs attr = attr_entry(i).attr;
remote_ui_hl_attr_define(ui, (Integer)i, attr, attr, inspect);
api_free_array(inspect);
}
for (size_t hlf = 0; hlf < HLF_COUNT; hlf++) {
remote_ui_hl_group_set(ui, cstr_as_string((char *)hlf_names[hlf]),
highlight_attr[hlf]);
}
}
/// Get attribute code for a syntax group.
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 || ns_id != 0) {
return get_attr_entry((HlEntry){ .attr = at_en, .kind = kHlSyntax,
.id1 = idx, .id2 = ns_id });
}
// If all the fields are cleared, clear the attr field back to default value
return 0;
}
void ns_hl_def(NS ns_id, int hl_id, HlAttrs attrs, int link_id, Dict(highlight) *dict)
{
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, &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),
.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_hl, int hl_id, bool link, bool nodefault)
{
|