diff options
Diffstat (limited to 'src/nvim/highlight.c')
-rw-r--r-- | src/nvim/highlight.c | 204 |
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); |