aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/api/ui.c5
-rw-r--r--src/nvim/highlight.c98
-rw-r--r--src/nvim/highlight.h8
-rw-r--r--src/nvim/highlight_defs.h2
-rw-r--r--src/nvim/option.c10
-rw-r--r--src/nvim/option_defs.h1
-rw-r--r--src/nvim/options.lua8
-rw-r--r--src/nvim/popupmnu.c18
-rw-r--r--src/nvim/screen.c13
-rw-r--r--src/nvim/ui_compositor.c42
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,