diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/api/ui.c | 5 | ||||
-rw-r--r-- | src/nvim/highlight.c | 98 | ||||
-rw-r--r-- | src/nvim/highlight.h | 8 | ||||
-rw-r--r-- | src/nvim/highlight_defs.h | 2 | ||||
-rw-r--r-- | src/nvim/option.c | 10 | ||||
-rw-r--r-- | src/nvim/option_defs.h | 1 | ||||
-rw-r--r-- | src/nvim/options.lua | 8 | ||||
-rw-r--r-- | src/nvim/popupmnu.c | 18 | ||||
-rw-r--r-- | src/nvim/screen.c | 13 | ||||
-rw-r--r-- | src/nvim/ui_compositor.c | 42 |
10 files changed, 187 insertions, 18 deletions
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index a934f18dbf..91009c950f 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -357,10 +357,7 @@ static void remote_ui_default_colors_set(UI *ui, Integer rgb_fg, Integer cterm_fg, Integer cterm_bg) { if (!ui->ui_ext[kUITermColors]) { - bool dark = (*p_bg == 'd'); - rgb_fg = rgb_fg != -1 ? rgb_fg : (dark ? 0xFFFFFF : 0x000000); - rgb_bg = rgb_bg != -1 ? rgb_bg : (dark ? 0x000000 : 0xFFFFFF); - rgb_sp = rgb_sp != -1 ? rgb_sp : 0xFF0000; + HL_SET_DEFAULT_COLORS(rgb_fg, rgb_bg, rgb_sp); } Array args = ARRAY_DICT_INIT; ADD(args, INTEGER_OBJ(rgb_fg)); diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index 20cdbc7ec9..cbb28d9a09 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, @@ -213,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(); @@ -220,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) { @@ -283,6 +296,85 @@ 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); + return attrs; +} + +/// Blend overlay attributes (for popupmenu) with other attributes +/// +/// This creates a new group when required. +/// This will be called on a per-cell basis when in use, 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); + cattrs.cterm_bg_color = fattrs.cterm_bg_color; + cattrs.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); + } + 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; +} + /// Get highlight attributes for a attribute code HlAttrs syn_attr2entry(int attr) { @@ -406,6 +498,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); diff --git a/src/nvim/highlight.h b/src/nvim/highlight.h index 6be0d6200b..a237ddbc34 100644 --- a/src/nvim/highlight.h +++ b/src/nvim/highlight.h @@ -10,4 +10,12 @@ # include "highlight.h.generated.h" #endif +# define HL_SET_DEFAULT_COLORS(rgb_fg, rgb_bg, rgb_sp) \ + do { \ + bool dark_ = (*p_bg == 'd'); \ + rgb_fg = rgb_fg != -1 ? rgb_fg : (dark_ ? 0xFFFFFF : 0x000000); \ + rgb_bg = rgb_bg != -1 ? rgb_bg : (dark_ ? 0x000000 : 0xFFFFFF); \ + rgb_sp = rgb_sp != -1 ? rgb_sp : 0xFF0000; \ + } while (0); + #endif // NVIM_HIGHLIGHT_H diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h index 40025fcbbb..1da33bfea5 100644 --- a/src/nvim/highlight_defs.h +++ b/src/nvim/highlight_defs.h @@ -160,6 +160,8 @@ typedef enum { kHlSyntax, kHlTerminal, kHlCombine, + kHlBlend, + kHlBlendThrough, } HlKind; typedef struct { diff --git a/src/nvim/option.c b/src/nvim/option.c index 3914e020b1..5517768194 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -50,6 +50,7 @@ #include "nvim/fold.h" #include "nvim/getchar.h" #include "nvim/hardcopy.h" +#include "nvim/highlight.h" #include "nvim/indent_c.h" #include "nvim/mbyte.h" #include "nvim/memfile.h" @@ -65,6 +66,7 @@ #include "nvim/normal.h" #include "nvim/os_unix.h" #include "nvim/path.h" +#include "nvim/popupmnu.h" #include "nvim/regexp.h" #include "nvim/screen.h" #include "nvim/spell.h" @@ -4338,6 +4340,14 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, if (p_uc && !old_value) { ml_open_files(); } + } else if (pp == &p_pb) { + p_pb = MAX(MIN(p_pb, 100), 0); + if (old_value != 0) { + hl_invalidate_blends(); + } + if (pum_drawn()) { + pum_recompose(); + } } else if (pp == &p_pyx) { if (p_pyx != 0 && p_pyx != 2 && p_pyx != 3) { errmsg = e_invarg; diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index c80de4d46b..ccb0496495 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -375,6 +375,7 @@ EXTERN int p_confirm; // 'confirm' EXTERN int p_cp; // 'compatible' EXTERN char_u *p_cot; // 'completeopt' EXTERN long p_ph; // 'pumheight' +EXTERN long p_pb; // 'pumblend' EXTERN char_u *p_cpo; // 'cpoptions' EXTERN char_u *p_csprg; // 'cscopeprg' EXTERN int p_csre; // 'cscoperelative' diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 127c15dd5a..b8f128103c 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -1806,6 +1806,14 @@ return { defaults={if_true={vi=0}} }, { + full_name='pumblend', abbreviation='pb', + type='number', scope={'global'}, + vi_def=true, + redraw={'ui_option'}, + varname='p_pb', + defaults={if_true={vi=0}} + }, + { full_name='pyxversion', abbreviation='pyx', type='number', scope={'global'}, secure=true, diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index 9b09006b5f..3c10b7ae0f 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -44,6 +44,7 @@ static int pum_col; // left column of pum static bool pum_is_visible = false; static bool pum_is_drawn = false; static bool pum_external = false; +static bool pum_invalid = false; // the screen was just cleared static ScreenGrid pum_grid = SCREEN_GRID_INIT; @@ -360,12 +361,14 @@ void pum_redraw(void) grid_assign_handle(&pum_grid); bool moved = ui_comp_put_grid(&pum_grid, pum_row, pum_col-col_off, pum_height, grid_width); + bool invalid_grid = moved || pum_invalid; + pum_invalid = false; if (!pum_grid.chars || pum_grid.Rows != pum_height || pum_grid.Columns != grid_width) { - grid_alloc(&pum_grid, pum_height, grid_width, !moved, false); + grid_alloc(&pum_grid, pum_height, grid_width, !invalid_grid, false); ui_call_grid_resize(pum_grid.handle, pum_grid.Columns, pum_grid.Rows); - } else if (moved) { + } else if (invalid_grid) { grid_invalidate(&pum_grid); } @@ -806,6 +809,17 @@ bool pum_drawn(void) return pum_visible() && !pum_external; } +/// Screen was cleared, need to redraw next time +void pum_invalidate(void) +{ + pum_invalid = true; +} + +void pum_recompose(void) +{ + ui_comp_compose_grid(&pum_grid); +} + /// Gets the height of the menu. /// /// @return the height of the popup menu, the number of entries visible. diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 014f63f400..00d7c05be4 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -154,7 +154,7 @@ static bool highlights_invalid = false; static bool conceal_cursor_used = false; -static bool floats_invalid = false; +static bool redraw_popupmenu = false; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "screen.c.generated.h" @@ -466,13 +466,13 @@ void update_screen(int type) end_search_hl(); // May need to redraw the popup menu. - if (pum_drawn() && floats_invalid) { + if (pum_drawn() && redraw_popupmenu) { pum_redraw(); } send_grid_resize = false; highlights_invalid = false; - floats_invalid = false; + redraw_popupmenu = false; /* Reset b_mod_set flags. Going through all windows is probably faster * than going through all buffers (there could be many buffers). */ @@ -6185,7 +6185,6 @@ static void screenclear2(void) default_grid.line_wraps[i] = false; } - floats_invalid = true; ui_call_grid_clear(1); // clear the display clear_cmdline = false; mode_displayed = false; @@ -6193,6 +6192,8 @@ static void screenclear2(void) redraw_all_later(NOT_VALID); redraw_cmdline = true; redraw_tabline = true; + redraw_popupmenu = true; + pum_invalidate(); if (must_redraw == CLEAR) { must_redraw = NOT_VALID; // no need to clear again } @@ -7196,6 +7197,10 @@ void screen_resize(int width, int height) } else { update_topline(); if (pum_drawn()) { + // TODO(bfredl): ins_compl_show_pum wants to redraw the screen first. + // For now make sure the nested update_screen(0) won't redraw the + // pum at the old position. Try to untangle this later. + redraw_popupmenu = false; ins_compl_show_pum(); } update_screen(NOT_VALID); diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c index 9e0c44f3c2..59ae9c34ec 100644 --- a/src/nvim/ui_compositor.c +++ b/src/nvim/ui_compositor.c @@ -15,6 +15,7 @@ #include "nvim/ascii.h" #include "nvim/vim.h" #include "nvim/ui.h" +#include "nvim/highlight.h" #include "nvim/memory.h" #include "nvim/ui_compositor.h" #include "nvim/ugrid.h" @@ -169,11 +170,9 @@ void ui_comp_remove_grid(ScreenGrid *grid) (void)kv_pop(layers); grid->comp_index = 0; - if (ui_comp_should_draw()) { - // inefficent: only draw up to grid->comp_index - compose_area(grid->comp_row, grid->comp_row+grid->Rows, - grid->comp_col, grid->comp_col+grid->Columns); - } + // recompose the area under the grid + // inefficent when being overlapped: only draw up to grid->comp_index + ui_comp_compose_grid(grid); } bool ui_comp_set_grid(handle_T handle) @@ -229,6 +228,10 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, int col = (int)startcol; ScreenGrid *grid = NULL; + schar_T *bg_line = &default_grid.chars[default_grid.line_offset[row] + +(size_t)startcol]; + sattr_T *bg_attrs = &default_grid.attrs[default_grid.line_offset[row] + +(size_t)startcol]; while (col < endcol) { int until = 0; @@ -256,6 +259,16 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, memcpy(linebuf+(col-startcol), grid->chars+off, n * sizeof(*linebuf)); memcpy(attrbuf+(col-startcol), grid->attrs+off, n * sizeof(*attrbuf)); + if (grid != &default_grid && p_pb) { + for (int i = col-(int)startcol; i < until-startcol; i++) { + bool thru = strequal((char *)linebuf[i], " "); + attrbuf[i] = (sattr_T)hl_blend_attrs(bg_attrs[i], attrbuf[i], thru); + if (thru) { + memcpy(linebuf[i], bg_line[i], sizeof(linebuf[i])); + } + } + } + // Tricky: if overlap caused a doublewidth char to get cut-off, must // replace the visible half with a space. if (linebuf[col-startcol][0] == NUL) { @@ -272,6 +285,7 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, skip = 0; } } + col = until; } assert(endcol <= chk_width); @@ -293,11 +307,25 @@ static void compose_area(Integer startrow, Integer endrow, { endrow = MIN(endrow, default_grid.Rows); endcol = MIN(endcol, default_grid.Columns); + if (endcol <= startcol) { + return; + } for (int r = (int)startrow; r < endrow; r++) { compose_line(r, startcol, endcol, kLineFlagInvalid); } } +/// compose the area under the grid. +/// +/// This is needed when some option affecting composition is changed, +/// such as 'pumblend' for popupmenu grid. +void ui_comp_compose_grid(ScreenGrid *grid) +{ + if (ui_comp_should_draw()) { + compose_area(grid->comp_row, grid->comp_row+grid->Rows, + grid->comp_col, grid->comp_col+grid->Columns); + } +} static void ui_comp_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Integer endcol, @@ -316,8 +344,10 @@ static void ui_comp_raw_line(UI *ui, Integer grid, Integer row, if (curgrid != &default_grid) { flags = flags & ~kLineFlagWrap; } + assert(row < default_grid.Rows); assert(clearcol <= default_grid.Columns); - if (flags & kLineFlagInvalid || kv_size(layers) > curgrid->comp_index+1) { + if (flags & kLineFlagInvalid + || kv_size(layers) > (p_pb ? 1 : curgrid->comp_index+1)) { compose_line(row, startcol, clearcol, flags); } else { ui_composed_call_raw_line(1, row, startcol, endcol, clearcol, clearattr, |