mirror of
https://github.com/neovim/neovim.git
synced 2024-12-24 13:15:09 -07:00
Merge pull request #25826 from bfredl/art_deco
refactor(decorations): break up Decoration struct into smaller pieces
This commit is contained in:
commit
2e722da44d
@ -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