mirror of
https://github.com/neovim/neovim.git
synced 2024-09-17 20:58:20 -04:00
Merge pull request #25767 from luukvbaal/signdel
feat(extmarks): add 'invalidate' property
This commit is contained in:
commit
1b0fd377ab
@ -2707,6 +2707,12 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {*opts})
|
||||
the extmark end position (if it exists) will be shifted in
|
||||
when new text is inserted (true for right, false for
|
||||
left). Defaults to false.
|
||||
• undo_restore : Restore the exact position of the mark if
|
||||
text around the mark was deleted and then restored by
|
||||
undo. Defaults to true.
|
||||
• invalidate : boolean that indicates whether to hide the
|
||||
extmark if the entirety of its range is deleted. If
|
||||
"undo_restore" is false, the extmark is deleted instead.
|
||||
• priority: a priority value for the highlight group or sign
|
||||
attribute. For example treesitter highlighting uses a
|
||||
value of 100.
|
||||
@ -2777,7 +2783,7 @@ nvim_set_decoration_provider({ns_id}, {*opts})
|
||||
|nvim_buf_set_extmark()| can be called to add marks on a per-window or
|
||||
per-lines basis. Use the `ephemeral` key to only use the mark for the
|
||||
current screen redraw (the callback will be called again for the next
|
||||
redraw ).
|
||||
redraw).
|
||||
|
||||
Note: this function should not be called often. Rather, the callbacks
|
||||
themselves can be used to throttle unneeded callbacks. the `on_start`
|
||||
|
@ -279,6 +279,9 @@ The following changes to existing APIs or features add new behavior.
|
||||
• Extmarks can opt-out of precise undo tracking using the new "undo_restore"
|
||||
flag to |nvim_buf_set_extmark()|
|
||||
|
||||
• Extmarks can be automatically hidden or removed using the new "invalidate"
|
||||
flag to |nvim_buf_set_extmark()|
|
||||
|
||||
• LSP hover and signature help now use Treesitter for highlighting of Markdown
|
||||
content.
|
||||
Note that syntax highlighting of code examples requires a matching parser
|
||||
|
8
runtime/lua/vim/_meta/api.lua
generated
8
runtime/lua/vim/_meta/api.lua
generated
@ -554,6 +554,12 @@ function vim.api.nvim_buf_line_count(buffer) end
|
||||
--- the extmark end position (if it exists) will be shifted in
|
||||
--- when new text is inserted (true for right, false for
|
||||
--- left). Defaults to false.
|
||||
--- • undo_restore : Restore the exact position of the mark if
|
||||
--- text around the mark was deleted and then restored by
|
||||
--- undo. Defaults to true.
|
||||
--- • invalidate : boolean that indicates whether to hide the
|
||||
--- extmark if the entirety of its range is deleted. If
|
||||
--- "undo_restore" is false, the extmark is deleted instead.
|
||||
--- • priority: a priority value for the highlight group or sign
|
||||
--- attribute. For example treesitter highlighting uses a
|
||||
--- value of 100.
|
||||
@ -1812,7 +1818,7 @@ function vim.api.nvim_set_current_win(window) end
|
||||
--- `nvim_buf_set_extmark()` can be called to add marks on a per-window or
|
||||
--- per-lines basis. Use the `ephemeral` key to only use the mark for the
|
||||
--- current screen redraw (the callback will be called again for the next
|
||||
--- redraw ).
|
||||
--- redraw).
|
||||
--- Note: this function should not be called often. Rather, the callbacks
|
||||
--- themselves can be used to throttle unneeded callbacks. the `on_start`
|
||||
--- callback can return `false` to disable the provider until the next redraw.
|
||||
|
2
runtime/lua/vim/_meta/api_keysets.lua
generated
2
runtime/lua/vim/_meta/api_keysets.lua
generated
@ -227,6 +227,7 @@ error('Cannot require a meta file')
|
||||
--- @field virt_text_hide? boolean
|
||||
--- @field hl_eol? boolean
|
||||
--- @field hl_mode? string
|
||||
--- @field invalidate? boolean
|
||||
--- @field ephemeral? boolean
|
||||
--- @field priority? integer
|
||||
--- @field right_gravity? boolean
|
||||
@ -243,6 +244,7 @@ error('Cannot require a meta file')
|
||||
--- @field conceal? string
|
||||
--- @field spell? boolean
|
||||
--- @field ui_watched? boolean
|
||||
--- @field undo_restore? boolean
|
||||
|
||||
--- @class vim.api.keyset.user_command
|
||||
--- @field addr? any
|
||||
|
@ -172,7 +172,7 @@ Integer nvim_buf_set_virtual_text(Buffer buffer, Integer src_id, Integer line, A
|
||||
decor.virt_text_width = width;
|
||||
decor.priority = 0;
|
||||
|
||||
extmark_set(buf, ns_id, NULL, (int)line, 0, -1, -1, &decor, true, false, false, NULL);
|
||||
extmark_set(buf, ns_id, NULL, (int)line, 0, -1, -1, &decor, true, false, false, false, NULL);
|
||||
return src_id;
|
||||
}
|
||||
|
||||
|
@ -170,6 +170,13 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict
|
||||
PUT(dict, "undo_restore", BOOLEAN_OBJ(false));
|
||||
}
|
||||
|
||||
if (extmark->invalidate) {
|
||||
PUT(dict, "invalidate", BOOLEAN_OBJ(true));
|
||||
}
|
||||
if (extmark->invalid) {
|
||||
PUT(dict, "invalid", BOOLEAN_OBJ(true));
|
||||
}
|
||||
|
||||
const Decoration *decor = &extmark->decor;
|
||||
if (decor->hl_id) {
|
||||
PUT(dict, "hl_group", hl_group_name(decor->hl_id, hl_name));
|
||||
@ -526,6 +533,9 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
|
||||
/// - undo_restore : Restore the exact position of the mark
|
||||
/// if text around the mark was deleted and then restored by undo.
|
||||
/// Defaults to true.
|
||||
/// - invalidate : boolean that indicates whether to hide the
|
||||
/// extmark if the entirety of its range is deleted. If
|
||||
/// "undo_restore" is false, the extmark is deleted instead.
|
||||
/// - priority: a priority value for the highlight group or sign
|
||||
/// attribute. For example treesitter highlighting uses a
|
||||
/// value of 100.
|
||||
@ -759,8 +769,6 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
||||
goto error;
|
||||
});
|
||||
|
||||
bool end_right_gravity = opts->end_right_gravity;
|
||||
|
||||
size_t len = 0;
|
||||
|
||||
if (!HAS_KEY(opts, set_extmark, spell)) {
|
||||
@ -823,7 +831,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
||||
|
||||
// TODO(bfredl): synergize these two branches even more
|
||||
if (opts->ephemeral && decor_state.win && decor_state.win->w_buffer == buf) {
|
||||
decor_add_ephemeral((int)line, (int)col, line2, col2, &decor, (uint64_t)ns_id, id);
|
||||
decor_push_ephemeral((int)line, (int)col, line2, col2, &decor, (uint64_t)ns_id, id);
|
||||
} else {
|
||||
if (opts->ephemeral) {
|
||||
api_set_error(err, kErrorTypeException, "not yet implemented");
|
||||
@ -831,8 +839,9 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
||||
}
|
||||
|
||||
extmark_set(buf, (uint32_t)ns_id, &id, (int)line, (colnr_T)col, line2, col2,
|
||||
has_decor ? &decor : NULL, right_gravity, end_right_gravity,
|
||||
!GET_BOOL_OR_TRUE(opts, set_extmark, undo_restore), err);
|
||||
has_decor ? &decor : NULL, right_gravity, opts->end_right_gravity,
|
||||
!GET_BOOL_OR_TRUE(opts, set_extmark, undo_restore),
|
||||
opts->invalidate, err);
|
||||
if (ERROR_SET(err)) {
|
||||
goto error;
|
||||
}
|
||||
@ -959,7 +968,7 @@ Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, In
|
||||
extmark_set(buf, ns, NULL,
|
||||
(int)line, (colnr_T)col_start,
|
||||
end_line, (colnr_T)col_end,
|
||||
&decor, true, false, false, NULL);
|
||||
&decor, true, false, false, false, NULL);
|
||||
return ns_id;
|
||||
}
|
||||
|
||||
@ -1010,7 +1019,7 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start,
|
||||
/// redrawn buffer. |nvim_buf_set_extmark()| can be called to add marks
|
||||
/// on a per-window or per-lines basis. Use the `ephemeral` key to only
|
||||
/// use the mark for the current screen redraw (the callback will be called
|
||||
/// again for the next redraw ).
|
||||
/// again for the next redraw).
|
||||
///
|
||||
/// Note: this function should not be called often. Rather, the callbacks
|
||||
/// themselves can be used to throttle unneeded callbacks. the `on_start`
|
||||
|
@ -32,6 +32,7 @@ typedef struct {
|
||||
Boolean virt_text_hide;
|
||||
Boolean hl_eol;
|
||||
String hl_mode;
|
||||
Boolean invalidate;
|
||||
Boolean ephemeral;
|
||||
Integer priority;
|
||||
Boolean right_gravity;
|
||||
|
@ -66,7 +66,7 @@ void bufhl_add_hl_pos_offset(buf_T *buf, int src_id, int hl_id, lpos_T pos_start
|
||||
}
|
||||
extmark_set(buf, (uint32_t)src_id, NULL,
|
||||
(int)lnum - 1, hl_start, (int)lnum - 1 + end_off, hl_end,
|
||||
&decor, true, false, true, NULL);
|
||||
&decor, true, false, true, false, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,7 +95,30 @@ void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor)
|
||||
}
|
||||
}
|
||||
|
||||
void decor_remove(buf_T *buf, int row, int row2, Decoration *decor)
|
||||
void decor_add(buf_T *buf, int row, int row2, Decoration *decor, bool hl_id)
|
||||
{
|
||||
if (decor) {
|
||||
if (kv_size(decor->virt_text) && decor->virt_text_pos == kVTInline) {
|
||||
buf->b_virt_text_inline++;
|
||||
}
|
||||
if (kv_size(decor->virt_lines)) {
|
||||
buf->b_virt_line_blocks++;
|
||||
}
|
||||
if (decor_has_sign(decor)) {
|
||||
buf->b_signs++;
|
||||
}
|
||||
if (decor->sign_text) {
|
||||
buf->b_signs_with_text++;
|
||||
// TODO(lewis6991): smarter invalidation
|
||||
buf_signcols_add_check(buf, NULL);
|
||||
}
|
||||
}
|
||||
if (decor || hl_id) {
|
||||
decor_redraw(buf, row, row2 > -1 ? row2 : row, decor);
|
||||
}
|
||||
}
|
||||
|
||||
void decor_remove(buf_T *buf, int row, int row2, Decoration *decor, bool invalidate)
|
||||
{
|
||||
decor_redraw(buf, row, row2, decor);
|
||||
if (decor) {
|
||||
@ -119,7 +142,9 @@ void decor_remove(buf_T *buf, int row, int row2, Decoration *decor)
|
||||
}
|
||||
}
|
||||
}
|
||||
decor_free(decor);
|
||||
if (!invalidate) {
|
||||
decor_free(decor);
|
||||
}
|
||||
}
|
||||
|
||||
void decor_clear(Decoration *decor)
|
||||
@ -180,7 +205,7 @@ Decoration *decor_find_virttext(buf_T *buf, int row, uint64_t ns_id)
|
||||
MTKey mark = marktree_itr_current(itr);
|
||||
if (mark.pos.row < 0 || mark.pos.row > row) {
|
||||
break;
|
||||
} else if (marktree_decor_level(mark) < kDecorLevelVisible) {
|
||||
} else if (mt_invalid(mark) || marktree_decor_level(mark) < kDecorLevelVisible) {
|
||||
goto next_mark;
|
||||
}
|
||||
Decoration *decor = mark.decor_full;
|
||||
@ -236,14 +261,14 @@ bool decor_redraw_start(win_T *wp, int top_row, DecorState *state)
|
||||
MTPair pair;
|
||||
|
||||
while (marktree_itr_step_overlap(buf->b_marktree, state->itr, &pair)) {
|
||||
if (marktree_decor_level(pair.start) < kDecorLevelVisible) {
|
||||
if (mt_invalid(pair.start) || marktree_decor_level(pair.start) < kDecorLevelVisible) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Decoration decor = get_decor(pair.start);
|
||||
|
||||
decor_add(state, pair.start.pos.row, pair.start.pos.col, pair.end_pos.row, pair.end_pos.col,
|
||||
&decor, false, pair.start.ns, pair.start.id);
|
||||
decor_push(state, pair.start.pos.row, pair.start.pos.col, pair.end_pos.row, pair.end_pos.col,
|
||||
&decor, false, pair.start.ns, pair.start.id);
|
||||
}
|
||||
|
||||
return true; // TODO(bfredl): check if available in the region
|
||||
@ -266,8 +291,8 @@ bool decor_redraw_line(win_T *wp, int row, DecorState *state)
|
||||
return (k.pos.row >= 0 && k.pos.row <= row);
|
||||
}
|
||||
|
||||
static void decor_add(DecorState *state, int start_row, int start_col, int end_row, int end_col,
|
||||
Decoration *decor, bool owned, uint64_t ns_id, uint64_t mark_id)
|
||||
static void decor_push(DecorState *state, int start_row, int start_col, int end_row, int end_col,
|
||||
Decoration *decor, bool owned, uint64_t ns_id, uint64_t mark_id)
|
||||
{
|
||||
int attr_id = decor->hl_id > 0 ? syn_id2attr(decor->hl_id) : 0;
|
||||
|
||||
@ -327,8 +352,7 @@ int decor_redraw_col(win_T *wp, int col, int win_col, bool hidden, DecorState *s
|
||||
break;
|
||||
}
|
||||
|
||||
if (mt_end(mark)
|
||||
|| marktree_decor_level(mark) < kDecorLevelVisible) {
|
||||
if (mt_invalid(mark) || mt_end(mark) || marktree_decor_level(mark) < kDecorLevelVisible) {
|
||||
goto next_mark;
|
||||
}
|
||||
|
||||
@ -339,8 +363,8 @@ int decor_redraw_col(win_T *wp, int col, int win_col, bool hidden, DecorState *s
|
||||
endpos = mark.pos;
|
||||
}
|
||||
|
||||
decor_add(state, mark.pos.row, mark.pos.col, endpos.row, endpos.col,
|
||||
&decor, false, mark.ns, mark.id);
|
||||
decor_push(state, mark.pos.row, mark.pos.col, endpos.row, endpos.col,
|
||||
&decor, false, mark.ns, mark.id);
|
||||
|
||||
next_mark:
|
||||
marktree_itr_next(buf->b_marktree, state->itr);
|
||||
@ -425,7 +449,7 @@ void decor_redraw_signs(buf_T *buf, int row, int *num_signs, SignTextAttrs sattr
|
||||
|
||||
MTPair pair;
|
||||
while (marktree_itr_step_overlap(buf->b_marktree, itr, &pair)) {
|
||||
if (marktree_decor_level(pair.start) < kDecorLevelVisible) {
|
||||
if (mt_invalid(pair.start) || marktree_decor_level(pair.start) < kDecorLevelVisible) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -444,7 +468,7 @@ void decor_redraw_signs(buf_T *buf, int row, int *num_signs, SignTextAttrs sattr
|
||||
break;
|
||||
}
|
||||
|
||||
if (mt_end(mark) || marktree_decor_level(mark) < kDecorLevelVisible) {
|
||||
if (mt_end(mark) || mt_invalid(mark) || marktree_decor_level(mark) < kDecorLevelVisible) {
|
||||
goto next_mark;
|
||||
}
|
||||
|
||||
@ -605,14 +629,14 @@ bool decor_redraw_eol(win_T *wp, DecorState *state, int *eol_attr, int eol_col)
|
||||
return has_virttext;
|
||||
}
|
||||
|
||||
void decor_add_ephemeral(int start_row, int start_col, int end_row, int end_col, Decoration *decor,
|
||||
uint64_t ns_id, uint64_t mark_id)
|
||||
void decor_push_ephemeral(int start_row, int start_col, int end_row, int end_col, Decoration *decor,
|
||||
uint64_t ns_id, uint64_t mark_id)
|
||||
{
|
||||
if (end_row == -1) {
|
||||
end_row = start_row;
|
||||
end_col = start_col;
|
||||
}
|
||||
decor_add(&decor_state, start_row, start_col, end_row, end_col, decor, true, ns_id, mark_id);
|
||||
decor_push(&decor_state, start_row, start_col, end_row, end_col, decor, true, ns_id, mark_id);
|
||||
}
|
||||
|
||||
/// @param has_fold whether line "lnum" has a fold, or kNone when not calculated yet
|
||||
|
@ -56,11 +56,12 @@
|
||||
/// 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, Error *err)
|
||||
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) {
|
||||
@ -74,10 +75,12 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col
|
||||
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);
|
||||
|
||||
if (id == 0) {
|
||||
id = ++*ns;
|
||||
@ -99,25 +102,20 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col
|
||||
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);
|
||||
decor_remove(buf, row, row, old_mark.decor_full, false);
|
||||
old_mark.decor_full = NULL;
|
||||
}
|
||||
old_mark.flags = 0;
|
||||
old_mark.flags = flags;
|
||||
if (decor_full) {
|
||||
old_mark.decor_full = decor;
|
||||
} else if (decor) {
|
||||
old_mark.hl_id = decor->hl_id;
|
||||
// Workaround: the gcc compiler of functionaltest-lua build
|
||||
// apparently incapable of handling basic integer constants.
|
||||
// This can be underanged as soon as we bump minimal gcc version.
|
||||
old_mark.flags = (uint16_t)(old_mark.flags
|
||||
| (decor->hl_eol ? (uint16_t)MT_FLAG_HL_EOL : (uint16_t)0));
|
||||
old_mark.priority = decor->priority;
|
||||
}
|
||||
marktree_revise(buf->b_marktree, itr, decor_level, old_mark);
|
||||
goto revised;
|
||||
}
|
||||
decor_remove(buf, old_mark.pos.row, old_mark.pos.row, old_mark.decor_full);
|
||||
decor_remove(buf, old_mark.pos.row, old_mark.pos.row, old_mark.decor_full, false);
|
||||
marktree_del_itr(buf->b_marktree, itr, false);
|
||||
}
|
||||
} else {
|
||||
@ -125,37 +123,18 @@ 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,
|
||||
mt_flags(right_gravity, decor_level, no_undo), 0, NULL };
|
||||
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;
|
||||
// workaround: see above
|
||||
mark.flags = (uint16_t)(mark.flags | (decor->hl_eol ? (uint16_t)MT_FLAG_HL_EOL : (uint16_t)0));
|
||||
mark.priority = decor->priority;
|
||||
}
|
||||
|
||||
marktree_put(buf->b_marktree, mark, end_row, end_col, end_right_gravity);
|
||||
|
||||
revised:
|
||||
if (decor) {
|
||||
if (kv_size(decor->virt_text) && decor->virt_text_pos == kVTInline) {
|
||||
buf->b_virt_text_inline++;
|
||||
}
|
||||
if (kv_size(decor->virt_lines)) {
|
||||
buf->b_virt_line_blocks++;
|
||||
}
|
||||
if (decor_has_sign(decor)) {
|
||||
buf->b_signs++;
|
||||
}
|
||||
if (decor->sign_text) {
|
||||
buf->b_signs_with_text++;
|
||||
// TODO(lewis6991): smarter invalidation
|
||||
buf_signcols_add_check(buf, NULL);
|
||||
}
|
||||
decor_redraw(buf, row, end_row > -1 ? end_row : row, decor);
|
||||
}
|
||||
decor_add(buf, row, end_row, decor, decor && decor->hl_id);
|
||||
|
||||
if (idp) {
|
||||
*idp = id;
|
||||
@ -215,7 +194,7 @@ linenr_T 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);
|
||||
decor_remove(buf, key.pos.row, key2.pos.row, key.decor_full, false);
|
||||
}
|
||||
|
||||
// TODO(bfredl): delete it from current undo header, opportunistically?
|
||||
@ -348,6 +327,8 @@ static void push_mark(ExtmarkInfoArray *array, uint32_t ns_id, ExtmarkType type_
|
||||
.row = mark.pos.row, .col = mark.pos.col,
|
||||
.end_row = end_pos.row,
|
||||
.end_col = end_pos.col,
|
||||
.invalidate = mt_invalidate(mark),
|
||||
.invalid = mt_invalid(mark),
|
||||
.right_gravity = mt_right(mark),
|
||||
.end_right_gravity = end_right,
|
||||
.no_undo = mt_no_undo(mark),
|
||||
@ -357,7 +338,7 @@ static void push_mark(ExtmarkInfoArray *array, uint32_t ns_id, ExtmarkType type_
|
||||
/// Lookup an extmark by id
|
||||
ExtmarkInfo extmark_from_id(buf_T *buf, uint32_t ns_id, uint32_t id)
|
||||
{
|
||||
ExtmarkInfo ret = { 0, 0, -1, -1, -1, -1, false, false, false, DECORATION_INIT };
|
||||
ExtmarkInfo ret = EXTMARKINFO_INIT;
|
||||
MTKey mark = marktree_lookup_ns(buf->b_marktree, ns_id, id, false, NULL);
|
||||
if (!mark.id) {
|
||||
return ret;
|
||||
@ -374,6 +355,8 @@ ExtmarkInfo extmark_from_id(buf_T *buf, uint32_t ns_id, uint32_t id)
|
||||
ret.right_gravity = mt_right(mark);
|
||||
ret.end_right_gravity = mt_right(end);
|
||||
ret.no_undo = mt_no_undo(mark);
|
||||
ret.invalidate = mt_invalidate(mark);
|
||||
ret.invalid = mt_invalid(mark);
|
||||
ret.decor = get_decor(mark);
|
||||
|
||||
return ret;
|
||||
@ -408,20 +391,17 @@ void extmark_free_all(buf_T *buf)
|
||||
*buf->b_extmark_ns = (Map(uint32_t, uint32_t)) MAP_INIT;
|
||||
}
|
||||
|
||||
/// copy extmarks data between range
|
||||
/// invalidate extmarks between range and copy to undo header
|
||||
///
|
||||
/// useful when we cannot simply reverse the operation. This will do nothing on
|
||||
/// redo, enforces correct position when undo.
|
||||
void u_extmark_copy(buf_T *buf, int l_row, colnr_T l_col, int u_row, colnr_T u_col)
|
||||
/// copying is useful when we cannot simply reverse the operation. This will do
|
||||
/// nothing on redo, enforces correct position when undo.
|
||||
void extmark_splice_delete(buf_T *buf, int l_row, colnr_T l_col, int u_row, colnr_T u_col,
|
||||
ExtmarkOp op)
|
||||
{
|
||||
u_header_T *uhp = u_force_get_undo_header(buf);
|
||||
if (!uhp) {
|
||||
return;
|
||||
}
|
||||
|
||||
MarkTreeIter itr[1] = { 0 };
|
||||
ExtmarkUndoObject undo;
|
||||
|
||||
MarkTreeIter itr[1] = { 0 };
|
||||
marktree_itr_get(buf->b_marktree, (int32_t)l_row, l_col, itr);
|
||||
while (true) {
|
||||
MTKey mark = marktree_itr_current(itr);
|
||||
@ -431,9 +411,33 @@ void u_extmark_copy(buf_T *buf, int l_row, colnr_T l_col, int u_row, colnr_T u_c
|
||||
break;
|
||||
}
|
||||
|
||||
if (!mt_no_undo(mark)) {
|
||||
bool invalidated = false;
|
||||
// Invalidate/delete mark
|
||||
if (!mt_invalid(mark) && mt_invalidate(mark) && !mt_end(mark)) {
|
||||
MTPos endpos = marktree_get_altpos(buf->b_marktree, mark, NULL);
|
||||
if (endpos.row < 0) {
|
||||
endpos = mark.pos;
|
||||
}
|
||||
if ((endpos.col <= u_col || (!u_col && endpos.row == mark.pos.row))
|
||||
&& mark.pos.col >= l_col
|
||||
&& mark.pos.row >= l_row && endpos.row <= u_row - (u_col ? 0 : 1)) {
|
||||
if (mt_no_undo(mark)) {
|
||||
extmark_del(buf, itr, mark, true);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Push mark to undo header
|
||||
if (uhp && op == kExtmarkUndo && !mt_no_undo(mark)) {
|
||||
ExtmarkSavePos pos;
|
||||
pos.mark = mt_lookup_key(mark);
|
||||
pos.invalidated = invalidated;
|
||||
pos.old_row = mark.pos.row;
|
||||
pos.old_col = mark.pos.col;
|
||||
pos.row = -1;
|
||||
@ -472,6 +476,14 @@ void extmark_apply_undo(ExtmarkUndoObject undo_info, bool undo)
|
||||
} else if (undo_info.type == kExtmarkSavePos) {
|
||||
ExtmarkSavePos pos = undo_info.data.savepos;
|
||||
if (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);
|
||||
}
|
||||
if (pos.old_row >= 0) {
|
||||
extmark_setraw(curbuf, pos.mark, pos.old_row, pos.old_col);
|
||||
}
|
||||
@ -513,7 +525,6 @@ void extmark_adjust(buf_T *buf, linenr_T line1, linenr_T line2, linenr_T amount,
|
||||
old_row = line2 - line1 + 1;
|
||||
// TODO(bfredl): ej kasta?
|
||||
old_byte = (bcount_t)buf->deleted_bytes2;
|
||||
|
||||
new_row = amount_after + old_row;
|
||||
} else {
|
||||
// A region is either deleted (amount == MAXLNUM) or
|
||||
@ -579,15 +590,15 @@ void extmark_splice_impl(buf_T *buf, int start_row, colnr_T start_col, bcount_t
|
||||
old_row, old_col, old_byte,
|
||||
new_row, new_col, new_byte);
|
||||
|
||||
if (undo == kExtmarkUndo && (old_row > 0 || old_col > 0)) {
|
||||
// Copy marks that would be effected by delete
|
||||
if (old_row > 0 || old_col > 0) {
|
||||
// Copy and invalidate marks that would be effected by delete
|
||||
// TODO(bfredl): Be "smart" about gravity here, left-gravity at the
|
||||
// beginning and right-gravity at the end need not be preserved.
|
||||
// Also be smart about marks that already have been saved (important for
|
||||
// merge!)
|
||||
int end_row = start_row + old_row;
|
||||
int end_col = (old_row ? 0 : start_col) + old_col;
|
||||
u_extmark_copy(buf, start_row, start_col, end_row, end_col);
|
||||
extmark_splice_delete(buf, start_row, start_col, end_row, end_col, undo);
|
||||
}
|
||||
|
||||
marktree_splice(buf->b_marktree, (int32_t)start_row, start_col,
|
||||
|
@ -25,9 +25,13 @@ typedef struct {
|
||||
colnr_T end_col;
|
||||
bool right_gravity;
|
||||
bool end_right_gravity;
|
||||
bool invalidate;
|
||||
bool invalid;
|
||||
bool no_undo;
|
||||
Decoration decor; // TODO(bfredl): CHONKY
|
||||
} ExtmarkInfo;
|
||||
#define EXTMARKINFO_INIT { 0, 0, -1, -1, -1, -1, false, false, false, false, false, \
|
||||
DECORATION_INIT };
|
||||
|
||||
typedef kvec_t(ExtmarkInfo) ExtmarkInfoArray;
|
||||
|
||||
@ -67,6 +71,7 @@ typedef struct {
|
||||
colnr_T old_col;
|
||||
int row;
|
||||
colnr_T col;
|
||||
bool invalidated;
|
||||
} ExtmarkSavePos;
|
||||
|
||||
typedef enum {
|
||||
|
@ -1146,9 +1146,14 @@ void marktree_revise(MarkTree *b, MarkTreeIter *itr, uint8_t decor_level, MTKey
|
||||
// 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_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;
|
||||
@ -2006,8 +2011,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)
|
||||
{
|
||||
MTKey key = { { row, col }, ns, id, 0,
|
||||
mt_flags(right_gravity, 0, false), 0, NULL };
|
||||
uint16_t flags = mt_flags(right_gravity, false, false, false, 0);
|
||||
MTKey key = { { row, col }, ns, id, 0, flags, 0, NULL };
|
||||
marktree_put(b, key, end_row, end_col, end_right);
|
||||
}
|
||||
|
||||
|
@ -78,17 +78,19 @@ typedef struct {
|
||||
#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 DECOR_LEVELS 4
|
||||
#define MT_FLAG_DECOR_OFFSET 6
|
||||
#define MT_FLAG_DECOR_OFFSET 8
|
||||
#define MT_FLAG_DECOR_MASK (((uint16_t)(DECOR_LEVELS - 1)) << MT_FLAG_DECOR_OFFSET)
|
||||
|
||||
// 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_NO_UNDO | MT_FLAG_HL_EOL)
|
||||
#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)
|
||||
|
||||
// this is defined so that start and end of the same range have adjacent ids
|
||||
#define MARKTREE_END_FLAG ((uint64_t)1)
|
||||
@ -132,17 +134,30 @@ static inline bool mt_no_undo(MTKey key)
|
||||
return key.flags & MT_FLAG_NO_UNDO;
|
||||
}
|
||||
|
||||
static inline bool mt_invalidate(MTKey key)
|
||||
{
|
||||
return key.flags & MT_FLAG_INVALIDATE;
|
||||
}
|
||||
|
||||
static inline bool mt_invalid(MTKey key)
|
||||
{
|
||||
return key.flags & MT_FLAG_INVALID;
|
||||
}
|
||||
|
||||
static inline uint8_t marktree_decor_level(MTKey key)
|
||||
{
|
||||
return (uint8_t)((key.flags&MT_FLAG_DECOR_MASK) >> MT_FLAG_DECOR_OFFSET);
|
||||
}
|
||||
|
||||
static inline uint16_t mt_flags(bool right_gravity, uint8_t decor_level, bool no_undo)
|
||||
static inline uint16_t mt_flags(bool right_gravity, bool hl_eol, bool no_undo, bool invalidate,
|
||||
uint8_t decor_level)
|
||||
{
|
||||
assert(decor_level < DECOR_LEVELS);
|
||||
return (uint16_t)((right_gravity ? MT_FLAG_RIGHT_GRAVITY : 0)
|
||||
| (decor_level << MT_FLAG_DECOR_OFFSET)
|
||||
| (no_undo ? MT_FLAG_NO_UNDO : 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));
|
||||
}
|
||||
|
||||
typedef kvec_withinit_t(uint64_t, 4) Intersection;
|
||||
|
@ -1599,12 +1599,70 @@ describe('API/extmarks', function()
|
||||
set_extmark(ns, 3, 0, 0, { sign_text = '>>' })
|
||||
set_extmark(ns, 4, 0, 0, { virt_text = {{'text', 'Normal'}}})
|
||||
set_extmark(ns, 5, 0, 0, { virt_lines = {{{ 'line', 'Normal' }}}})
|
||||
eq(5, #get_extmarks(-1, 0, -1, { details = true }))
|
||||
eq(5, #get_extmarks(-1, 0, -1, {}))
|
||||
eq({{ 2, 0, 0 }}, get_extmarks(-1, 0, -1, { type = 'highlight' }))
|
||||
eq({{ 3, 0, 0 }}, get_extmarks(-1, 0, -1, { type = 'sign' }))
|
||||
eq({{ 4, 0, 0 }}, get_extmarks(-1, 0, -1, { type = 'virt_text' }))
|
||||
eq({{ 5, 0, 0 }}, get_extmarks(-1, 0, -1, { type = 'virt_lines' }))
|
||||
end)
|
||||
|
||||
it("invalidated marks are deleted", function()
|
||||
screen = Screen.new(40, 6)
|
||||
screen:attach()
|
||||
feed('dd6iaaa bbb ccc<CR><ESC>gg')
|
||||
set_extmark(ns, 1, 0, 0, { invalidate = true, sign_text = 'S1' })
|
||||
set_extmark(ns, 2, 1, 0, { invalidate = true, sign_text = 'S2' })
|
||||
-- mark with invalidate is removed
|
||||
command('d')
|
||||
screen:expect([[
|
||||
S2^aaa bbb ccc |
|
||||
aaa bbb ccc |
|
||||
aaa bbb ccc |
|
||||
aaa bbb ccc |
|
||||
aaa bbb ccc |
|
||||
|
|
||||
]])
|
||||
-- mark is restored with undo_restore == true
|
||||
command('silent undo')
|
||||
screen:expect([[
|
||||
S1^aaa bbb ccc |
|
||||
S2aaa bbb ccc |
|
||||
aaa bbb ccc |
|
||||
aaa bbb ccc |
|
||||
aaa bbb ccc |
|
||||
|
|
||||
]])
|
||||
-- mark is deleted with undo_restore == false
|
||||
set_extmark(ns, 1, 0, 0, { invalidate = true, undo_restore = false, sign_text = 'S1' })
|
||||
set_extmark(ns, 2, 1, 0, { invalidate = true, undo_restore = false, sign_text = 'S2' })
|
||||
command('1d 2')
|
||||
eq(0, #get_extmarks(-1, 0, -1, {}))
|
||||
-- mark is not removed when deleting bytes before the range
|
||||
set_extmark(ns, 3, 0, 4, { invalidate = true, undo_restore = false,
|
||||
hl_group = 'Error', end_col = 7 })
|
||||
feed('dw')
|
||||
eq(3, get_extmark_by_id(ns, 3, { details = true })[3].end_col)
|
||||
-- mark is not removed when deleting bytes at the start of the range
|
||||
feed('x')
|
||||
eq(2, get_extmark_by_id(ns, 3, { details = true })[3].end_col)
|
||||
-- mark is not removed when deleting bytes from the end of the range
|
||||
feed('lx')
|
||||
eq(1, get_extmark_by_id(ns, 3, { details = true})[3].end_col)
|
||||
-- mark is not removed when deleting bytes beyond end of the range
|
||||
feed('x')
|
||||
eq(1, get_extmark_by_id(ns, 3, { details = true})[3].end_col)
|
||||
-- mark is removed when all bytes in the range are deleted
|
||||
feed('hx')
|
||||
eq({}, get_extmark_by_id(ns, 3, {}))
|
||||
-- multiline mark is not removed when start of its range is deleted
|
||||
set_extmark(ns, 4, 1, 4, { undo_restore = false, invalidate = true,
|
||||
hl_group = 'Error', end_col = 7, end_row = 3 })
|
||||
feed('ddDdd')
|
||||
eq({0, 0}, get_extmark_by_id(ns, 4, {}))
|
||||
-- multiline mark is removed when entirety of its range is deleted
|
||||
feed('vj2ed')
|
||||
eq({}, get_extmark_by_id(ns, 4, {}))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('Extmarks buffer api with many marks', function()
|
||||
|
Loading…
Reference in New Issue
Block a user