diff options
author | Luuk van Baal <luukvbaal@gmail.com> | 2023-10-24 13:32:00 +0200 |
---|---|---|
committer | Luuk van Baal <luukvbaal@gmail.com> | 2023-11-08 02:53:49 +0100 |
commit | 4e6f559b8c5f77924fdbe2e5abd9c6aa8efad13f (patch) | |
tree | 758c87fba796537921e16c49bcd2c377e77e6506 /src/nvim/extmark.c | |
parent | 324fad1e88ba38c87db446418a96fd3170b7f392 (diff) | |
download | rneovim-4e6f559b8c5f77924fdbe2e5abd9c6aa8efad13f.tar.gz rneovim-4e6f559b8c5f77924fdbe2e5abd9c6aa8efad13f.tar.bz2 rneovim-4e6f559b8c5f77924fdbe2e5abd9c6aa8efad13f.zip |
feat(extmarks): add 'invalidate' property to extmarks
Problem: No way to have extmarks automatically removed when the range it
is attached to is deleted.
Solution: Add new 'invalidate' property that will hide a mark when the
entirety of its range is deleted. When "undo_restore" is set
to false, delete the mark from the buffer instead.
Diffstat (limited to 'src/nvim/extmark.c')
-rw-r--r-- | src/nvim/extmark.c | 103 |
1 files changed, 57 insertions, 46 deletions
diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index 42ca99d706..434be75af0 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -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, |