// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// highlight.c: low level code for UI and syntax highlighting
#include <assert.h>
#include <inttypes.h>
#include <limits.h>
#include <string.h>
#include "klib/kvec.h"
#include "lauxlib.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.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/log.h"
#include "nvim/lua/executor.h"
#include "nvim/macros.h"
#include "nvim/map.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/option.h"
#include "nvim/popupmenu.h"
#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "highlight.c.generated.h"
#endif
static bool hlstate_active = false;
static kvec_t(HlEntry) attr_entries = KV_INITIAL_VALUE;
static Map(HlEntry, int) attr_entry_ids = MAP_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;
/// highlight entries private to a namespace
static Map(ColorKey, ColorItem) ns_hls;
typedef int NSHlAttr[HLF_COUNT + 1];
static PMap(handle_T) ns_hl_attr;
void highlight_init(void)
{
// index 0 is no attribute, add dummy entry:
kv_push(attr_entries, ((HlEntry){ .attr = HLATTRS_INIT, .kind = kHlUnknown,
.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)
{
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;
}
int id = map_get(HlEntry, int)(&attr_entry_ids, entry);
if (id > 0) {
return id;
}
static bool recursive = false;
if (kv_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) {
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;
}
}
size_t next_id = kv_size(attr_entries);
if (next_id > INT_MAX) {
ELOG("The index on attr_entries has overflowed");
return 0;
}
id = (int)next_id;
kv_push(attr_entries, entry);
map_put(HlEntry, int)(&attr_entry_ids, entry, id);
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 < kv_size(attr_entries); i++) {
Array inspect = hl_inspect((int)i);
remote_ui_hl_attr_define(ui, (Integer)i, kv_A(attr_entries, i).attr,
kv_A(attr_entries, i).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, 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),
.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)
{
static int recursive = 0;
if (*ns_hl == 0) {
// ns=0 (the default namespace) does not have a
|