decoration: Clean up duplicate Decoration attributes + bonus hl_eol flag

This commit is contained in:
Björn Linse 2021-04-13 21:38:01 +02:00
parent 3893027200
commit 7b488314d0
5 changed files with 88 additions and 47 deletions

View File

@ -1437,6 +1437,10 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id,
/// default /// default
/// - "combine": combine with background text color /// - "combine": combine with background text color
/// - "blend": blend with background text color. /// - "blend": blend with background text color.
/// - hl_eol : when true, for a multiline highlight covering the
/// EOL of a line, continue the highlight for the rest
/// of the screen line (just like for diff and
/// cursorline highlight).
/// ///
/// - ephemeral : for use with |nvim_set_decoration_provider| /// - ephemeral : for use with |nvim_set_decoration_provider|
/// callbacks. The mark will only be used for the current /// callbacks. The mark will only be used for the current
@ -1581,6 +1585,11 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
if (ERROR_SET(err)) { if (ERROR_SET(err)) {
goto error; goto error;
} }
} else if (strequal("hl_eol", k.data)) {
decor.hl_eol = api_object_to_bool(*v, "hl_eol", false, err);
if (ERROR_SET(err)) {
goto error;
}
} else if (strequal("hl_mode", k.data)) { } else if (strequal("hl_mode", k.data)) {
if (v->type != kObjectTypeString) { if (v->type != kObjectTypeString) {
api_set_error(err, kErrorTypeValidation, api_set_error(err, kErrorTypeValidation,
@ -1669,7 +1678,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
if (ephemeral) { if (ephemeral) {
d = &decor; d = &decor;
} else if (kv_size(decor.virt_text) } else if (kv_size(decor.virt_text)
|| decor.priority != DECOR_PRIORITY_BASE) { || decor.priority != DECOR_PRIORITY_BASE
|| decor.hl_eol) {
// TODO(bfredl): this is a bit sketchy. eventually we should // TODO(bfredl): this is a bit sketchy. eventually we should
// have predefined decorations for both marks/ephemerals // have predefined decorations for both marks/ephemerals
d = xcalloc(1, sizeof(*d)); d = xcalloc(1, sizeof(*d));
@ -1680,7 +1690,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
// TODO(bfredl): synergize these two branches even more // TODO(bfredl): synergize these two branches even more
if (ephemeral && decor_state.buf == buf) { 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);
} else { } else {
if (ephemeral) { if (ephemeral) {
api_set_error(err, kErrorTypeException, "not yet implemented"); api_set_error(err, kErrorTypeException, "not yet implemented");

View File

@ -144,9 +144,9 @@ bool decor_redraw_reset(buf_T *buf, DecorState *state)
state->row = -1; state->row = -1;
state->buf = buf; state->buf = buf;
for (size_t i = 0; i < kv_size(state->active); i++) { for (size_t i = 0; i < kv_size(state->active); i++) {
HlRange item = kv_A(state->active, i); DecorRange item = kv_A(state->active, i);
if (item.virt_text_owned) { if (item.virt_text_owned) {
clear_virttext(&item.virt_text); clear_virttext(&item.decor.virt_text);
} }
} }
kv_size(state->active) = 0; kv_size(state->active) = 0;
@ -190,14 +190,14 @@ bool decor_redraw_start(buf_T *buf, int top_row, DecorState *state)
if (mark.id&MARKTREE_END_FLAG) { if (mark.id&MARKTREE_END_FLAG) {
decor_add(state, altpos.row, altpos.col, mark.row, mark.col, decor_add(state, altpos.row, altpos.col, mark.row, mark.col,
decor, false, 0); decor, false);
} else { } else {
if (altpos.row == -1) { if (altpos.row == -1) {
altpos.row = mark.row; altpos.row = mark.row;
altpos.col = mark.col; altpos.col = mark.col;
} }
decor_add(state, mark.row, mark.col, altpos.row, altpos.col, decor_add(state, mark.row, mark.col, altpos.row, altpos.col,
decor, false, 0); decor, false);
} }
next_mark: next_mark:
@ -222,22 +222,19 @@ bool decor_redraw_line(buf_T *buf, int row, DecorState *state)
} }
static void decor_add(DecorState *state, int start_row, int start_col, static void decor_add(DecorState *state, int start_row, int start_col,
int end_row, int end_col, Decoration *decor, bool owned, int end_row, int end_col, Decoration *decor, bool owned)
DecorPriority priority)
{ {
int attr_id = decor->hl_id > 0 ? syn_id2attr(decor->hl_id) : 0; int attr_id = decor->hl_id > 0 ? syn_id2attr(decor->hl_id) : 0;
HlRange range = { start_row, start_col, end_row, end_col, DecorRange range = { start_row, start_col, end_row, end_col,
attr_id, MAX(priority, decor->priority), *decor, attr_id,
decor->virt_text,
decor->virt_text_pos, decor->virt_text_hide, decor->hl_mode,
kv_size(decor->virt_text) && owned, -1 }; kv_size(decor->virt_text) && owned, -1 };
kv_pushp(state->active); kv_pushp(state->active);
size_t index; size_t index;
for (index = kv_size(state->active)-1; index > 0; index--) { for (index = kv_size(state->active)-1; index > 0; index--) {
HlRange item = kv_A(state->active, index-1); DecorRange item = kv_A(state->active, index-1);
if (item.priority <= range.priority) { if (item.decor.priority <= range.decor.priority) {
break; break;
} }
kv_A(state->active, index) = kv_A(state->active, index-1); kv_A(state->active, index) = kv_A(state->active, index-1);
@ -291,7 +288,7 @@ int decor_redraw_col(buf_T *buf, int col, int virt_col, bool hidden,
} }
decor_add(state, mark.row, mark.col, endpos.row, endpos.col, decor_add(state, mark.row, mark.col, endpos.row, endpos.col,
decor, false, 0); decor, false);
next_mark: next_mark:
marktree_itr_next(buf->b_marktree, state->itr); marktree_itr_next(buf->b_marktree, state->itr);
@ -300,11 +297,11 @@ next_mark:
int attr = 0; int attr = 0;
size_t j = 0; size_t j = 0;
for (size_t i = 0; i < kv_size(state->active); i++) { for (size_t i = 0; i < kv_size(state->active); i++) {
HlRange item = kv_A(state->active, i); DecorRange item = kv_A(state->active, i);
bool active = false, keep = true; bool active = false, keep = true;
if (item.end_row < state->row if (item.end_row < state->row
|| (item.end_row == state->row && item.end_col <= col)) { || (item.end_row == state->row && item.end_col <= col)) {
if (!(item.start_row >= state->row && kv_size(item.virt_text))) { if (!(item.start_row >= state->row && kv_size(item.decor.virt_text))) {
keep = false; keep = false;
} }
} else { } else {
@ -324,13 +321,13 @@ next_mark:
attr = hl_combine_attr(attr, item.attr_id); attr = hl_combine_attr(attr, item.attr_id);
} }
if ((item.start_row == state->row && item.start_col <= col) if ((item.start_row == state->row && item.start_col <= col)
&& kv_size(item.virt_text) && item.virt_col == -1) { && kv_size(item.decor.virt_text) && item.virt_col == -1) {
item.virt_col = (item.virt_text_hide && hidden) ? -2 : virt_col; item.virt_col = (item.decor.virt_text_hide && hidden) ? -2 : virt_col;
} }
if (keep) { if (keep) {
kv_A(state->active, j++) = item; kv_A(state->active, j++) = item;
} else if (item.virt_text_owned) { } else if (item.virt_text_owned) {
clear_virttext(&item.virt_text); clear_virttext(&item.decor.virt_text);
} }
} }
kv_size(state->active) = j; kv_size(state->active) = j;
@ -343,28 +340,34 @@ void decor_redraw_end(DecorState *state)
state->buf = NULL; state->buf = NULL;
} }
VirtText decor_redraw_virt_text(buf_T *buf, DecorState *state) VirtText decor_redraw_eol(buf_T *buf, DecorState *state, int *eol_attr)
{ {
decor_redraw_col(buf, MAXCOL, MAXCOL, false, state); decor_redraw_col(buf, MAXCOL, MAXCOL, false, state);
VirtText text = VIRTTEXT_EMPTY;
for (size_t i = 0; i < kv_size(state->active); i++) { for (size_t i = 0; i < kv_size(state->active); i++) {
HlRange item = kv_A(state->active, i); DecorRange item = kv_A(state->active, i);
if (item.start_row == state->row && kv_size(item.virt_text) if (!kv_size(text)
&& item.virt_text_pos == kVTEndOfLine) { && item.start_row == state->row && kv_size(item.decor.virt_text)
return item.virt_text; && item.decor.virt_text_pos == kVTEndOfLine) {
text = item.decor.virt_text;
}
if (item.decor.hl_eol && item.start_row <= state->row) {
*eol_attr = hl_combine_attr(*eol_attr, item.attr_id);
} }
} }
return VIRTTEXT_EMPTY;
return text;
} }
void decor_add_ephemeral(int start_row, int start_col, int end_row, int end_col, void decor_add_ephemeral(int start_row, int start_col, int end_row, int end_col,
Decoration *decor, DecorPriority priority) Decoration *decor)
{ {
if (end_row == -1) { if (end_row == -1) {
end_row = start_row; end_row = start_row;
end_col = start_col; end_col = start_col;
} }
decor_add(&decor_state, start_row, start_col, end_row, end_col, decor, true, decor_add(&decor_state, start_row, start_col, end_row, end_col, decor, true);
priority);
} }

View File

@ -37,33 +37,28 @@ struct Decoration
VirtTextPos virt_text_pos; VirtTextPos virt_text_pos;
bool virt_text_hide; bool virt_text_hide;
HlMode hl_mode; HlMode hl_mode;
bool hl_eol;
// TODO(bfredl): style, signs, etc // TODO(bfredl): style, signs, etc
DecorPriority priority; DecorPriority priority;
bool shared; // shared decoration, don't free bool shared; // shared decoration, don't free
}; };
#define DECORATION_INIT { 0, KV_INITIAL_VALUE, kVTEndOfLine, false, \ #define DECORATION_INIT { 0, KV_INITIAL_VALUE, kVTEndOfLine, false, \
kHlModeUnknown, DECOR_PRIORITY_BASE, false } kHlModeUnknown, false, DECOR_PRIORITY_BASE, false }
typedef struct { typedef struct {
int start_row; int start_row;
int start_col; int start_col;
int end_row; int end_row;
int end_col; int end_col;
int attr_id; Decoration decor;
// TODO(bfredl): embed decoration instead, perhaps using an arena int attr_id; // cached lookup of decor.hl_id
// for ephemerals?
DecorPriority priority;
VirtText virt_text;
VirtTextPos virt_text_pos;
bool virt_text_hide;
HlMode hl_mode;
bool virt_text_owned; bool virt_text_owned;
int virt_col; int virt_col;
} HlRange; } DecorRange;
typedef struct { typedef struct {
MarkTreeIter itr[1]; MarkTreeIter itr[1];
kvec_t(HlRange) active; kvec_t(DecorRange) active;
buf_T *buf; buf_T *buf;
int top_row; int top_row;
int row; int row;

View File

@ -3951,7 +3951,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
.hl_id = hl_err })); .hl_id = hl_err }));
do_virttext = true; do_virttext = true;
} else if (has_decor) { } else if (has_decor) {
virt_text = decor_redraw_virt_text(wp->w_buffer, &decor_state); virt_text = decor_redraw_eol(wp->w_buffer, &decor_state, &line_attr);
if (kv_size(virt_text)) { if (kv_size(virt_text)) {
do_virttext = true; do_virttext = true;
} }
@ -4381,11 +4381,12 @@ void draw_virt_text(buf_T *buf, int *end_col, int max_col)
{ {
DecorState *state = &decor_state; DecorState *state = &decor_state;
for (size_t i = 0; i < kv_size(state->active); i++) { for (size_t i = 0; i < kv_size(state->active); i++) {
HlRange *item = &kv_A(state->active, i); DecorRange *item = &kv_A(state->active, i);
if (item->start_row == state->row && kv_size(item->virt_text) if (item->start_row == state->row && kv_size(item->decor.virt_text)
&& item->virt_text_pos == kVTOverlay && item->decor.virt_text_pos == kVTOverlay
&& item->virt_col >= 0) { && item->virt_col >= 0) {
VirtText vt = item->virt_text; VirtText vt = item->decor.virt_text;
HlMode hl_mode = item->decor.hl_mode;
LineState s = LINE_STATE(""); LineState s = LINE_STATE("");
int virt_attr = 0; int virt_attr = 0;
int col = item->virt_col; int col = item->virt_col;
@ -4405,9 +4406,9 @@ void draw_virt_text(buf_T *buf, int *end_col, int max_col)
} }
int attr; int attr;
bool through = false; bool through = false;
if (item->hl_mode == kHlModeCombine) { if (hl_mode == kHlModeCombine) {
attr = hl_combine_attr(linebuf_attr[col], virt_attr); attr = hl_combine_attr(linebuf_attr[col], virt_attr);
} else if (item->hl_mode == kHlModeBlend) { } else if (hl_mode == kHlModeBlend) {
through = (*s.p == ' '); through = (*s.p == ' ');
attr = hl_blend_attrs(linebuf_attr[col], virt_attr, &through); attr = hl_blend_attrs(linebuf_attr[col], virt_attr, &through);
} else { } else {

View File

@ -29,6 +29,7 @@ describe('decorations providers', function()
[10] = {italic = true, background = Screen.colors.Magenta}; [10] = {italic = true, background = Screen.colors.Magenta};
[11] = {foreground = Screen.colors.Red, background = tonumber('0x005028')}; [11] = {foreground = Screen.colors.Red, background = tonumber('0x005028')};
[12] = {foreground = tonumber('0x990000')}; [12] = {foreground = tonumber('0x990000')};
[13] = {background = Screen.colors.LightBlue};
} }
end) end)
@ -331,6 +332,37 @@ describe('decorations providers', function()
| |
]]} ]]}
end) end)
it('can highlight beyond EOL', function()
insert(mulholland)
setup_provider [[
local test_ns = a.nvim_create_namespace "veberod"
function on_do(event, ...)
if event == "line" then
local win, buf, line = ...
if string.find(a.nvim_buf_get_lines(buf, line, line+1, true)[1], "buf") then
a.nvim_buf_set_extmark(buf, test_ns, line, 0, {
end_line = line+1;
hl_group = 'DiffAdd';
hl_eol = true;
ephemeral = true;
})
end
end
end
]]
screen:expect{grid=[[
// just to see if there was an accident |
// on Mulholland Drive |
try_start(); |
{13:bufref_T save_buf; }|
{13:switch_buffer(&save_buf, buf); }|
posp = getmark(mark, false); |
{13:restore_buffer(&save_buf);^ }|
|
]]}
end)
end) end)
describe('extmark decorations', function() describe('extmark decorations', function()