Merge pull request #14065 from bfredl/overlaymode

more virt_text display options
This commit is contained in:
Björn Linse
2021-03-10 13:28:07 +01:00
committed by GitHub
5 changed files with 286 additions and 94 deletions

View File

@@ -1430,6 +1430,18 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id,
/// - "eol": right after eol character (default)
/// - "overlay": display over the specified column, without
/// shifting the underlying text.
/// - virt_text_hide : hide the virtual text when the background
/// text is selected or hidden due to
/// horizontal scroll 'nowrap'
/// - hl_mode : control how highlights are combined with the
/// highlights of the text. Currently only affects
/// virt_text highlights, but might affect `hl_group`
/// in later versions.
/// - "replace": only show the virt_text color. This is the
/// default
/// - "combine": combine with background text color
/// - "blend": blend with background text color.
///
/// - ephemeral : for use with |nvim_set_decoration_provider|
/// callbacks. The mark will only be used for the current
/// redraw cycle, and not be permantently stored in the
@@ -1477,11 +1489,10 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
bool ephemeral = false;
uint64_t id = 0;
int line2 = -1, hl_id = 0;
DecorPriority priority = DECOR_PRIORITY_BASE;
int line2 = -1;
Decoration decor = DECORATION_INIT;
colnr_T col2 = -1;
VirtText virt_text = KV_INITIAL_VALUE;
VirtTextPos virt_text_pos = kVTEndOfLine;
bool right_gravity = true;
bool end_right_gravity = false;
bool end_gravity_set = false;
@@ -1528,12 +1539,12 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
switch (v->type) {
case kObjectTypeString:
hl_group = v->data.string;
hl_id = syn_check_group(
decor.hl_id = syn_check_group(
(char_u *)(hl_group.data),
(int)hl_group.size);
break;
case kObjectTypeInteger:
hl_id = (int)v->data.integer;
decor.hl_id = (int)v->data.integer;
break;
default:
api_set_error(err, kErrorTypeValidation,
@@ -1546,7 +1557,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
"virt_text is not an Array");
goto error;
}
virt_text = parse_virt_text(v->data.array, err);
decor.virt_text = parse_virt_text(v->data.array, err);
if (ERROR_SET(err)) {
goto error;
}
@@ -1558,9 +1569,33 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
}
String str = v->data.string;
if (strequal("eol", str.data)) {
virt_text_pos = kVTEndOfLine;
decor.virt_text_pos = kVTEndOfLine;
} else if (strequal("overlay", str.data)) {
virt_text_pos = kVTOverlay;
decor.virt_text_pos = kVTOverlay;
} else {
api_set_error(err, kErrorTypeValidation,
"virt_text_pos: invalid value");
goto error;
}
} else if (strequal("virt_text_hide", k.data)) {
decor.virt_text_hide = api_object_to_bool(*v,
"virt_text_hide", false, err);
if (ERROR_SET(err)) {
goto error;
}
} else if (strequal("hl_mode", k.data)) {
if (v->type != kObjectTypeString) {
api_set_error(err, kErrorTypeValidation,
"hl_mode is not a String");
goto error;
}
String str = v->data.string;
if (strequal("replace", str.data)) {
decor.hl_mode = kHlModeReplace;
} else if (strequal("combine", str.data)) {
decor.hl_mode = kHlModeCombine;
} else if (strequal("blend", str.data)) {
decor.hl_mode = kHlModeBlend;
} else {
api_set_error(err, kErrorTypeValidation,
"virt_text_pos: invalid value");
@@ -1583,7 +1618,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
"priority is not a valid value");
goto error;
}
priority = (DecorPriority)v->data.integer;
decor.priority = (DecorPriority)v->data.integer;
} else if (strequal("right_gravity", k.data)) {
if (v->type != kObjectTypeBoolean) {
api_set_error(err, kErrorTypeValidation,
@@ -1631,23 +1666,23 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
col2 = 0;
}
Decoration *decor = NULL, tmp = { 0 };
Decoration *d = NULL;
if (kv_size(virt_text) || priority != DECOR_PRIORITY_BASE) {
if (ephemeral) {
d = &decor;
} else if (kv_size(decor.virt_text)
|| decor.priority != DECOR_PRIORITY_BASE) {
// TODO(bfredl): this is a bit sketchy. eventually we should
// have predefined decorations for both marks/ephemerals
decor = ephemeral ? &tmp : xcalloc(1, sizeof(*decor));
decor->hl_id = hl_id;
decor->virt_text = virt_text;
decor->priority = priority;
decor->virt_text_pos = virt_text_pos;
} else if (hl_id) {
decor = decor_hl(hl_id);
d = xcalloc(1, sizeof(*d));
*d = decor;
} else if (decor.hl_id) {
d = decor_hl(decor.hl_id);
}
// TODO(bfredl): synergize these two branches even more
if (ephemeral && decor_state.buf == buf) {
decor_add_ephemeral((int)line, (int)col, line2, col2, decor, 0);
decor_add_ephemeral((int)line, (int)col, line2, col2, &decor, 0);
} else {
if (ephemeral) {
api_set_error(err, kErrorTypeException, "not yet implemented");
@@ -1655,14 +1690,14 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
}
id = extmark_set(buf, (uint64_t)ns_id, id, (int)line, (colnr_T)col,
line2, col2, decor, right_gravity,
line2, col2, d, right_gravity,
end_right_gravity, kExtmarkNoUndo);
}
return (Integer)id;
error:
clear_virttext(&virt_text);
clear_virttext(&decor.virt_text);
return 0;
}

View File

@@ -230,7 +230,7 @@ static void decor_add(DecorState *state, int start_row, int start_col,
HlRange range = { start_row, start_col, end_row, end_col,
attr_id, MAX(priority, decor->priority),
kv_size(decor->virt_text) ? &decor->virt_text : NULL,
decor->virt_text_pos,
decor->virt_text_pos, decor->virt_text_hide, decor->hl_mode,
kv_size(decor->virt_text) && owned, -1 };
kv_pushp(state->active);
@@ -245,7 +245,8 @@ static void decor_add(DecorState *state, int start_row, int start_col,
kv_A(state->active, index) = range;
}
int decor_redraw_col(buf_T *buf, int col, int virt_col, DecorState *state)
int decor_redraw_col(buf_T *buf, int col, int virt_col, bool hidden,
DecorState *state)
{
if (col <= state->col_until) {
return state->current;
@@ -324,7 +325,7 @@ next_mark:
}
if ((item.start_row == state->row && item.start_col <= col)
&& item.virt_text && item.virt_col == -1) {
item.virt_col = virt_col;
item.virt_col = (item.virt_text_hide && hidden) ? -2 : virt_col;
}
if (keep) {
kv_A(state->active, j++) = item;
@@ -345,7 +346,7 @@ void decor_redraw_end(DecorState *state)
VirtText *decor_redraw_virt_text(buf_T *buf, DecorState *state)
{
decor_redraw_col(buf, MAXCOL, MAXCOL, state);
decor_redraw_col(buf, MAXCOL, MAXCOL, false, state);
for (size_t i = 0; i < kv_size(state->active); i++) {
HlRange item = kv_A(state->active, i);
if (item.start_row == state->row && item.virt_text

View File

@@ -23,15 +23,26 @@ typedef enum {
kVTOverlay,
} VirtTextPos;
typedef enum {
kHlModeUnknown,
kHlModeReplace,
kHlModeCombine,
kHlModeBlend,
} HlMode;
struct Decoration
{
int hl_id; // highlight group
VirtText virt_text;
VirtTextPos virt_text_pos;
bool virt_text_hide;
HlMode hl_mode;
// TODO(bfredl): style, signs, etc
DecorPriority priority;
bool shared; // shared decoration, don't free
};
#define DECORATION_INIT { 0, KV_INITIAL_VALUE, kVTEndOfLine, false, \
kHlModeUnknown, DECOR_PRIORITY_BASE, false }
typedef struct {
int start_row;
@@ -39,9 +50,13 @@ typedef struct {
int end_row;
int end_col;
int attr_id;
// TODO(bfredl): embed decoration instead, perhaps using an arena
// for ephemerals?
DecorPriority priority;
VirtText *virt_text;
VirtTextPos virt_text_pos;
bool virt_text_hide;
HlMode hl_mode;
bool virt_text_owned;
int virt_col;
} HlRange;

View File

@@ -2096,6 +2096,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
char_u buf_fold[FOLD_TEXT_LEN + 1]; // Hold value returned by get_foldtext
bool area_active = false;
/* draw_state: items that are drawn in sequence: */
#define WL_START 0 /* nothing done yet */
# define WL_CMDLINE WL_START + 1 /* cmdline window column */
@@ -2850,6 +2852,12 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
if (draw_state == WL_LINE - 1 && n_extra == 0) {
sign_idx = 0;
draw_state = WL_LINE;
if (has_decor && row == startrow + filler_lines) {
// hide virt_text on text hidden by 'nowrap'
decor_redraw_col(wp->w_buffer, vcol, off, true, &decor_state);
}
if (saved_n_extra) {
/* Continue item from end of wrapped line. */
n_extra = saved_n_extra;
@@ -2934,10 +2942,14 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
&& vcol_prev < vcol // not at margin
&& vcol < tocol)) {
area_attr = attr; // start highlighting
if (area_highlighting) {
area_active = true;
}
} else if (area_attr != 0 && (vcol == tocol
|| (noinvcur
&& (colnr_T)vcol == wp->w_virtcol))) {
area_attr = 0; // stop highlighting
area_active = false;
}
if (!n_extra) {
@@ -3397,9 +3409,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
char_attr = hl_combine_attr(spell_attr, char_attr);
}
if (wp->w_buffer->terminal) {
char_attr = hl_combine_attr(term_attrs[vcol], char_attr);
}
if (has_decor && v > 0) {
bool selected = (area_active || (area_highlighting && noinvcur
&& (colnr_T)vcol == wp->w_virtcol));
int extmark_attr = decor_redraw_col(wp->w_buffer, (colnr_T)v-1, off,
&decor_state);
selected, &decor_state);
if (extmark_attr != 0) {
if (!attr_pri) {
char_attr = hl_combine_attr(char_attr, extmark_attr);
@@ -3409,10 +3427,6 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
}
}
if (wp->w_buffer->terminal) {
char_attr = hl_combine_attr(term_attrs[vcol], char_attr);
}
// Found last space before word: check for line break.
if (wp->w_p_lbr && c0 == c && vim_isbreak(c)
&& !vim_isbreak((int)(*ptr))) {
@@ -4355,10 +4369,22 @@ void draw_virt_text(buf_T *buf, int *end_col, int max_col)
virt_pos++;
continue;
}
int cells = line_putchar(&s, &linebuf_char[col], 2, false);
linebuf_attr[col++] = virt_attr;
int attr;
bool through = false;
if (item->hl_mode == kHlModeCombine) {
attr = hl_combine_attr(linebuf_attr[col], virt_attr);
} else if (item->hl_mode == kHlModeBlend) {
through = (*s.p == ' ');
attr = hl_blend_attrs(linebuf_attr[col], virt_attr, &through);
} else {
attr = virt_attr;
}
schar_T dummy[2];
int cells = line_putchar(&s, through ? dummy : &linebuf_char[col],
max_col-col, false);
linebuf_attr[col++] = attr;
if (cells > 1) {
linebuf_attr[col++] = virt_attr;
linebuf_attr[col++] = attr;
}
}
*end_col = MAX(*end_col, col);