aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/highlight.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/highlight.c')
-rw-r--r--src/nvim/highlight.c204
1 files changed, 202 insertions, 2 deletions
diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c
index 41d60fa3ea..4c5fca6d39 100644
--- a/src/nvim/highlight.c
+++ b/src/nvim/highlight.c
@@ -23,11 +23,15 @@ static kvec_t(HlEntry) attr_entries = KV_INITIAL_VALUE;
static Map(HlEntry, int) *attr_entry_ids;
static Map(int, int) *combine_attr_entries;
+static Map(int, int) *blend_attr_entries;
+static Map(int, int) *blendthrough_attr_entries;
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)();
// index 0 is no attribute, add dummy entry:
kv_push(attr_entries, ((HlEntry){ .attr = HLATTRS_INIT, .kind = kHlUnknown,
@@ -101,6 +105,9 @@ static int get_attr_entry(HlEntry entry)
/// When a UI connects, we need to send it the table of highlights used so far.
void ui_send_all_hls(UI *ui)
{
+ if (!ui->hl_attr_define) {
+ return;
+ }
for (size_t i = 1; i < kv_size(attr_entries); i++) {
Array inspect = hl_inspect((int)i);
ui->hl_attr_define(ui, (Integer)i, kv_A(attr_entries, i).attr,
@@ -210,6 +217,8 @@ void clear_hl_tables(bool reinit)
kv_size(attr_entries) = 1;
map_clear(HlEntry, int)(attr_entry_ids);
map_clear(int, int)(combine_attr_entries);
+ map_clear(int, int)(blend_attr_entries);
+ map_clear(int, int)(blendthrough_attr_entries);
highlight_attr_set_all();
highlight_changed();
screen_invalidate_highlights();
@@ -217,15 +226,22 @@ void clear_hl_tables(bool reinit)
kv_destroy(attr_entries);
map_free(HlEntry, int)(attr_entry_ids);
map_free(int, int)(combine_attr_entries);
+ map_free(int, int)(blend_attr_entries);
+ map_free(int, int)(blendthrough_attr_entries);
}
}
+void hl_invalidate_blends(void)
+{
+ map_clear(int, int)(blend_attr_entries);
+ map_clear(int, int)(blendthrough_attr_entries);
+}
+
// Combine special attributes (e.g., for spelling) with other attributes
// (e.g., for syntax highlighting).
// "prim_attr" overrules "char_attr".
// This creates a new group when required.
-// Since we expect there to be few spelling mistakes we don't cache the
-// result.
+// Since we expect there to be a lot of spelling mistakes we cache the result.
// Return the resulting attributes.
int hl_combine_attr(int char_attr, int prim_attr)
{
@@ -280,6 +296,188 @@ int hl_combine_attr(int char_attr, int prim_attr)
return id;
}
+/// Get the used rgb colors for an attr group.
+///
+/// If colors are unset, use builtin default colors. Never returns -1
+/// Cterm colors are unchanged.
+static HlAttrs get_colors_force(int attr)
+{
+ HlAttrs attrs = syn_attr2entry(attr);
+ if (attrs.rgb_bg_color == -1) {
+ attrs.rgb_bg_color = normal_bg;
+ }
+ if (attrs.rgb_fg_color == -1) {
+ attrs.rgb_fg_color = normal_fg;
+ }
+ if (attrs.rgb_sp_color == -1) {
+ attrs.rgb_sp_color = normal_sp;
+ }
+ HL_SET_DEFAULT_COLORS(attrs.rgb_fg_color, attrs.rgb_bg_color,
+ attrs.rgb_sp_color);
+
+ if (attrs.rgb_ae_attr & HL_INVERSE) {
+ int temp = attrs.rgb_bg_color;
+ attrs.rgb_bg_color = attrs.rgb_fg_color;
+ attrs.rgb_fg_color = temp;
+ attrs.rgb_ae_attr &= ~HL_INVERSE;
+ }
+
+ return attrs;
+}
+
+/// Blend overlay attributes (for popupmenu) with other attributes
+///
+/// This creates a new group when required.
+/// This is called per-cell, so cache the result.
+///
+/// @return the resulting attributes.
+int hl_blend_attrs(int back_attr, int front_attr, bool through)
+{
+ int combine_tag = (back_attr << 16) + front_attr;
+ Map(int, int) *map = through ? blendthrough_attr_entries : blend_attr_entries;
+ int id = map_get(int, int)(map, combine_tag);
+ if (id > 0) {
+ return id;
+ }
+
+ HlAttrs battrs = get_colors_force(back_attr);
+ HlAttrs fattrs = get_colors_force(front_attr);
+ HlAttrs cattrs;
+ if (through) {
+ cattrs = battrs;
+ cattrs.rgb_fg_color = rgb_blend((int)p_pb, battrs.rgb_fg_color,
+ fattrs.rgb_bg_color);
+ if (cattrs.rgb_ae_attr & (HL_UNDERLINE|HL_UNDERCURL)) {
+ cattrs.rgb_sp_color = rgb_blend((int)p_pb, battrs.rgb_sp_color,
+ fattrs.rgb_bg_color);
+ } else {
+ cattrs.rgb_sp_color = -1;
+ }
+
+ cattrs.cterm_bg_color = fattrs.cterm_bg_color;
+ cattrs.cterm_fg_color = cterm_blend((int)p_pb, battrs.cterm_fg_color,
+ fattrs.cterm_bg_color);
+ } else {
+ cattrs = fattrs;
+ if (p_pb >= 50) {
+ cattrs.rgb_ae_attr |= battrs.rgb_ae_attr;
+ }
+ cattrs.rgb_fg_color = rgb_blend((int)p_pb/2, battrs.rgb_fg_color,
+ fattrs.rgb_fg_color);
+ if (cattrs.rgb_ae_attr & (HL_UNDERLINE|HL_UNDERCURL)) {
+ cattrs.rgb_sp_color = rgb_blend((int)p_pb/2, battrs.rgb_bg_color,
+ fattrs.rgb_sp_color);
+ } else {
+ cattrs.rgb_sp_color = -1;
+ }
+ }
+ cattrs.rgb_bg_color = rgb_blend((int)p_pb, battrs.rgb_bg_color,
+ fattrs.rgb_bg_color);
+
+ HlKind kind = through ? kHlBlendThrough : kHlBlend;
+ id = get_attr_entry((HlEntry){ .attr = cattrs, .kind = kind,
+ .id1 = back_attr, .id2 = front_attr });
+ if (id > 0) {
+ map_put(int, int)(map, combine_tag, id);
+ }
+ return id;
+}
+
+static int rgb_blend(int ratio, int rgb1, int rgb2)
+{
+ int a = ratio, b = 100-ratio;
+ int r1 = (rgb1 & 0xFF0000) >> 16;
+ int g1 = (rgb1 & 0x00FF00) >> 8;
+ int b1 = (rgb1 & 0x0000FF) >> 0;
+ int r2 = (rgb2 & 0xFF0000) >> 16;
+ int g2 = (rgb2 & 0x00FF00) >> 8;
+ int b2 = (rgb2 & 0x0000FF) >> 0;
+ int mr = (a * r1 + b * r2)/100;
+ int mg = (a * g1 + b * g2)/100;
+ int mb = (a * b1 + b * b2)/100;
+ return (mr << 16) + (mg << 8) + mb;
+}
+
+static int cterm_blend(int ratio, int c1, int c2)
+{
+ // 1. Convert cterm color numbers to RGB.
+ // 2. Blend the RGB colors.
+ // 3. Convert the RGB result to a cterm color.
+ int rgb1 = hl_cterm2rgb_color(c1);
+ int rgb2 = hl_cterm2rgb_color(c2);
+ int rgb_blended = rgb_blend(ratio, rgb1, rgb2);
+ return hl_rgb2cterm_color(rgb_blended);
+}
+
+/// Converts RGB color to 8-bit color (0-255).
+static int hl_rgb2cterm_color(int rgb)
+{
+ int r = (rgb & 0xFF0000) >> 16;
+ int g = (rgb & 0x00FF00) >> 8;
+ int b = (rgb & 0x0000FF) >> 0;
+
+ return (r * 6 / 256) * 36 + (g * 6 / 256) * 6 + (b * 6 / 256);
+}
+
+/// Converts 8-bit color (0-255) to RGB color.
+/// This is compatible with xterm.
+static int hl_cterm2rgb_color(int nr)
+{
+ static int cube_value[] = {
+ 0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF
+ };
+ static int grey_ramp[] = {
+ 0x08, 0x12, 0x1C, 0x26, 0x30, 0x3A, 0x44, 0x4E, 0x58, 0x62, 0x6C, 0x76,
+ 0x80, 0x8A, 0x94, 0x9E, 0xA8, 0xB2, 0xBC, 0xC6, 0xD0, 0xDA, 0xE4, 0xEE
+ };
+ static char_u ansi_table[16][4] = {
+ // R G B idx
+ { 0, 0, 0, 1 } , // black
+ { 224, 0, 0, 2 } , // dark red
+ { 0, 224, 0, 3 } , // dark green
+ { 224, 224, 0, 4 } , // dark yellow / brown
+ { 0, 0, 224, 5 } , // dark blue
+ { 224, 0, 224, 6 } , // dark magenta
+ { 0, 224, 224, 7 } , // dark cyan
+ { 224, 224, 224, 8 } , // light grey
+
+ { 128, 128, 128, 9 } , // dark grey
+ { 255, 64, 64, 10 } , // light red
+ { 64, 255, 64, 11 } , // light green
+ { 255, 255, 64, 12 } , // yellow
+ { 64, 64, 255, 13 } , // light blue
+ { 255, 64, 255, 14 } , // light magenta
+ { 64, 255, 255, 15 } , // light cyan
+ { 255, 255, 255, 16 } , // white
+ };
+
+ int r = 0;
+ int g = 0;
+ int b = 0;
+ int idx;
+ // *ansi_idx = 0;
+
+ if (nr < 16) {
+ r = ansi_table[nr][0];
+ g = ansi_table[nr][1];
+ b = ansi_table[nr][2];
+ // *ansi_idx = ansi_table[nr][3];
+ } else if (nr < 232) { // 216 color-cube
+ idx = nr - 16;
+ r = cube_value[idx / 36 % 6];
+ g = cube_value[idx / 6 % 6];
+ b = cube_value[idx % 6];
+ // *ansi_idx = -1;
+ } else if (nr < 256) { // 24 greyscale ramp
+ idx = nr - 232;
+ r = grey_ramp[idx];
+ g = grey_ramp[idx];
+ b = grey_ramp[idx];
+ // *ansi_idx = -1;
+ }
+ return (r << 16) + (g << 8) + b;
+}
+
/// Get highlight attributes for a attribute code
HlAttrs syn_attr2entry(int attr)
{
@@ -403,6 +601,8 @@ static void hl_inspect_impl(Array *arr, int attr)
break;
case kHlCombine:
+ case kHlBlend:
+ case kHlBlendThrough:
// attribute combination is associative, so flatten to an array
hl_inspect_impl(arr, e.id1);
hl_inspect_impl(arr, e.id2);