mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
refactor(decorations): break up Decoration struct into smaller pieces
Remove the monolithic Decoration struct. Before this change, each extmark could either represent just a hl_id + priority value as a inline decoration, or it would take a pointer to this monolitic 112 byte struct which has to be allocated. This change separates the decorations into two pieces: DecorSignHighlight for signs, highlights and simple set-flag decorations (like spell, ui-watched), and DecorVirtText for virtual text and lines. The main separation here is whether they are expected to allocate more memory. Currently this is not really true as sign text has to be an allocated string, but the plan is to get rid of this eventually (it can just be an array of two schar_T:s). Further refactors are expected to improve the representation of each decoration kind individually. The goal of this particular PR is to get things started by cutting the Gordian knot which was the monolithic struct Decoration. Now, each extmark can either contain chained indicies/pointers to these kinds of objects, or it can fit a subset of DecorSignHighlight inline. The point of this change is not only to make decorations smaller in memory. In fact, the main motivation is to later allow them to grow _larger_, but on a dynamic, on demand fashion. As a simple example, it would be possible to augment highlights to take a list of multiple `hl_group`:s, which then would trivially map to a chain of multiple DecorSignHighlight entries. One small feature improvement included with this refactor itself, is that the restriction that extmarks cannot be removed inside a decoration provider has been lifted. These are instead safely lifetime extended on a "to free" list until the current iteration of screen drawing is done. NB: flags is a mess. but DecorLevel is useless, this slightly less so
This commit is contained in:
parent
8c6b0a5f21
commit
0b38fe4dbb
@ -154,21 +154,25 @@ Integer nvim_buf_set_virtual_text(Buffer buffer, Integer src_id, Integer line, A
|
||||
return 0;
|
||||
}
|
||||
|
||||
Decoration *existing = decor_find_virttext(buf, (int)line, ns_id);
|
||||
DecorVirtText *existing = decor_find_virttext(buf, (int)line, ns_id);
|
||||
|
||||
if (existing) {
|
||||
clear_virttext(&existing->virt_text);
|
||||
existing->virt_text = virt_text;
|
||||
existing->virt_text_width = width;
|
||||
clear_virttext(&existing->data.virt_text);
|
||||
existing->data.virt_text = virt_text;
|
||||
existing->width = width;
|
||||
return src_id;
|
||||
}
|
||||
|
||||
Decoration decor = DECORATION_INIT;
|
||||
decor.virt_text = virt_text;
|
||||
decor.virt_text_width = width;
|
||||
decor.priority = 0;
|
||||
DecorVirtText *vt = xmalloc(sizeof *vt);
|
||||
*vt = (DecorVirtText)DECOR_VIRT_TEXT_INIT;
|
||||
vt->data.virt_text = virt_text;
|
||||
vt->width = width;
|
||||
vt->priority = 0;
|
||||
|
||||
extmark_set(buf, ns_id, NULL, (int)line, 0, -1, -1, &decor, true, false, false, false, NULL);
|
||||
DecorInline decor = { .ext = true, .data.ext.vt = vt, .data.ext.sh_idx = DECOR_ID_INVALID };
|
||||
|
||||
extmark_set(buf, ns_id, NULL, (int)line, 0, -1, -1, decor, 0, true,
|
||||
false, false, false, NULL);
|
||||
return src_id;
|
||||
}
|
||||
|
||||
|
@ -103,15 +103,6 @@ bool ns_initialized(uint32_t ns)
|
||||
return ns < (uint32_t)next_namespace_id;
|
||||
}
|
||||
|
||||
static Object hl_group_name(int hl_id, bool hl_name)
|
||||
{
|
||||
if (hl_name) {
|
||||
return CSTR_TO_OBJ(syn_id2name(hl_id));
|
||||
} else {
|
||||
return INTEGER_OBJ(hl_id);
|
||||
}
|
||||
}
|
||||
|
||||
Array virt_text_to_array(VirtText vt, bool hl_name)
|
||||
{
|
||||
Array chunks = ARRAY_DICT_INIT;
|
||||
@ -176,85 +167,9 @@ static Array extmark_to_array(MTPair extmark, bool id, bool add_dict, bool hl_na
|
||||
PUT(dict, "invalid", BOOLEAN_OBJ(true));
|
||||
}
|
||||
|
||||
// pretend this is a pointer for a short while, Decoration will be factored away very soon
|
||||
const Decoration decor[1] = { get_decor(start) };
|
||||
if (decor->hl_id) {
|
||||
PUT(dict, "hl_group", hl_group_name(decor->hl_id, hl_name));
|
||||
PUT(dict, "hl_eol", BOOLEAN_OBJ(decor->hl_eol));
|
||||
}
|
||||
if (decor->hl_mode) {
|
||||
PUT(dict, "hl_mode", CSTR_TO_OBJ(hl_mode_str[decor->hl_mode]));
|
||||
}
|
||||
decor_to_dict_legacy(&dict, mt_decor(start), hl_name);
|
||||
|
||||
if (kv_size(decor->virt_text)) {
|
||||
Array chunks = virt_text_to_array(decor->virt_text, hl_name);
|
||||
PUT(dict, "virt_text", ARRAY_OBJ(chunks));
|
||||
PUT(dict, "virt_text_hide", BOOLEAN_OBJ(decor->virt_text_hide));
|
||||
if (decor->virt_text_pos == kVTWinCol) {
|
||||
PUT(dict, "virt_text_win_col", INTEGER_OBJ(decor->col));
|
||||
}
|
||||
PUT(dict, "virt_text_pos",
|
||||
CSTR_TO_OBJ(virt_text_pos_str[decor->virt_text_pos]));
|
||||
}
|
||||
|
||||
if (decor->ui_watched) {
|
||||
PUT(dict, "ui_watched", BOOLEAN_OBJ(true));
|
||||
}
|
||||
|
||||
if (kv_size(decor->virt_lines)) {
|
||||
Array all_chunks = ARRAY_DICT_INIT;
|
||||
bool virt_lines_leftcol = false;
|
||||
for (size_t i = 0; i < kv_size(decor->virt_lines); i++) {
|
||||
virt_lines_leftcol = kv_A(decor->virt_lines, i).left_col;
|
||||
Array chunks = virt_text_to_array(kv_A(decor->virt_lines, i).line, hl_name);
|
||||
ADD(all_chunks, ARRAY_OBJ(chunks));
|
||||
}
|
||||
PUT(dict, "virt_lines", ARRAY_OBJ(all_chunks));
|
||||
PUT(dict, "virt_lines_above", BOOLEAN_OBJ(decor->virt_lines_above));
|
||||
PUT(dict, "virt_lines_leftcol", BOOLEAN_OBJ(virt_lines_leftcol));
|
||||
}
|
||||
|
||||
if (decor->sign_text) {
|
||||
PUT(dict, "sign_text", CSTR_TO_OBJ(decor->sign_text));
|
||||
}
|
||||
|
||||
// uncrustify:off
|
||||
|
||||
struct { char *name; const int val; } hls[] = {
|
||||
{ "sign_hl_group" , decor->sign_hl_id },
|
||||
{ "number_hl_group" , decor->number_hl_id },
|
||||
{ "line_hl_group" , decor->line_hl_id },
|
||||
{ "cursorline_hl_group", decor->cursorline_hl_id },
|
||||
{ NULL, 0 },
|
||||
};
|
||||
|
||||
// uncrustify:on
|
||||
|
||||
for (int j = 0; hls[j].name; j++) {
|
||||
if (hls[j].val) {
|
||||
PUT(dict, hls[j].name, hl_group_name(hls[j].val, hl_name));
|
||||
}
|
||||
}
|
||||
|
||||
if (decor->sign_text
|
||||
|| decor->hl_id
|
||||
|| kv_size(decor->virt_text)
|
||||
|| decor->ui_watched) {
|
||||
PUT(dict, "priority", INTEGER_OBJ(decor->priority));
|
||||
}
|
||||
|
||||
if (decor->conceal) {
|
||||
String name = cstr_to_string((char *)&decor->conceal_char);
|
||||
PUT(dict, "conceal", STRING_OBJ(name));
|
||||
}
|
||||
|
||||
if (decor->spell != kNone) {
|
||||
PUT(dict, "spell", BOOLEAN_OBJ(decor->spell == kTrue));
|
||||
}
|
||||
|
||||
if (dict.size) {
|
||||
ADD(rv, DICTIONARY_OBJ(dict));
|
||||
}
|
||||
ADD(rv, DICTIONARY_OBJ(dict));
|
||||
}
|
||||
|
||||
return rv;
|
||||
@ -581,8 +496,14 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
||||
Dict(set_extmark) *opts, Error *err)
|
||||
FUNC_API_SINCE(7)
|
||||
{
|
||||
Decoration decor = DECORATION_INIT;
|
||||
bool has_decor = false;
|
||||
DecorHighlightInline hl = DECOR_HIGHLIGHT_INLINE_INIT;
|
||||
// TODO(bfredl): in principle signs with max one (1) hl group and max 4 bytes of text.
|
||||
// should be a candidate for inlining as well.
|
||||
DecorSignHighlight sign = DECOR_SIGN_HIGHLIGHT_INIT;
|
||||
DecorVirtText virt_text = DECOR_VIRT_TEXT_INIT;
|
||||
DecorVirtText virt_lines = DECOR_VIRT_LINES_INIT;
|
||||
bool has_hl = false;
|
||||
String conceal_char_large = STRING_INIT;
|
||||
|
||||
buf_T *buf = find_buffer_by_handle(buffer, err);
|
||||
if (!buf) {
|
||||
@ -643,11 +564,11 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
||||
Object *opt;
|
||||
int *dest;
|
||||
} hls[] = {
|
||||
{ "hl_group" , &opts->hl_group , &decor.hl_id },
|
||||
{ "sign_hl_group" , &opts->sign_hl_group , &decor.sign_hl_id },
|
||||
{ "number_hl_group" , &opts->number_hl_group , &decor.number_hl_id },
|
||||
{ "line_hl_group" , &opts->line_hl_group , &decor.line_hl_id },
|
||||
{ "cursorline_hl_group", &opts->cursorline_hl_group, &decor.cursorline_hl_id },
|
||||
{ "hl_group" , &opts->hl_group , &hl.hl_id },
|
||||
{ "sign_hl_group" , &opts->sign_hl_group , &sign.hl_id },
|
||||
{ "number_hl_group" , &opts->number_hl_group , &sign.number_hl_id },
|
||||
{ "line_hl_group" , &opts->line_hl_group , &sign.line_hl_id },
|
||||
{ "cursorline_hl_group", &opts->cursorline_hl_group, &sign.cursorline_hl_id },
|
||||
{ NULL, NULL, NULL },
|
||||
};
|
||||
|
||||
@ -655,26 +576,33 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
||||
|
||||
for (int j = 0; hls[j].name && hls[j].dest; j++) {
|
||||
if (hls[j].opt->type != kObjectTypeNil) {
|
||||
if (j > 0) {
|
||||
sign.flags |= kSHIsSign;
|
||||
} else {
|
||||
has_hl = true;
|
||||
}
|
||||
*hls[j].dest = object_to_hl_id(*hls[j].opt, hls[j].name, err);
|
||||
if (ERROR_SET(err)) {
|
||||
goto error;
|
||||
}
|
||||
has_decor = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (HAS_KEY(opts, set_extmark, conceal)) {
|
||||
hl.flags |= kSHConceal;
|
||||
has_hl = true;
|
||||
String c = opts->conceal;
|
||||
decor.conceal = true;
|
||||
if (c.size) {
|
||||
decor.conceal_char = utf_ptr2char(c.data);
|
||||
if (c.size > 0) {
|
||||
if (c.size <= 4) {
|
||||
memcpy(hl.conceal_char, c.data, c.size + (c.size < 4 ? 1 : 0));
|
||||
} else {
|
||||
conceal_char_large = c;
|
||||
}
|
||||
}
|
||||
has_decor = true;
|
||||
}
|
||||
|
||||
if (HAS_KEY(opts, set_extmark, virt_text)) {
|
||||
decor.virt_text = parse_virt_text(opts->virt_text, err, &decor.virt_text_width);
|
||||
has_decor = true;
|
||||
virt_text.data.virt_text = parse_virt_text(opts->virt_text, err, &virt_text.width);
|
||||
if (ERROR_SET(err)) {
|
||||
goto error;
|
||||
}
|
||||
@ -683,13 +611,13 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
||||
if (HAS_KEY(opts, set_extmark, virt_text_pos)) {
|
||||
String str = opts->virt_text_pos;
|
||||
if (strequal("eol", str.data)) {
|
||||
decor.virt_text_pos = kVTEndOfLine;
|
||||
virt_text.pos = kVPosEndOfLine;
|
||||
} else if (strequal("overlay", str.data)) {
|
||||
decor.virt_text_pos = kVTOverlay;
|
||||
virt_text.pos = kVPosOverlay;
|
||||
} else if (strequal("right_align", str.data)) {
|
||||
decor.virt_text_pos = kVTRightAlign;
|
||||
virt_text.pos = kVPosRightAlign;
|
||||
} else if (strequal("inline", str.data)) {
|
||||
decor.virt_text_pos = kVTInline;
|
||||
virt_text.pos = kVPosInline;
|
||||
} else {
|
||||
VALIDATE_S(false, "virt_text_pos", str.data, {
|
||||
goto error;
|
||||
@ -698,26 +626,26 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
||||
}
|
||||
|
||||
if (HAS_KEY(opts, set_extmark, virt_text_win_col)) {
|
||||
decor.col = (int)opts->virt_text_win_col;
|
||||
decor.virt_text_pos = kVTWinCol;
|
||||
virt_text.col = (int)opts->virt_text_win_col;
|
||||
virt_text.pos = kVPosWinCol;
|
||||
}
|
||||
|
||||
decor.hl_eol = opts->hl_eol;
|
||||
decor.virt_text_hide = opts->virt_text_hide;
|
||||
hl.flags |= opts->hl_eol ? kSHHlEol : 0;
|
||||
virt_text.flags |= opts->virt_text_hide ? kVTHide : 0;
|
||||
|
||||
if (HAS_KEY(opts, set_extmark, hl_mode)) {
|
||||
String str = opts->hl_mode;
|
||||
if (strequal("replace", str.data)) {
|
||||
decor.hl_mode = kHlModeReplace;
|
||||
virt_text.hl_mode = kHlModeReplace;
|
||||
} else if (strequal("combine", str.data)) {
|
||||
decor.hl_mode = kHlModeCombine;
|
||||
virt_text.hl_mode = kHlModeCombine;
|
||||
} else if (strequal("blend", str.data)) {
|
||||
if (decor.virt_text_pos == kVTInline) {
|
||||
if (virt_text.pos == kVPosInline) {
|
||||
VALIDATE(false, "%s", "cannot use 'blend' hl_mode with inline virtual text", {
|
||||
goto error;
|
||||
});
|
||||
}
|
||||
decor.hl_mode = kHlModeBlend;
|
||||
virt_text.hl_mode = kHlModeBlend;
|
||||
} else {
|
||||
VALIDATE_S(false, "hl_mode", str.data, {
|
||||
goto error;
|
||||
@ -735,29 +663,32 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
||||
});
|
||||
int dummig;
|
||||
VirtText jtem = parse_virt_text(a.items[j].data.array, err, &dummig);
|
||||
kv_push(decor.virt_lines, ((struct virt_line){ jtem, virt_lines_leftcol }));
|
||||
kv_push(virt_lines.data.virt_lines, ((struct virt_line){ jtem, virt_lines_leftcol }));
|
||||
if (ERROR_SET(err)) {
|
||||
goto error;
|
||||
}
|
||||
has_decor = true;
|
||||
}
|
||||
}
|
||||
|
||||
decor.virt_lines_above = opts->virt_lines_above;
|
||||
virt_lines.flags |= opts->virt_lines_above ? kVTLinesAbove : 0;
|
||||
|
||||
if (HAS_KEY(opts, set_extmark, priority)) {
|
||||
VALIDATE_RANGE((opts->priority >= 0 && opts->priority <= UINT16_MAX), "priority", {
|
||||
goto error;
|
||||
});
|
||||
decor.priority = (DecorPriority)opts->priority;
|
||||
hl.priority = (DecorPriority)opts->priority;
|
||||
sign.priority = (DecorPriority)opts->priority;
|
||||
virt_text.priority = (DecorPriority)opts->priority;
|
||||
virt_lines.priority = (DecorPriority)opts->priority;
|
||||
}
|
||||
|
||||
if (HAS_KEY(opts, set_extmark, sign_text)) {
|
||||
VALIDATE_S(init_sign_text(NULL, &decor.sign_text, opts->sign_text.data),
|
||||
sign.text.ptr = NULL;
|
||||
VALIDATE_S(init_sign_text(NULL, &sign.text.ptr, opts->sign_text.data),
|
||||
"sign_text", "", {
|
||||
goto error;
|
||||
});
|
||||
has_decor = true;
|
||||
sign.flags |= kSHIsSign;
|
||||
}
|
||||
|
||||
bool right_gravity = GET_BOOL_OR_TRUE(opts, set_extmark, right_gravity);
|
||||
@ -771,16 +702,18 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
||||
|
||||
size_t len = 0;
|
||||
|
||||
if (!HAS_KEY(opts, set_extmark, spell)) {
|
||||
decor.spell = kNone;
|
||||
} else {
|
||||
decor.spell = opts->spell ? kTrue : kFalse;
|
||||
has_decor = true;
|
||||
if (HAS_KEY(opts, set_extmark, spell)) {
|
||||
hl.flags |= (opts->spell) ? kSHSpellOn : kSHSpellOff;
|
||||
has_hl = true;
|
||||
}
|
||||
|
||||
decor.ui_watched = opts->ui_watched;
|
||||
if (decor.ui_watched) {
|
||||
has_decor = true;
|
||||
if (opts->ui_watched) {
|
||||
hl.flags |= kSHUIWatched;
|
||||
if (virt_text.pos == kVPosOverlay) {
|
||||
// TODO(bfredl): in a revised interface this should be the default.
|
||||
hl.flags |= kSHUIWatchedOverlay;
|
||||
}
|
||||
has_hl = true;
|
||||
}
|
||||
|
||||
VALIDATE_RANGE((line >= 0), "line", {
|
||||
@ -829,28 +762,90 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
||||
col2 = 0;
|
||||
}
|
||||
|
||||
// TODO(bfredl): synergize these two branches even more
|
||||
if (opts->ephemeral && decor_state.win && decor_state.win->w_buffer == buf) {
|
||||
decor_push_ephemeral((int)line, (int)col, line2, col2, &decor, (uint64_t)ns_id, id);
|
||||
int r = (int)line;
|
||||
int c = (int)col;
|
||||
if (line2 == -1) {
|
||||
line2 = r;
|
||||
col2 = c;
|
||||
}
|
||||
|
||||
if (kv_size(virt_text.data.virt_text)) {
|
||||
decor_range_add_virt(&decor_state, r, c, line2, col2, decor_put_vt(virt_text, NULL), true);
|
||||
}
|
||||
if (kv_size(virt_lines.data.virt_lines)) {
|
||||
decor_range_add_virt(&decor_state, r, c, line2, col2, decor_put_vt(virt_lines, NULL), true);
|
||||
}
|
||||
if (has_hl) {
|
||||
DecorSignHighlight sh = decor_sh_from_inline(hl, conceal_char_large);
|
||||
decor_range_add_sh(&decor_state, r, c, line2, col2, &sh, true, (uint32_t)ns_id, id);
|
||||
}
|
||||
if (sign.flags & kSHIsSign) {
|
||||
decor_range_add_sh(&decor_state, r, c, line2, col2, &sign, true, (uint32_t)ns_id, id);
|
||||
}
|
||||
} else {
|
||||
if (opts->ephemeral) {
|
||||
api_set_error(err, kErrorTypeException, "not yet implemented");
|
||||
goto error;
|
||||
}
|
||||
|
||||
uint16_t decor_flags = 0;
|
||||
|
||||
DecorVirtText *decor_alloc = NULL;
|
||||
if (kv_size(virt_text.data.virt_text)) {
|
||||
decor_alloc = decor_put_vt(virt_text, decor_alloc);
|
||||
if (virt_text.pos == kVPosInline) {
|
||||
decor_flags |= MT_FLAG_DECOR_VIRT_TEXT_INLINE;
|
||||
}
|
||||
}
|
||||
if (kv_size(virt_lines.data.virt_lines)) {
|
||||
decor_alloc = decor_put_vt(virt_lines, decor_alloc);
|
||||
decor_flags |= MT_FLAG_DECOR_VIRT_LINES;
|
||||
}
|
||||
|
||||
uint32_t decor_indexed = DECOR_ID_INVALID;
|
||||
if (sign.flags & kSHIsSign) {
|
||||
decor_indexed = decor_put_sh(sign);
|
||||
if (sign.text.ptr != NULL) {
|
||||
decor_flags |= MT_FLAG_DECOR_SIGNTEXT;
|
||||
}
|
||||
if (sign.number_hl_id || sign.line_hl_id || sign.cursorline_hl_id) {
|
||||
decor_flags |= MT_FLAG_DECOR_SIGNHL;
|
||||
}
|
||||
}
|
||||
|
||||
DecorInline decor = DECOR_INLINE_INIT;
|
||||
if (decor_alloc || decor_indexed != DECOR_ID_INVALID || conceal_char_large.size) {
|
||||
if (has_hl) {
|
||||
DecorSignHighlight sh = decor_sh_from_inline(hl, conceal_char_large);
|
||||
sh.next = decor_indexed;
|
||||
decor_indexed = decor_put_sh(sh);
|
||||
}
|
||||
decor.ext = true;
|
||||
decor.data.ext = (DecorExt){ .sh_idx = decor_indexed, .vt = decor_alloc };
|
||||
} else {
|
||||
decor.data.hl = hl;
|
||||
}
|
||||
|
||||
if (has_hl) {
|
||||
decor_flags |= MT_FLAG_DECOR_HL;
|
||||
}
|
||||
|
||||
extmark_set(buf, (uint32_t)ns_id, &id, (int)line, (colnr_T)col, line2, col2,
|
||||
has_decor ? &decor : NULL, right_gravity, opts->end_right_gravity,
|
||||
decor, decor_flags, right_gravity, opts->end_right_gravity,
|
||||
!GET_BOOL_OR_TRUE(opts, set_extmark, undo_restore),
|
||||
opts->invalidate, err);
|
||||
if (ERROR_SET(err)) {
|
||||
goto error;
|
||||
decor_free(decor);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return (Integer)id;
|
||||
|
||||
error:
|
||||
decor_clear(&decor);
|
||||
clear_virttext(&virt_text.data.virt_text);
|
||||
clear_virtlines(&virt_lines.data.virt_lines);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -873,11 +868,6 @@ Boolean nvim_buf_del_extmark(Buffer buffer, Integer ns_id, Integer id, Error *er
|
||||
return false;
|
||||
});
|
||||
|
||||
if (decor_state.running_on_lines) {
|
||||
api_set_error(err, kErrorTypeValidation, "Cannot remove extmarks during on_line callbacks");
|
||||
return false;
|
||||
}
|
||||
|
||||
return extmark_del_id(buf, (uint32_t)ns_id, (uint32_t)id);
|
||||
}
|
||||
|
||||
@ -962,13 +952,11 @@ Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, In
|
||||
end_line++;
|
||||
}
|
||||
|
||||
Decoration decor = DECORATION_INIT;
|
||||
decor.hl_id = hl_id;
|
||||
DecorInline decor = DECOR_INLINE_INIT;
|
||||
decor.data.hl.hl_id = hl_id;
|
||||
|
||||
extmark_set(buf, ns, NULL,
|
||||
(int)line, (colnr_T)col_start,
|
||||
end_line, (colnr_T)col_end,
|
||||
&decor, true, false, false, false, NULL);
|
||||
extmark_set(buf, ns, NULL, (int)line, (colnr_T)col_start, end_line, (colnr_T)col_end,
|
||||
decor, MT_FLAG_DECOR_HL, true, false, false, false, NULL);
|
||||
return ns_id;
|
||||
}
|
||||
|
||||
@ -997,11 +985,6 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start,
|
||||
return;
|
||||
});
|
||||
|
||||
if (decor_state.running_on_lines) {
|
||||
api_set_error(err, kErrorTypeValidation, "Cannot remove extmarks during on_line callbacks");
|
||||
return;
|
||||
}
|
||||
|
||||
if (line_end < 0 || line_end > MAXLNUM) {
|
||||
line_end = MAXLNUM;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -6,91 +6,54 @@
|
||||
|
||||
#include "klib/kvec.h"
|
||||
#include "nvim/buffer_defs.h"
|
||||
#include "nvim/decoration_defs.h"
|
||||
#include "nvim/extmark_defs.h"
|
||||
#include "nvim/macros.h"
|
||||
#include "nvim/marktree.h"
|
||||
#include "nvim/pos.h"
|
||||
#include "nvim/types.h"
|
||||
|
||||
// actual Decoration data is in extmark_defs.h
|
||||
|
||||
typedef uint16_t DecorPriority;
|
||||
#define DECOR_PRIORITY_BASE 0x1000
|
||||
|
||||
typedef enum {
|
||||
kVTEndOfLine,
|
||||
kVTOverlay,
|
||||
kVTWinCol,
|
||||
kVTRightAlign,
|
||||
kVTInline,
|
||||
} VirtTextPos;
|
||||
// actual Decor* data is in decoration_defs.h
|
||||
|
||||
EXTERN const char *const virt_text_pos_str[] INIT( = { "eol", "overlay", "win_col", "right_align",
|
||||
"inline" });
|
||||
|
||||
typedef enum {
|
||||
kHlModeUnknown,
|
||||
kHlModeReplace,
|
||||
kHlModeCombine,
|
||||
kHlModeBlend,
|
||||
} HlMode;
|
||||
|
||||
EXTERN const char *const hl_mode_str[] INIT( = { "", "replace", "combine", "blend" });
|
||||
|
||||
#define VIRTTEXT_EMPTY ((VirtText)KV_INITIAL_VALUE)
|
||||
|
||||
typedef kvec_t(struct virt_line { VirtText line; bool left_col; }) VirtLines;
|
||||
|
||||
struct Decoration {
|
||||
VirtText virt_text;
|
||||
VirtLines virt_lines;
|
||||
|
||||
int hl_id; // highlight group
|
||||
VirtTextPos virt_text_pos;
|
||||
HlMode hl_mode;
|
||||
|
||||
// TODO(bfredl): at some point turn this into FLAGS
|
||||
bool virt_text_hide;
|
||||
bool hl_eol;
|
||||
bool virt_lines_above;
|
||||
bool conceal;
|
||||
TriState spell;
|
||||
// TODO(bfredl): style, etc
|
||||
DecorPriority priority;
|
||||
int col; // fixed col value, like win_col
|
||||
int virt_text_width; // width of virt_text
|
||||
char *sign_text;
|
||||
char *sign_name;
|
||||
int sign_hl_id;
|
||||
int sign_add_id;
|
||||
int number_hl_id;
|
||||
int line_hl_id;
|
||||
int cursorline_hl_id;
|
||||
// TODO(bfredl): in principle this should be a schar_T, but we
|
||||
// probably want some kind of glyph cache for that..
|
||||
int conceal_char;
|
||||
bool ui_watched; // watched for win_extmark
|
||||
};
|
||||
#define DECORATION_INIT { KV_INITIAL_VALUE, KV_INITIAL_VALUE, 0, kVTEndOfLine, \
|
||||
kHlModeUnknown, false, false, false, false, kNone, \
|
||||
DECOR_PRIORITY_BASE, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, false }
|
||||
typedef enum {
|
||||
kDecorKindHighlight,
|
||||
kDecorKindSign,
|
||||
kDecorKindVirtText,
|
||||
kDecorKindVirtLines,
|
||||
kDecorKindUIWatched,
|
||||
} DecorRangeKind;
|
||||
|
||||
typedef struct {
|
||||
int start_row;
|
||||
int start_col;
|
||||
int end_row;
|
||||
int end_col;
|
||||
Decoration decor;
|
||||
int attr_id; // cached lookup of decor.hl_id
|
||||
bool virt_text_owned;
|
||||
// next pointers MUST NOT be used, these are separate ranges
|
||||
// vt->next could be pointing to freelist memory at this point
|
||||
union {
|
||||
DecorSignHighlight sh;
|
||||
DecorVirtText *vt;
|
||||
struct {
|
||||
uint32_t ns_id;
|
||||
uint32_t mark_id;
|
||||
VirtTextPos pos;
|
||||
} ui;
|
||||
} data;
|
||||
int attr_id; // cached lookup of inl.hl_id if it was a highlight
|
||||
bool owned; // ephemeral decoration, free memory immediately
|
||||
DecorPriority priority;
|
||||
DecorRangeKind kind;
|
||||
/// Screen column to draw the virtual text.
|
||||
/// When -1, the virtual text may be drawn after deciding where.
|
||||
/// When -3, the virtual text should be drawn on the next screen line.
|
||||
/// When -10, the virtual text has just been added.
|
||||
/// When INT_MIN, the virtual text should no longer be drawn.
|
||||
int draw_col;
|
||||
uint64_t ns_id;
|
||||
uint64_t mark_id;
|
||||
} DecorRange;
|
||||
|
||||
typedef struct {
|
||||
@ -109,23 +72,11 @@ typedef struct {
|
||||
|
||||
TriState spell;
|
||||
|
||||
// This is used to prevent removing/updating extmarks inside
|
||||
// on_lines callbacks which is not allowed since it can lead to
|
||||
// heap-use-after-free errors.
|
||||
bool running_on_lines;
|
||||
bool running_decor_provider;
|
||||
} DecorState;
|
||||
|
||||
EXTERN DecorState decor_state INIT( = { 0 });
|
||||
|
||||
static inline bool decor_has_sign(Decoration *decor)
|
||||
{
|
||||
return decor->sign_text
|
||||
|| decor->sign_hl_id
|
||||
|| decor->number_hl_id
|
||||
|| decor->line_hl_id
|
||||
|| decor->cursorline_hl_id;
|
||||
}
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "decoration.h.generated.h"
|
||||
#endif
|
||||
|
129
src/nvim/decoration_defs.h
Normal file
129
src/nvim/decoration_defs.h
Normal file
@ -0,0 +1,129 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "klib/kvec.h"
|
||||
|
||||
#define DECOR_ID_INVALID UINT32_MAX
|
||||
|
||||
typedef struct {
|
||||
char *text;
|
||||
int hl_id;
|
||||
} VirtTextChunk;
|
||||
|
||||
typedef kvec_t(VirtTextChunk) VirtText;
|
||||
#define VIRTTEXT_EMPTY ((VirtText)KV_INITIAL_VALUE)
|
||||
|
||||
typedef enum {
|
||||
kVPosEndOfLine,
|
||||
kVPosOverlay,
|
||||
kVPosWinCol,
|
||||
kVPosRightAlign,
|
||||
kVPosInline,
|
||||
} VirtTextPos;
|
||||
|
||||
typedef kvec_t(struct virt_line { VirtText line; bool left_col; }) VirtLines;
|
||||
|
||||
typedef uint16_t DecorPriority;
|
||||
#define DECOR_PRIORITY_BASE 0x1000
|
||||
|
||||
typedef enum {
|
||||
kHlModeUnknown,
|
||||
kHlModeReplace,
|
||||
kHlModeCombine,
|
||||
kHlModeBlend,
|
||||
} HlMode;
|
||||
|
||||
enum {
|
||||
kSHIsSign = 1,
|
||||
kSHHlEol = 2,
|
||||
kSHUIWatched = 4,
|
||||
kSHUIWatchedOverlay = 8,
|
||||
kSHSpellOn = 16,
|
||||
kSHSpellOff = 32,
|
||||
kSHConceal = 64,
|
||||
kSHConcealAlloc = 128,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint16_t flags;
|
||||
DecorPriority priority;
|
||||
int hl_id;
|
||||
char conceal_char[4];
|
||||
} DecorHighlightInline;
|
||||
|
||||
#define DECOR_HIGHLIGHT_INLINE_INIT { 0, DECOR_PRIORITY_BASE, 0, { 0 } }
|
||||
typedef struct {
|
||||
uint16_t flags;
|
||||
DecorPriority priority;
|
||||
int hl_id; // if sign: highlight of sign text
|
||||
// TODO(bfredl): Later this should be schar_T[2], but then it needs to handle
|
||||
// invalidations of the cache
|
||||
union {
|
||||
// for now:
|
||||
// 1. sign is always allocated (drawline.c expects a `char *` for a sign)
|
||||
// 2. conceal char is allocated if larger than 8 bytes.
|
||||
char *ptr; // sign or conceal text
|
||||
char data[8];
|
||||
} text;
|
||||
// NOTE: if more functionality is added to a Highlight these should be overloaded
|
||||
// or restructured
|
||||
char *sign_name;
|
||||
int sign_add_id;
|
||||
int number_hl_id;
|
||||
int line_hl_id;
|
||||
int cursorline_hl_id;
|
||||
uint32_t next;
|
||||
} DecorSignHighlight;
|
||||
|
||||
#define DECOR_SIGN_HIGHLIGHT_INIT { 0, DECOR_PRIORITY_BASE, 0, { .ptr = NULL }, NULL, 0, 0, 0, 0, \
|
||||
DECOR_ID_INVALID }
|
||||
|
||||
enum {
|
||||
kVTIsLines = 1,
|
||||
kVTHide = 2,
|
||||
kVTLinesAbove = 4,
|
||||
};
|
||||
|
||||
typedef struct DecorVirtText DecorVirtText;
|
||||
struct DecorVirtText {
|
||||
uint8_t flags;
|
||||
uint8_t hl_mode;
|
||||
DecorPriority priority;
|
||||
int width; // width of virt_text
|
||||
int col;
|
||||
VirtTextPos pos;
|
||||
// TODO(bfredl): reduce this to one datatype, later
|
||||
union {
|
||||
VirtText virt_text;
|
||||
VirtLines virt_lines;
|
||||
} data;
|
||||
DecorVirtText *next;
|
||||
};
|
||||
#define DECOR_VIRT_TEXT_INIT { 0, kHlModeUnknown, DECOR_PRIORITY_BASE, 0, 0, kVPosEndOfLine, \
|
||||
{ .virt_text = KV_INITIAL_VALUE }, NULL, }
|
||||
#define DECOR_VIRT_LINES_INIT { kVTIsLines, kHlModeUnknown, DECOR_PRIORITY_BASE, 0, 0, \
|
||||
kVPosEndOfLine, { .virt_lines = KV_INITIAL_VALUE }, NULL, }
|
||||
|
||||
typedef struct {
|
||||
uint32_t sh_idx;
|
||||
DecorVirtText *vt;
|
||||
} DecorExt;
|
||||
|
||||
// Stored inline in marktree, with MT_FLAG_DECOR_EXT in MTKey.flags
|
||||
typedef union {
|
||||
DecorHighlightInline hl;
|
||||
DecorExt ext;
|
||||
} DecorInlineData;
|
||||
|
||||
// Not stored in the marktree, but used when passing around args
|
||||
//
|
||||
// Convention: an empty "no decoration" value should always be encoded
|
||||
// with ext=false and an unset DecorHighlightInline (no flags, no hl_id)
|
||||
typedef struct {
|
||||
bool ext;
|
||||
DecorInlineData data;
|
||||
} DecorInline;
|
||||
|
||||
// initializes in a valid state for the DecorHighlightInline branch
|
||||
#define DECOR_INLINE_INIT { .ext = false, .data.hl = DECOR_HIGHLIGHT_INLINE_INIT }
|
@ -122,6 +122,9 @@ void decor_providers_invoke_win(win_T *wp, DecorProviders *providers,
|
||||
DecorProviders *line_providers)
|
||||
{
|
||||
kvi_init(*line_providers);
|
||||
// this might change in the future
|
||||
// then we would need decor_state.running_decor_provider just like "on_line" below
|
||||
assert(kv_size(decor_state.active) == 0);
|
||||
|
||||
linenr_T knownmax = MIN(wp->w_buffer->b_ml.ml_line_count,
|
||||
((wp->w_valid & VALID_BOTLINE)
|
||||
@ -153,7 +156,7 @@ void decor_providers_invoke_win(win_T *wp, DecorProviders *providers,
|
||||
/// @param[out] err Provider error
|
||||
void decor_providers_invoke_line(win_T *wp, DecorProviders *providers, int row, bool *has_decor)
|
||||
{
|
||||
decor_state.running_on_lines = true;
|
||||
decor_state.running_decor_provider = true;
|
||||
for (size_t k = 0; k < kv_size(*providers); k++) {
|
||||
DecorProvider *p = kv_A(*providers, k);
|
||||
if (p && p->redraw_line != LUA_NOREF) {
|
||||
@ -171,7 +174,7 @@ void decor_providers_invoke_line(win_T *wp, DecorProviders *providers, int row,
|
||||
hl_check_ns();
|
||||
}
|
||||
}
|
||||
decor_state.running_on_lines = false;
|
||||
decor_state.running_decor_provider = false;
|
||||
}
|
||||
|
||||
/// For each provider invoke the 'buf' callback for a given buffer.
|
||||
@ -207,6 +210,7 @@ void decor_providers_invoke_end(DecorProviders *providers)
|
||||
decor_provider_invoke(p, "end", p->redraw_end, args, true);
|
||||
}
|
||||
}
|
||||
decor_check_to_be_deleted();
|
||||
}
|
||||
|
||||
/// Mark all cached state of per-namespace highlights as invalid. Revalidate
|
||||
|
@ -265,18 +265,25 @@ static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int
|
||||
bool do_eol = state->eol_col > -1;
|
||||
for (size_t i = 0; i < kv_size(state->active); i++) {
|
||||
DecorRange *item = &kv_A(state->active, i);
|
||||
if (!(item->start_row == state->row && decor_virt_pos(&item->decor))) {
|
||||
if (!(item->start_row == state->row && decor_virt_pos(item))) {
|
||||
continue;
|
||||
}
|
||||
if (item->draw_col == -1) {
|
||||
|
||||
DecorVirtText *vt = NULL;
|
||||
if (item->kind == kDecorKindVirtText) {
|
||||
assert(item->data.vt);
|
||||
vt = item->data.vt;
|
||||
}
|
||||
if (decor_virt_pos(item) && item->draw_col == -1) {
|
||||
bool updated = true;
|
||||
if (item->decor.virt_text_pos == kVTRightAlign) {
|
||||
right_pos -= item->decor.virt_text_width;
|
||||
VirtTextPos pos = decor_virt_pos_kind(item);
|
||||
if (pos == kVPosRightAlign) {
|
||||
right_pos -= vt->width;
|
||||
item->draw_col = right_pos;
|
||||
} else if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) {
|
||||
} else if (pos == kVPosEndOfLine && do_eol) {
|
||||
item->draw_col = state->eol_col;
|
||||
} else if (item->decor.virt_text_pos == kVTWinCol) {
|
||||
item->draw_col = MAX(col_off + item->decor.col, 0);
|
||||
} else if (pos == kVPosWinCol) {
|
||||
item->draw_col = MAX(col_off + vt->col, 0);
|
||||
} else {
|
||||
updated = false;
|
||||
}
|
||||
@ -289,19 +296,19 @@ static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int
|
||||
continue;
|
||||
}
|
||||
int col = 0;
|
||||
if (item->decor.ui_watched) {
|
||||
if (item->kind == kDecorKindUIWatched) {
|
||||
// send mark position to UI
|
||||
col = item->draw_col;
|
||||
WinExtmark m = { (NS)item->ns_id, item->mark_id, win_row, col };
|
||||
WinExtmark m = { (NS)item->data.ui.ns_id, item->data.ui.mark_id, win_row, col };
|
||||
kv_push(win_extmark_arr, m);
|
||||
}
|
||||
if (kv_size(item->decor.virt_text)) {
|
||||
if (vt) {
|
||||
int vcol = item->draw_col - col_off;
|
||||
col = draw_virt_text_item(buf, item->draw_col, item->decor.virt_text,
|
||||
item->decor.hl_mode, max_col, vcol);
|
||||
col = draw_virt_text_item(buf, item->draw_col, vt->data.virt_text,
|
||||
vt->hl_mode, max_col, vcol);
|
||||
}
|
||||
item->draw_col = INT_MIN; // deactivate
|
||||
if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) {
|
||||
if (vt && vt->pos == kVPosEndOfLine && do_eol) {
|
||||
state->eol_col = col + 1;
|
||||
}
|
||||
|
||||
@ -807,9 +814,9 @@ static bool has_more_inline_virt(winlinevars_T *wlv, ptrdiff_t v)
|
||||
for (size_t i = 0; i < kv_size(state->active); i++) {
|
||||
DecorRange *item = &kv_A(state->active, i);
|
||||
if (item->start_row != state->row
|
||||
|| !kv_size(item->decor.virt_text)
|
||||
|| item->decor.virt_text_pos != kVTInline
|
||||
|| item->decor.virt_text_width == 0) {
|
||||
|| item->kind != kDecorKindVirtText
|
||||
|| item->data.vt->pos != kVPosInline
|
||||
|| item->data.vt->width == 0) {
|
||||
continue;
|
||||
}
|
||||
if (item->draw_col >= -1 && item->start_col >= v) {
|
||||
@ -830,14 +837,14 @@ static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t
|
||||
for (size_t i = 0; i < kv_size(state->active); i++) {
|
||||
DecorRange *item = &kv_A(state->active, i);
|
||||
if (item->start_row != state->row
|
||||
|| !kv_size(item->decor.virt_text)
|
||||
|| item->decor.virt_text_pos != kVTInline
|
||||
|| item->decor.virt_text_width == 0) {
|
||||
|| item->kind != kDecorKindVirtText
|
||||
|| item->data.vt->pos != kVPosInline
|
||||
|| item->data.vt->width == 0) {
|
||||
continue;
|
||||
}
|
||||
if (item->draw_col >= -1 && item->start_col == v) {
|
||||
wlv->virt_inline = item->decor.virt_text;
|
||||
wlv->virt_inline_hl_mode = item->decor.hl_mode;
|
||||
wlv->virt_inline = item->data.vt->data.virt_text;
|
||||
wlv->virt_inline_hl_mode = item->data.vt->hl_mode;
|
||||
item->draw_col = INT_MIN;
|
||||
break;
|
||||
}
|
||||
|
@ -50,67 +50,35 @@
|
||||
///
|
||||
/// must not be used during iteration!
|
||||
void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col, int end_row,
|
||||
colnr_T end_col, Decoration *decor, bool right_gravity, bool end_right_gravity,
|
||||
bool no_undo, bool invalidate, Error *err)
|
||||
colnr_T end_col, DecorInline decor, uint16_t decor_flags, bool right_gravity,
|
||||
bool end_right_gravity, bool no_undo, bool invalidate, Error *err)
|
||||
{
|
||||
uint32_t *ns = map_put_ref(uint32_t, uint32_t)(buf->b_extmark_ns, ns_id, NULL, NULL);
|
||||
uint32_t id = idp ? *idp : 0;
|
||||
bool decor_full = false;
|
||||
bool hl_eol = false;
|
||||
|
||||
uint8_t decor_level = kDecorLevelNone; // no decor
|
||||
if (decor) {
|
||||
if (kv_size(decor->virt_text)
|
||||
|| kv_size(decor->virt_lines)
|
||||
|| decor->conceal
|
||||
|| decor_has_sign(decor)
|
||||
|| decor->ui_watched
|
||||
|| decor->spell != kNone) {
|
||||
decor_full = true;
|
||||
decor = xmemdup(decor, sizeof *decor);
|
||||
}
|
||||
decor_level = kDecorLevelVisible; // decor affects redraw
|
||||
hl_eol = decor->hl_eol;
|
||||
if (kv_size(decor->virt_lines)) {
|
||||
decor_level = kDecorLevelVirtLine; // decor affects horizontal size
|
||||
}
|
||||
}
|
||||
uint16_t flags = mt_flags(right_gravity, hl_eol, no_undo, invalidate, decor_level);
|
||||
|
||||
uint16_t flags = mt_flags(right_gravity, no_undo, invalidate, decor.ext) | decor_flags;
|
||||
if (id == 0) {
|
||||
id = ++*ns;
|
||||
} else {
|
||||
MarkTreeIter itr[1] = { 0 };
|
||||
MTKey old_mark = marktree_lookup_ns(buf->b_marktree, ns_id, id, false, itr);
|
||||
if (old_mark.id) {
|
||||
if (decor_state.running_on_lines) {
|
||||
if (err) {
|
||||
api_set_error(err, kErrorTypeException,
|
||||
"Cannot change extmarks during on_line callbacks");
|
||||
}
|
||||
goto error;
|
||||
}
|
||||
if (mt_paired(old_mark) || end_row > -1) {
|
||||
extmark_del_id(buf, ns_id, id);
|
||||
} else {
|
||||
// TODO(bfredl): we need to do more if "revising" a decoration mark.
|
||||
assert(marktree_itr_valid(itr));
|
||||
if (old_mark.pos.row == row && old_mark.pos.col == col) {
|
||||
if (marktree_decor_level(old_mark) > kDecorLevelNone) {
|
||||
decor_remove(buf, row, row, old_mark.decor_full, false);
|
||||
old_mark.decor_full = NULL;
|
||||
if (mt_decor_any(old_mark)) {
|
||||
buf_decor_remove(buf, row, row, mt_decor(old_mark), true);
|
||||
}
|
||||
old_mark.flags = flags;
|
||||
if (decor_full) {
|
||||
old_mark.decor_full = decor;
|
||||
} else if (decor) {
|
||||
old_mark.hl_id = decor->hl_id;
|
||||
old_mark.priority = decor->priority;
|
||||
}
|
||||
marktree_revise(buf->b_marktree, itr, decor_level, old_mark);
|
||||
|
||||
// not paired: we can revise in place
|
||||
mt_itr_rawkey(itr).flags &= (uint16_t) ~MT_FLAG_EXTERNAL_MASK;
|
||||
mt_itr_rawkey(itr).flags |= flags;
|
||||
mt_itr_rawkey(itr).decor_data = decor.data;
|
||||
goto revised;
|
||||
}
|
||||
decor_remove(buf, old_mark.pos.row, old_mark.pos.row, old_mark.decor_full, false);
|
||||
buf_decor_remove(buf, old_mark.pos.row, old_mark.pos.row, mt_decor(old_mark), true);
|
||||
marktree_del_itr(buf->b_marktree, itr, false);
|
||||
}
|
||||
} else {
|
||||
@ -118,29 +86,19 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col
|
||||
}
|
||||
}
|
||||
|
||||
MTKey mark = { { row, col }, ns_id, id, 0, flags, 0, NULL };
|
||||
if (decor_full) {
|
||||
mark.decor_full = decor;
|
||||
} else if (decor) {
|
||||
mark.hl_id = decor->hl_id;
|
||||
mark.priority = decor->priority;
|
||||
}
|
||||
MTKey mark = { { row, col }, ns_id, id, flags, decor.data };
|
||||
|
||||
marktree_put(buf->b_marktree, mark, end_row, end_col, end_right_gravity);
|
||||
|
||||
revised:
|
||||
decor_add(buf, row, end_row, decor, decor && decor->hl_id);
|
||||
if (decor_flags || decor.ext) {
|
||||
buf_put_decor(buf, decor, row);
|
||||
decor_redraw(buf, row, end_row > -1 ? end_row : row, decor);
|
||||
}
|
||||
|
||||
if (idp) {
|
||||
*idp = id;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
if (decor_full) {
|
||||
decor_free(decor);
|
||||
}
|
||||
}
|
||||
|
||||
static bool extmark_setraw(buf_T *buf, uint64_t mark, int row, colnr_T col)
|
||||
@ -189,8 +147,8 @@ void extmark_del(buf_T *buf, MarkTreeIter *itr, MTKey key, bool restore)
|
||||
}
|
||||
}
|
||||
|
||||
if (marktree_decor_level(key) > kDecorLevelNone) {
|
||||
decor_remove(buf, key.pos.row, key2.pos.row, key.decor_full, false);
|
||||
if (mt_decor_any(key)) {
|
||||
buf_decor_remove(buf, key.pos.row, key2.pos.row, mt_decor(key), true);
|
||||
}
|
||||
|
||||
// TODO(bfredl): delete it from current undo header, opportunistically?
|
||||
@ -231,7 +189,6 @@ bool extmark_clear(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_r
|
||||
marktree_itr_next(buf->b_marktree, itr);
|
||||
}
|
||||
}
|
||||
|
||||
return marks_cleared;
|
||||
}
|
||||
|
||||
@ -294,24 +251,11 @@ static void push_mark(ExtmarkInfoArray *array, uint32_t ns_id, ExtmarkType type_
|
||||
if (!(ns_id == UINT32_MAX || mark.start.ns == ns_id)) {
|
||||
return;
|
||||
}
|
||||
uint16_t type_flags = kExtmarkNone;
|
||||
if (type_filter != kExtmarkNone) {
|
||||
Decoration *decor = mark.start.decor_full;
|
||||
if (decor && (decor->sign_text || decor->number_hl_id)) {
|
||||
type_flags |= (kExtmarkSignHL|kExtmarkSign);
|
||||
}
|
||||
if (decor && (decor->line_hl_id || decor->cursorline_hl_id)) {
|
||||
type_flags |= (kExtmarkSignHL|kExtmarkHighlight);
|
||||
}
|
||||
if (decor && decor->virt_text.size) {
|
||||
type_flags |= kExtmarkVirtText;
|
||||
}
|
||||
if (decor && decor->virt_lines.size) {
|
||||
type_flags |= kExtmarkVirtLines;
|
||||
}
|
||||
if (mark.start.hl_id) {
|
||||
type_flags |= kExtmarkHighlight;
|
||||
if (!mt_decor_any(mark.start)) {
|
||||
return;
|
||||
}
|
||||
uint16_t type_flags = decor_type_flags(mt_decor(mark.start));
|
||||
|
||||
if (!(type_flags & type_filter)) {
|
||||
return;
|
||||
@ -349,9 +293,9 @@ void extmark_free_all(buf_T *buf)
|
||||
break;
|
||||
}
|
||||
|
||||
// don't free mark.decor_full twice for a paired mark.
|
||||
// don't free mark.decor twice for a paired mark.
|
||||
if (!(mt_paired(mark) && mt_end(mark))) {
|
||||
decor_free(mark.decor_full);
|
||||
decor_free(mt_decor(mark));
|
||||
}
|
||||
|
||||
marktree_itr_next(buf->b_marktree, itr);
|
||||
@ -398,9 +342,8 @@ void extmark_splice_delete(buf_T *buf, int l_row, colnr_T l_col, int u_row, coln
|
||||
continue;
|
||||
} else {
|
||||
invalidated = true;
|
||||
mark.flags |= MT_FLAG_INVALID;
|
||||
marktree_revise(curbuf->b_marktree, itr, marktree_decor_level(mark), mark);
|
||||
decor_remove(buf, mark.pos.row, endpos.row, mark.decor_full, true);
|
||||
mt_itr_rawkey(itr).flags |= MT_FLAG_INVALID;
|
||||
buf_decor_remove(buf, mark.pos.row, endpos.row, mt_decor(mark), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -451,10 +394,8 @@ void extmark_apply_undo(ExtmarkUndoObject undo_info, bool undo)
|
||||
if (pos.invalidated) {
|
||||
MarkTreeIter itr[1] = { 0 };
|
||||
MTKey mark = marktree_lookup(curbuf->b_marktree, pos.mark, itr);
|
||||
MTKey end = marktree_get_alt(curbuf->b_marktree, mark, NULL);
|
||||
mark.flags &= (uint16_t) ~MT_FLAG_INVALID;
|
||||
marktree_revise(curbuf->b_marktree, itr, marktree_decor_level(mark), mark);
|
||||
decor_add(curbuf, mark.pos.row, end.pos.row, mark.decor_full, mark.hl_id);
|
||||
mt_itr_rawkey(itr).flags &= (uint16_t) ~MT_FLAG_INVALID;
|
||||
buf_put_decor(curbuf, mt_decor(mark), mark.pos.row);
|
||||
}
|
||||
if (pos.old_row >= 0) {
|
||||
extmark_setraw(curbuf, pos.mark, pos.old_row, pos.old_col);
|
||||
|
@ -64,6 +64,8 @@ typedef enum {
|
||||
kExtmarkClear,
|
||||
} UndoObjectType;
|
||||
|
||||
// TODO(bfredl): if possible unify these with marktree flags,
|
||||
// so it is possible to filter extmarks directly on top-level flags
|
||||
typedef enum {
|
||||
kExtmarkNone = 0x1,
|
||||
kExtmarkSign = 0x2,
|
||||
|
@ -3,13 +3,6 @@
|
||||
#include "klib/kvec.h"
|
||||
#include "nvim/types.h"
|
||||
|
||||
typedef struct {
|
||||
char *text;
|
||||
int hl_id;
|
||||
} VirtTextChunk;
|
||||
|
||||
typedef kvec_t(VirtTextChunk) VirtText;
|
||||
|
||||
typedef struct undo_object ExtmarkUndoObject;
|
||||
typedef kvec_t(ExtmarkUndoObject) extmark_undo_vec_t;
|
||||
|
||||
@ -21,9 +14,3 @@ typedef enum {
|
||||
kExtmarkNoUndo, // Operation should not be reversible
|
||||
kExtmarkUndoNoRedo, // Operation should be undoable, but not redoable
|
||||
} ExtmarkOp;
|
||||
|
||||
typedef enum {
|
||||
kDecorLevelNone = 0,
|
||||
kDecorLevelVisible = 1,
|
||||
kDecorLevelVirtLine = 2,
|
||||
} DecorLevel;
|
||||
|
@ -287,7 +287,7 @@ static inline void marktree_putp_aux(MarkTree *b, MTNode *x, MTKey k)
|
||||
|
||||
void marktree_put(MarkTree *b, MTKey key, int end_row, int end_col, bool end_right)
|
||||
{
|
||||
assert(!(key.flags & ~MT_FLAG_EXTERNAL_MASK));
|
||||
assert(!(key.flags & ~(MT_FLAG_EXTERNAL_MASK | MT_FLAG_RIGHT_GRAVITY)));
|
||||
if (end_row >= 0) {
|
||||
key.flags |= MT_FLAG_PAIRED;
|
||||
}
|
||||
@ -1137,25 +1137,6 @@ static void marktree_free_node(MarkTree *b, MTNode *x)
|
||||
b->n_nodes--;
|
||||
}
|
||||
|
||||
/// NB: caller must check not pair!
|
||||
void marktree_revise(MarkTree *b, MarkTreeIter *itr, uint8_t decor_level, MTKey key)
|
||||
{
|
||||
// TODO(bfredl): clean up this mess and re-instantiate &= and |= forms
|
||||
// once we upgrade to a non-broken version of gcc in functionaltest-lua CI
|
||||
rawkey(itr).flags = (uint16_t)(rawkey(itr).flags & (uint16_t) ~MT_FLAG_DECOR_MASK);
|
||||
rawkey(itr).flags = (uint16_t)(rawkey(itr).flags & (uint16_t) ~MT_FLAG_INVALID);
|
||||
rawkey(itr).flags = (uint16_t)(rawkey(itr).flags
|
||||
| (uint16_t)(decor_level << MT_FLAG_DECOR_OFFSET)
|
||||
| (uint16_t)(key.flags & MT_FLAG_DECOR_MASK)
|
||||
| (uint16_t)(key.flags & MT_FLAG_HL_EOL)
|
||||
| (uint16_t)(key.flags & MT_FLAG_NO_UNDO)
|
||||
| (uint16_t)(key.flags & MT_FLAG_INVALID)
|
||||
| (uint16_t)(key.flags & MT_FLAG_INVALIDATE));
|
||||
rawkey(itr).decor_full = key.decor_full;
|
||||
rawkey(itr).hl_id = key.hl_id;
|
||||
rawkey(itr).priority = key.priority;
|
||||
}
|
||||
|
||||
/// @param itr iterator is invalid after call
|
||||
void marktree_move(MarkTree *b, MarkTreeIter *itr, int row, int col)
|
||||
{
|
||||
@ -2003,8 +1984,8 @@ static void marktree_itr_fix_pos(MarkTree *b, MarkTreeIter *itr)
|
||||
void marktree_put_test(MarkTree *b, uint32_t ns, uint32_t id, int row, int col, bool right_gravity,
|
||||
int end_row, int end_col, bool end_right)
|
||||
{
|
||||
uint16_t flags = mt_flags(right_gravity, false, false, false, 0);
|
||||
MTKey key = { { row, col }, ns, id, 0, flags, 0, NULL };
|
||||
uint16_t flags = mt_flags(right_gravity, false, false, false);
|
||||
MTKey key = { { row, col }, ns, id, flags, { .hl = DECOR_HIGHLIGHT_INLINE_INIT } };
|
||||
marktree_put(b, key, end_row, end_col, end_right);
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include "klib/kvec.h"
|
||||
#include "nvim/assert.h"
|
||||
#include "nvim/decoration_defs.h"
|
||||
#include "nvim/garray.h"
|
||||
#include "nvim/map.h"
|
||||
#include "nvim/pos.h"
|
||||
@ -47,6 +48,8 @@ typedef struct {
|
||||
} MarkTreeIter;
|
||||
|
||||
#define marktree_itr_valid(itr) ((itr)->x != NULL)
|
||||
// accces raw key: flags in MT_FLAG_EXTERNAL_MASK and decor_data are safe to modify.
|
||||
#define mt_itr_rawkey(itr) ((itr)->x->key[(itr)->i])
|
||||
|
||||
// Internal storage
|
||||
//
|
||||
@ -56,10 +59,8 @@ typedef struct {
|
||||
MTPos pos;
|
||||
uint32_t ns;
|
||||
uint32_t id;
|
||||
int32_t hl_id;
|
||||
uint16_t flags;
|
||||
uint16_t priority;
|
||||
Decoration *decor_full;
|
||||
DecorInlineData decor_data; // "ext" tag in flags
|
||||
} MTKey;
|
||||
|
||||
typedef struct {
|
||||
@ -68,28 +69,40 @@ typedef struct {
|
||||
bool end_right_gravity;
|
||||
} MTPair;
|
||||
|
||||
#define MT_INVALID_KEY (MTKey) { { -1, -1 }, 0, 0, 0, 0, 0, NULL }
|
||||
#define MT_INVALID_KEY (MTKey) { { -1, -1 }, 0, 0, 0, { .hl = DECOR_HIGHLIGHT_INLINE_INIT } }
|
||||
|
||||
#define MT_FLAG_REAL (((uint16_t)1) << 0)
|
||||
#define MT_FLAG_END (((uint16_t)1) << 1)
|
||||
#define MT_FLAG_PAIRED (((uint16_t)1) << 2)
|
||||
// orphaned: the other side of this paired mark was deleted. this mark must be deleted very soon!
|
||||
#define MT_FLAG_ORPHANED (((uint16_t)1) << 3)
|
||||
#define MT_FLAG_HL_EOL (((uint16_t)1) << 4)
|
||||
#define MT_FLAG_NO_UNDO (((uint16_t)1) << 5)
|
||||
#define MT_FLAG_INVALIDATE (((uint16_t)1) << 6)
|
||||
#define MT_FLAG_INVALID (((uint16_t)1) << 7)
|
||||
#define MT_FLAG_NO_UNDO (((uint16_t)1) << 4)
|
||||
#define MT_FLAG_INVALIDATE (((uint16_t)1) << 5)
|
||||
#define MT_FLAG_INVALID (((uint16_t)1) << 6)
|
||||
// discriminant for union
|
||||
#define MT_FLAG_DECOR_EXT (((uint16_t)1) << 7)
|
||||
|
||||
#define DECOR_LEVELS 4
|
||||
#define MT_FLAG_DECOR_OFFSET 8
|
||||
#define MT_FLAG_DECOR_MASK (((uint16_t)(DECOR_LEVELS - 1)) << MT_FLAG_DECOR_OFFSET)
|
||||
// TODO(bfredl): flags for decorations. These cover the cases where we quickly needs
|
||||
// to skip over irrelevant marks internally. When we refactor this more, also make all info
|
||||
// for ExtmarkType included here
|
||||
#define MT_FLAG_DECOR_HL (((uint16_t)1) << 8)
|
||||
#define MT_FLAG_DECOR_SIGNTEXT (((uint16_t)1) << 9)
|
||||
// TODO(bfredl): for now this means specifically number_hl, line_hl, cursorline_hl
|
||||
// needs to clean up the name.
|
||||
#define MT_FLAG_DECOR_SIGNHL (((uint16_t)1) << 10)
|
||||
#define MT_FLAG_DECOR_VIRT_LINES (((uint16_t)1) << 11)
|
||||
#define MT_FLAG_DECOR_VIRT_TEXT_INLINE (((uint16_t)1) << 12)
|
||||
|
||||
// These _must_ be last to preserve ordering of marks
|
||||
#define MT_FLAG_RIGHT_GRAVITY (((uint16_t)1) << 14)
|
||||
#define MT_FLAG_LAST (((uint16_t)1) << 15)
|
||||
|
||||
#define MT_FLAG_EXTERNAL_MASK (MT_FLAG_DECOR_MASK | MT_FLAG_RIGHT_GRAVITY | MT_FLAG_HL_EOL \
|
||||
| MT_FLAG_NO_UNDO | MT_FLAG_INVALIDATE | MT_FLAG_INVALID)
|
||||
#define MT_FLAG_DECOR_MASK (MT_FLAG_DECOR_EXT| MT_FLAG_DECOR_HL | MT_FLAG_DECOR_SIGNTEXT \
|
||||
| MT_FLAG_DECOR_SIGNHL | MT_FLAG_DECOR_VIRT_LINES \
|
||||
| MT_FLAG_DECOR_VIRT_TEXT_INLINE)
|
||||
|
||||
#define MT_FLAG_EXTERNAL_MASK (MT_FLAG_DECOR_MASK | MT_FLAG_NO_UNDO \
|
||||
| MT_FLAG_INVALIDATE | MT_FLAG_INVALID)
|
||||
|
||||
// this is defined so that start and end of the same range have adjacent ids
|
||||
#define MARKTREE_END_FLAG ((uint64_t)1)
|
||||
@ -143,20 +156,22 @@ static inline bool mt_invalid(MTKey key)
|
||||
return key.flags & MT_FLAG_INVALID;
|
||||
}
|
||||
|
||||
static inline uint8_t marktree_decor_level(MTKey key)
|
||||
static inline bool mt_decor_any(MTKey key)
|
||||
{
|
||||
return (uint8_t)((key.flags&MT_FLAG_DECOR_MASK) >> MT_FLAG_DECOR_OFFSET);
|
||||
return key.flags & MT_FLAG_DECOR_MASK;
|
||||
}
|
||||
|
||||
static inline uint16_t mt_flags(bool right_gravity, bool hl_eol, bool no_undo, bool invalidate,
|
||||
uint8_t decor_level)
|
||||
static inline bool mt_decor_sign(MTKey key)
|
||||
{
|
||||
return key.flags & (MT_FLAG_DECOR_SIGNTEXT | MT_FLAG_DECOR_SIGNHL);
|
||||
}
|
||||
|
||||
static inline uint16_t mt_flags(bool right_gravity, bool no_undo, bool invalidate, bool decor_ext)
|
||||
{
|
||||
assert(decor_level < DECOR_LEVELS);
|
||||
return (uint16_t)((right_gravity ? MT_FLAG_RIGHT_GRAVITY : 0)
|
||||
| (hl_eol ? MT_FLAG_HL_EOL : 0)
|
||||
| (no_undo ? MT_FLAG_NO_UNDO : 0)
|
||||
| (invalidate ? MT_FLAG_INVALIDATE : 0)
|
||||
| (decor_level << MT_FLAG_DECOR_OFFSET));
|
||||
| (decor_ext ? MT_FLAG_DECOR_EXT : 0));
|
||||
}
|
||||
|
||||
static inline MTPair mtpair_from(MTKey start, MTKey end)
|
||||
@ -164,6 +179,11 @@ static inline MTPair mtpair_from(MTKey start, MTKey end)
|
||||
return (MTPair){ .start = start, .end_pos = end.pos, .end_right_gravity = mt_right(end) };
|
||||
}
|
||||
|
||||
static inline DecorInline mt_decor(MTKey key)
|
||||
{
|
||||
return (DecorInline){ .ext = key.flags & MT_FLAG_DECOR_EXT, .data = key.decor_data };
|
||||
}
|
||||
|
||||
typedef kvec_withinit_t(uint64_t, 4) Intersection;
|
||||
|
||||
struct mtnode_s {
|
||||
@ -186,8 +206,6 @@ static inline uint64_t mt_dbg_id(uint64_t id)
|
||||
typedef struct {
|
||||
MTNode *root;
|
||||
size_t n_keys, n_nodes;
|
||||
// TODO(bfredl): the pointer to node could be part of the larger
|
||||
// Map(uint64_t, ExtmarkItem) essentially;
|
||||
PMap(uint64_t) id2node[1];
|
||||
} MarkTree;
|
||||
|
||||
|
@ -222,21 +222,25 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
|
||||
if (mark.pos.row != cts->cts_row || mark.pos.col > col) {
|
||||
break;
|
||||
} else if (mark.pos.col == col) {
|
||||
if (!mt_end(mark)) {
|
||||
Decoration decor = get_decor(mark);
|
||||
if (decor.virt_text_pos == kVTInline) {
|
||||
if (mt_right(mark)) {
|
||||
cts->cts_cur_text_width_right += decor.virt_text_width;
|
||||
} else {
|
||||
cts->cts_cur_text_width_left += decor.virt_text_width;
|
||||
}
|
||||
size += decor.virt_text_width;
|
||||
if (*s == TAB) {
|
||||
// tab size changes because of the inserted text
|
||||
size -= tab_size;
|
||||
tab_size = win_chartabsize(wp, s, vcol + size);
|
||||
size += tab_size;
|
||||
if (!mt_end(mark) && mark.flags & (MT_FLAG_DECOR_VIRT_TEXT_INLINE)) {
|
||||
DecorInline decor = mt_decor(mark);
|
||||
DecorVirtText *vt = decor.ext ? decor.data.ext.vt : NULL;
|
||||
while (vt) {
|
||||
if (!(vt->flags & kVTIsLines) && vt->pos == kVPosInline) {
|
||||
if (mt_right(mark)) {
|
||||
cts->cts_cur_text_width_right += vt->width;
|
||||
} else {
|
||||
cts->cts_cur_text_width_left += vt->width;
|
||||
}
|
||||
size += vt->width;
|
||||
if (*s == TAB) {
|
||||
// tab size changes because of the inserted text
|
||||
size -= tab_size;
|
||||
tab_size = win_chartabsize(wp, s, vcol + size);
|
||||
size += tab_size;
|
||||
}
|
||||
}
|
||||
vt = vt->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
113
src/nvim/sign.c
113
src/nvim/sign.c
@ -71,9 +71,9 @@ static int64_t group_get_ns(const char *group)
|
||||
return ns ? ns : -1;
|
||||
}
|
||||
|
||||
static const char *sign_get_name(MTKey mark)
|
||||
static const char *sign_get_name(DecorSignHighlight *sh)
|
||||
{
|
||||
char *name = mark.decor_full->sign_name;
|
||||
char *name = sh->sign_name;
|
||||
return !name ? "" : map_has(cstr_t, &sign_map, name) ? name : "[Deleted]";
|
||||
}
|
||||
|
||||
@ -92,15 +92,24 @@ static void buf_set_sign(buf_T *buf, uint32_t *id, char *group, int prio, linenr
|
||||
}
|
||||
|
||||
uint32_t ns = group ? (uint32_t)nvim_create_namespace(cstr_as_string(group)) : 0;
|
||||
Decoration decor = DECORATION_INIT;
|
||||
decor.sign_text = sp->sn_text ? xstrdup(sp->sn_text) : NULL;
|
||||
decor.sign_name = xstrdup(sp->sn_name);
|
||||
decor.sign_hl_id = sp->sn_text_hl;
|
||||
decor.line_hl_id = sp->sn_line_hl;
|
||||
decor.number_hl_id = sp->sn_num_hl;
|
||||
decor.cursorline_hl_id = sp->sn_cul_hl;
|
||||
decor.priority = (DecorPriority)prio;
|
||||
extmark_set(buf, ns, id, lnum - 1, 0, -1, -1, &decor, true, false, true, true, NULL);
|
||||
DecorSignHighlight sign = DECOR_SIGN_HIGHLIGHT_INIT;
|
||||
|
||||
sign.flags |= kSHIsSign;
|
||||
sign.text.ptr = sp->sn_text ? xstrdup(sp->sn_text) : NULL;
|
||||
sign.sign_name = xstrdup(sp->sn_name);
|
||||
sign.hl_id = sp->sn_text_hl;
|
||||
sign.line_hl_id = sp->sn_line_hl;
|
||||
sign.number_hl_id = sp->sn_num_hl;
|
||||
sign.cursorline_hl_id = sp->sn_cul_hl;
|
||||
sign.priority = (DecorPriority)prio;
|
||||
|
||||
bool has_hl = (sp->sn_line_hl || sp->sn_num_hl || sp->sn_cul_hl);
|
||||
uint16_t decor_flags = (sp->sn_text ? MT_FLAG_DECOR_SIGNTEXT : 0)
|
||||
| (has_hl ? MT_FLAG_DECOR_SIGNHL : 0);
|
||||
|
||||
DecorInline decor = { .ext = true, .data.ext = { .vt = NULL, .sh_idx = decor_put_sh(sign) } };
|
||||
extmark_set(buf, ns, id, lnum - 1, 0, -1, -1, decor, decor_flags, true,
|
||||
false, true, true, NULL);
|
||||
}
|
||||
|
||||
/// For an existing, placed sign with "id", modify the sign, group or priority.
|
||||
@ -148,9 +157,18 @@ int sign_cmp(const void *p1, const void *p2)
|
||||
const MTKey *s2 = (MTKey *)p2;
|
||||
int n = s1->pos.row - s2->pos.row;
|
||||
|
||||
return n ? n : (n = s2->decor_full->priority - s1->decor_full->priority)
|
||||
? n : (n = (int)(s2->id - s1->id))
|
||||
? n : (s2->decor_full->sign_add_id - s1->decor_full->sign_add_id);
|
||||
if (n) {
|
||||
return n;
|
||||
}
|
||||
|
||||
DecorSignHighlight *sh1 = decor_find_sign(mt_decor(*s1));
|
||||
DecorSignHighlight *sh2 = decor_find_sign(mt_decor(*s2));
|
||||
assert(sh1 && sh2);
|
||||
|
||||
n = sh2->priority - sh1->priority;
|
||||
|
||||
return n ? n : (n = (int)(s2->id - s1->id))
|
||||
? n : (sh2->sign_add_id - sh1->sign_add_id);
|
||||
}
|
||||
|
||||
/// Delete the specified signs
|
||||
@ -177,8 +195,7 @@ static int buf_delete_signs(buf_T *buf, char *group, int id, linenr_T atlnum)
|
||||
|
||||
MTPair pair;
|
||||
while (marktree_itr_step_overlap(buf->b_marktree, itr, &pair)) {
|
||||
if ((ns == UINT32_MAX || ns == pair.start.ns)
|
||||
&& pair.start.decor_full && decor_has_sign(pair.start.decor_full)) {
|
||||
if ((ns == UINT32_MAX || ns == pair.start.ns) && mt_decor_sign(pair.start)) {
|
||||
kv_push(signs, pair.start);
|
||||
}
|
||||
}
|
||||
@ -191,7 +208,7 @@ static int buf_delete_signs(buf_T *buf, char *group, int id, linenr_T atlnum)
|
||||
if (row && mark.pos.row > row) {
|
||||
break;
|
||||
}
|
||||
if (!mt_end(mark) && mark.decor_full && decor_has_sign(mark.decor_full)
|
||||
if (!mt_end(mark) && mt_decor_sign(mark)
|
||||
&& (id == 0 || (int)mark.id == id)
|
||||
&& (ns == UINT32_MAX || ns == mark.ns)) {
|
||||
if (atlnum > 0) {
|
||||
@ -248,7 +265,7 @@ static void sign_list_placed(buf_T *rbuf, char *group)
|
||||
|
||||
while (itr->x) {
|
||||
MTKey mark = marktree_itr_current(itr);
|
||||
if (!mt_end(mark) && mark.decor_full && decor_has_sign(mark.decor_full)
|
||||
if (!mt_end(mark) && mt_decor_sign(mark)
|
||||
&& (ns == UINT32_MAX || ns == mark.ns)) {
|
||||
kv_push(signs, mark);
|
||||
}
|
||||
@ -262,14 +279,16 @@ static void sign_list_placed(buf_T *rbuf, char *group)
|
||||
namebuf[0] = '\0';
|
||||
groupbuf[0] = '\0';
|
||||
MTKey mark = kv_A(signs, i);
|
||||
if (mark.decor_full->sign_name != NULL) {
|
||||
vim_snprintf(namebuf, MSG_BUF_LEN, _(" name=%s"), sign_get_name(mark));
|
||||
|
||||
DecorSignHighlight *sh = decor_find_sign(mt_decor(mark));
|
||||
if (sh->sign_name != NULL) {
|
||||
vim_snprintf(namebuf, MSG_BUF_LEN, _(" name=%s"), sign_get_name(sh));
|
||||
}
|
||||
if (mark.ns != 0) {
|
||||
vim_snprintf(groupbuf, MSG_BUF_LEN, _(" group=%s"), describe_ns((int)mark.ns, ""));
|
||||
}
|
||||
vim_snprintf(lbuf, MSG_BUF_LEN, _(" line=%" PRIdLINENR " id=%u%s%s priority=%d"),
|
||||
mark.pos.row + 1, mark.id, groupbuf, namebuf, mark.decor_full->priority);
|
||||
mark.pos.row + 1, mark.id, groupbuf, namebuf, sh->priority);
|
||||
msg_puts(lbuf);
|
||||
msg_putchar('\n');
|
||||
}
|
||||
@ -841,21 +860,12 @@ void ex_sign(exarg_T *eap)
|
||||
}
|
||||
}
|
||||
|
||||
/// Append dictionary of information for a defined sign "sp", or placed
|
||||
/// sign "mark" to "retlist". Either "sp", or "mark" is NULL.
|
||||
static void sign_list_append_info(sign_T *sp, MTKey *mark, list_T *retlist)
|
||||
/// Get dictionary of information for a defined sign "sp"
|
||||
static dict_T *sign_get_info_dict(sign_T *sp)
|
||||
{
|
||||
dict_T *d = tv_dict_alloc();
|
||||
tv_list_append_dict(retlist, d);
|
||||
|
||||
tv_dict_add_str(d, S_LEN("name"), sp ? sp->sn_name : sign_get_name(*mark));
|
||||
if (mark != NULL) {
|
||||
tv_dict_add_nr(d, S_LEN("id"), (int)mark->id);
|
||||
tv_dict_add_str(d, S_LEN("group"), describe_ns((int)mark->ns, ""));
|
||||
tv_dict_add_nr(d, S_LEN("lnum"), mark->pos.row + 1);
|
||||
tv_dict_add_nr(d, S_LEN("priority"), mark->decor_full->priority);
|
||||
return;
|
||||
}
|
||||
tv_dict_add_str(d, S_LEN("name"), sp->sn_name);
|
||||
|
||||
if (sp->sn_icon != NULL) {
|
||||
tv_dict_add_str(d, S_LEN("icon"), sp->sn_icon);
|
||||
@ -871,6 +881,22 @@ static void sign_list_append_info(sign_T *sp, MTKey *mark, list_T *retlist)
|
||||
tv_dict_add_str(d, arg[i], strlen(arg[i]), p ? p : "NONE");
|
||||
}
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
/// Get dictionary of information for placed sign "mark"
|
||||
static dict_T *sign_get_placed_info_dict(MTKey mark)
|
||||
{
|
||||
dict_T *d = tv_dict_alloc();
|
||||
|
||||
DecorSignHighlight *sh = decor_find_sign(mt_decor(mark));
|
||||
|
||||
tv_dict_add_str(d, S_LEN("name"), sign_get_name(sh));
|
||||
tv_dict_add_nr(d, S_LEN("id"), (int)mark.id);
|
||||
tv_dict_add_str(d, S_LEN("group"), describe_ns((int)mark.ns, ""));
|
||||
tv_dict_add_nr(d, S_LEN("lnum"), mark.pos.row + 1);
|
||||
tv_dict_add_nr(d, S_LEN("priority"), sh->priority);
|
||||
return d;
|
||||
}
|
||||
|
||||
/// Returns information about signs placed in a buffer as list of dicts.
|
||||
@ -883,8 +909,8 @@ list_T *get_buffer_signs(buf_T *buf)
|
||||
|
||||
while (itr->x) {
|
||||
MTKey mark = marktree_itr_current(itr);
|
||||
if (!mt_end(mark) && mark.decor_full && decor_has_sign(mark.decor_full)) {
|
||||
sign_list_append_info(NULL, &mark, l);
|
||||
if (!mt_end(mark) && mt_decor_sign(mark)) {
|
||||
tv_list_append_dict(l, sign_get_placed_info_dict(mark));
|
||||
}
|
||||
marktree_itr_next(buf->b_marktree, itr);
|
||||
}
|
||||
@ -918,13 +944,15 @@ static void sign_get_placed_in_buf(buf_T *buf, linenr_T lnum, int sign_id, const
|
||||
if (lnum && mark.pos.row >= lnum) {
|
||||
break;
|
||||
}
|
||||
if (!mt_end(mark) && mark.decor_full && decor_has_sign(mark.decor_full)
|
||||
if (!mt_end(mark)
|
||||
&& (ns == UINT32_MAX || ns == mark.ns)
|
||||
&& ((lnum == 0 && sign_id == 0)
|
||||
|| (sign_id == 0 && lnum == mark.pos.row + 1)
|
||||
|| (lnum == 0 && sign_id == (int)mark.id)
|
||||
|| (lnum == mark.pos.row + 1 && sign_id == (int)mark.id))) {
|
||||
kv_push(signs, mark);
|
||||
if (mt_decor_sign(mark)) {
|
||||
kv_push(signs, mark);
|
||||
}
|
||||
}
|
||||
marktree_itr_next(buf->b_marktree, itr);
|
||||
}
|
||||
@ -932,7 +960,7 @@ static void sign_get_placed_in_buf(buf_T *buf, linenr_T lnum, int sign_id, const
|
||||
if (kv_size(signs)) {
|
||||
qsort((void *)&kv_A(signs, 0), kv_size(signs), sizeof(MTKey), sign_cmp);
|
||||
for (size_t i = 0; i < kv_size(signs); i++) {
|
||||
sign_list_append_info(NULL, &kv_A(signs, i), l);
|
||||
tv_list_append_dict(l, sign_get_placed_info_dict(kv_A(signs, i)));
|
||||
}
|
||||
kv_destroy(signs);
|
||||
}
|
||||
@ -1222,18 +1250,17 @@ void f_sign_define(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
/// "sign_getdefined()" function
|
||||
void f_sign_getdefined(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
{
|
||||
sign_T *sp;
|
||||
|
||||
tv_list_alloc_ret(rettv, 0);
|
||||
|
||||
if (argvars[0].v_type == VAR_UNKNOWN) {
|
||||
sign_T *sp;
|
||||
map_foreach_value(&sign_map, sp, {
|
||||
sign_list_append_info(sp, NULL, rettv->vval.v_list);
|
||||
tv_list_append_dict(rettv->vval.v_list, sign_get_info_dict(sp));
|
||||
});
|
||||
} else {
|
||||
sp = pmap_get(cstr_t)(&sign_map, tv_get_string(&argvars[0]));
|
||||
sign_T *sp = pmap_get(cstr_t)(&sign_map, tv_get_string(&argvars[0]));
|
||||
if (sp != NULL) {
|
||||
sign_list_append_info(sp, NULL, rettv->vval.v_list);
|
||||
tv_list_append_dict(rettv->vval.v_list, sign_get_info_dict(sp));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,4 @@ typedef enum {
|
||||
|
||||
#define TRISTATE_FROM_INT(val) ((val) == 0 ? kFalse : ((val) >= 1 ? kTrue : kNone))
|
||||
|
||||
typedef struct Decoration Decoration;
|
||||
|
||||
typedef int64_t OptInt;
|
||||
|
@ -1579,6 +1579,7 @@ describe('API/extmarks', function()
|
||||
eq({0, 0, {
|
||||
ns_id = 1,
|
||||
cursorline_hl_group = "Statement",
|
||||
priority = 4096,
|
||||
right_gravity = true,
|
||||
} }, get_extmark_by_id(ns, marks[3], { details = true }))
|
||||
end)
|
||||
|
@ -762,8 +762,6 @@ describe('Buffer highlighting', function()
|
||||
local s1 = {{'Köttbullar', 'Comment'}, {'Kräuterbutter'}}
|
||||
local s2 = {{'こんにちは', 'Comment'}}
|
||||
|
||||
-- TODO: only a virtual text from the same ns currently overrides
|
||||
-- an existing virtual text. We might add a prioritation system.
|
||||
set_virtual_text(id1, 0, s1, {})
|
||||
eq({{1, 0, 0, {
|
||||
ns_id = 1,
|
||||
@ -775,7 +773,6 @@ describe('Buffer highlighting', function()
|
||||
virt_text_hide = false,
|
||||
}}}, get_extmarks(id1, {0,0}, {0, -1}, {details=true}))
|
||||
|
||||
-- TODO: is this really valid? shouldn't the max be line_count()-1?
|
||||
local lastline = line_count()
|
||||
set_virtual_text(id1, line_count(), s2, {})
|
||||
eq({{3, lastline, 0, {
|
||||
|
@ -663,7 +663,7 @@ describe('decorations providers', function()
|
||||
]])
|
||||
end)
|
||||
|
||||
it('does not allow removing extmarks during on_line callbacks', function()
|
||||
it('does allow removing extmarks during on_line callbacks', function()
|
||||
exec_lua([[
|
||||
eok = true
|
||||
]])
|
||||
@ -676,7 +676,7 @@ describe('decorations providers', function()
|
||||
end
|
||||
]])
|
||||
exec_lua([[
|
||||
assert(eok == false)
|
||||
assert(eok == true)
|
||||
]])
|
||||
end)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user