diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2023-11-29 22:39:54 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2023-11-29 22:39:54 +0000 |
commit | 21cb7d04c387e4198ca8098a884c78b56ffcf4c2 (patch) | |
tree | 84fe5690df1551f0bb2bdfe1a13aacd29ebc1de7 /src/nvim/extmark.c | |
parent | d9c904f85a23a496df4eb6be42aa43f007b22d50 (diff) | |
parent | 4a8bf24ac690004aedf5540fa440e788459e5e34 (diff) | |
download | rneovim-colorcolchar.tar.gz rneovim-colorcolchar.tar.bz2 rneovim-colorcolchar.zip |
Merge remote-tracking branch 'upstream/master' into colorcolcharcolorcolchar
Diffstat (limited to 'src/nvim/extmark.c')
-rw-r--r-- | src/nvim/extmark.c | 405 |
1 files changed, 170 insertions, 235 deletions
diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index 3e059bcc6c..d9c1993f32 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -1,6 +1,3 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - // Implements extended marks for plugins. Marks sit in a MarkTree // datastructure which provides both efficient mark insertations/lookups // and adjustment to text changes. See marktree.c for more details. @@ -29,91 +26,58 @@ // code for redrawing the line with the deleted decoration. #include <assert.h> -#include <sys/types.h> +#include <stddef.h> -#include "nvim/buffer.h" +#include "nvim/api/private/defs.h" #include "nvim/buffer_defs.h" #include "nvim/buffer_updates.h" #include "nvim/decoration.h" #include "nvim/extmark.h" #include "nvim/extmark_defs.h" #include "nvim/globals.h" -#include "nvim/map.h" +#include "nvim/map_defs.h" #include "nvim/marktree.h" #include "nvim/memline.h" -#include "nvim/memory.h" -#include "nvim/pos.h" +#include "nvim/pos_defs.h" #include "nvim/undo.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "extmark.c.generated.h" #endif -static uint32_t *buf_ns_ref(buf_T *buf, uint32_t ns_id, bool put) -{ - return map_ref(uint32_t, uint32_t)(buf->b_extmark_ns, ns_id, put); -} - /// Create or update an extmark /// /// 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, - ExtmarkOp op) + 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 = buf_ns_ref(buf, ns_id, true); + 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; - - 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 - if (kv_size(decor->virt_lines)) { - decor_level = kDecorLevelVirtLine; // decor affects horizontal size - } - } + 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_t old_mark = marktree_lookup_ns(buf->b_marktree, ns_id, id, false, itr); + MTKey old_mark = marktree_lookup_ns(buf->b_marktree, ns_id, id, false, itr); if (old_mark.id) { if (mt_paired(old_mark) || end_row > -1) { - extmark_del(buf, ns_id, id); + extmark_del_id(buf, ns_id, id); } else { - // TODO(bfredl): we need to do more if "revising" a decoration mark. - assert(itr->node); + 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); - old_mark.decor_full = NULL; - } - old_mark.flags = 0; - 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; + if (mt_decor_any(old_mark)) { + buf_decor_remove(buf, row, row, mt_decor(old_mark), true); } - 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); + 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 { @@ -121,40 +85,13 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col } } - mtkey_t mark = { { row, col }, ns_id, id, 0, - mt_flags(right_gravity, decor_level), 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; - } + MTKey mark = { { row, col }, ns_id, id, flags, decor.data }; marktree_put(buf->b_marktree, mark, end_row, end_col, end_right_gravity); revised: - if (op != kExtmarkNoUndo) { - // TODO(bfredl): this doesn't cover all the cases and probably shouldn't - // be done "prematurely". Any movement in undo history might necessitate - // adding new marks to old undo headers. add a test case for this (doesn't - // fail extmark_spec.lua, and it should) - uint64_t mark_id = mt_lookup_id(ns_id, id, false); - u_extmark_set(buf, mark_id, row, col); - } - - if (decor) { - if (kv_size(decor->virt_lines)) { - buf->b_virt_line_blocks++; - } - if (decor_has_sign(decor)) { - buf->b_signs++; - } - if (decor->sign_text) { - // TODO(lewis6991): smarter invalidation - buf_signcols_add_check(buf, NULL); - } + if (decor_flags || decor.ext) { + buf_put_decor(buf, decor, row, end_row > -1 ? end_row : row); decor_redraw(buf, row, end_row > -1 ? end_row : row, decor); } @@ -166,7 +103,7 @@ revised: static bool extmark_setraw(buf_T *buf, uint64_t mark, int row, colnr_T col) { MarkTreeIter itr[1] = { 0 }; - mtkey_t key = marktree_lookup(buf->b_marktree, mark, itr); + MTKey key = marktree_lookup(buf->b_marktree, mark, itr); if (key.pos.row == -1) { return false; } @@ -179,33 +116,41 @@ static bool extmark_setraw(buf_T *buf, uint64_t mark, int row, colnr_T col) return true; } -/// Remove an extmark +/// Remove an extmark in "ns_id" by "id" /// -/// @return 0 on missing id -bool extmark_del(buf_T *buf, uint32_t ns_id, uint32_t id) +/// @return false on missing id +bool extmark_del_id(buf_T *buf, uint32_t ns_id, uint32_t id) { MarkTreeIter itr[1] = { 0 }; - mtkey_t key = marktree_lookup_ns(buf->b_marktree, ns_id, id, false, itr); - if (!key.id) { - return false; + MTKey key = marktree_lookup_ns(buf->b_marktree, ns_id, id, false, itr); + if (key.id) { + extmark_del(buf, itr, key, false); } - assert(key.pos.row >= 0); - marktree_del_itr(buf->b_marktree, itr, false); - mtkey_t key2 = key; + return key.id > 0; +} + +/// Remove a (paired) extmark "key" pointed to by "itr" +void extmark_del(buf_T *buf, MarkTreeIter *itr, MTKey key, bool restore) +{ + assert(key.pos.row >= 0); - if (mt_paired(key)) { - key2 = marktree_lookup_ns(buf->b_marktree, ns_id, id, true, itr); + MTKey key2 = key; + uint64_t other = marktree_del_itr(buf->b_marktree, itr, false); + if (other) { + key2 = marktree_lookup(buf->b_marktree, other, itr); assert(key2.pos.row >= 0); marktree_del_itr(buf->b_marktree, itr, false); + if (restore) { + marktree_itr_get(buf->b_marktree, key.pos.row, key.pos.col, itr); + } } - if (marktree_decor_level(key) > kDecorLevelNone) { - decor_remove(buf, key.pos.row, key2.pos.row, key.decor_full); + 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? - return true; } /// Free extmarks in a ns between lines @@ -216,79 +161,33 @@ bool extmark_clear(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_r return false; } - bool marks_cleared = false; - bool all_ns = (ns_id == 0); uint32_t *ns = NULL; if (!all_ns) { - ns = buf_ns_ref(buf, ns_id, false); + ns = map_ref(uint32_t, uint32_t)(buf->b_extmark_ns, ns_id, NULL); if (!ns) { // nothing to do return false; } } - // the value is either zero or the lnum (row+1) if highlight was present. - static Map(uint64_t, ssize_t) delete_set = MAP_INIT; - typedef struct { int row1; } DecorItem; - static kvec_t(DecorItem) decors; - + bool marks_cleared = false; MarkTreeIter itr[1] = { 0 }; marktree_itr_get(buf->b_marktree, l_row, l_col, itr); while (true) { - mtkey_t mark = marktree_itr_current(itr); + MTKey mark = marktree_itr_current(itr); if (mark.pos.row < 0 || mark.pos.row > u_row || (mark.pos.row == u_row && mark.pos.col > u_col)) { break; } - ssize_t *del_status = map_ref(uint64_t, ssize_t)(&delete_set, mt_lookup_key(mark), - false); - if (del_status) { - marktree_del_itr(buf->b_marktree, itr, false); - if (*del_status >= 0) { // we had a decor_id - DecorItem it = kv_A(decors, *del_status); - decor_remove(buf, it.row1, mark.pos.row, mark.decor_full); - } - map_del(uint64_t, ssize_t)(&delete_set, mt_lookup_key(mark)); - continue; - } - - assert(mark.ns > 0 && mark.id > 0); if (mark.ns == ns_id || all_ns) { marks_cleared = true; - if (mt_paired(mark)) { - uint64_t other = mt_lookup_id(mark.ns, mark.id, !mt_end(mark)); - ssize_t decor_id = -1; - if (marktree_decor_level(mark) > kDecorLevelNone) { - // Save the decoration and the first pos. Clear the decoration - // later when we know the full range. - decor_id = (ssize_t)kv_size(decors); - kv_push(decors, - ((DecorItem) { .row1 = mark.pos.row })); - } - map_put(uint64_t, ssize_t)(&delete_set, other, decor_id); - } else if (mark.decor_full) { - decor_remove(buf, mark.pos.row, mark.pos.row, mark.decor_full); - } - marktree_del_itr(buf->b_marktree, itr, false); + extmark_del(buf, itr, mark, true); } else { marktree_itr_next(buf->b_marktree, itr); } } - uint64_t id; - ssize_t decor_id; - map_foreach(&delete_set, id, decor_id, { - mtkey_t mark = marktree_lookup(buf->b_marktree, id, itr); - assert(itr->node); - marktree_del_itr(buf->b_marktree, itr, false); - if (decor_id >= 0) { - DecorItem it = kv_A(decors, decor_id); - decor_remove(buf, it.row1, mark.pos.row, mark.decor_full); - } - }); - map_clear(uint64_t, ssize_t)(&delete_set); - kv_size(decors) = 0; return marks_cleared; } @@ -297,19 +196,34 @@ bool extmark_clear(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_r /// /// if upper_lnum or upper_col are negative the buffer /// will be searched to the start, or end -/// dir can be set to control the order of the array -/// amount = amount of marks to find or -1 for all +/// reverse can be set to control the order of the array +/// amount = amount of marks to find or INT64_MAX for all ExtmarkInfoArray extmark_get(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_row, - colnr_T u_col, int64_t amount, bool reverse) + colnr_T u_col, int64_t amount, bool reverse, ExtmarkType type_filter, + bool overlap) { ExtmarkInfoArray array = KV_INITIAL_VALUE; MarkTreeIter itr[1]; - // Find all the marks - marktree_itr_get_ext(buf->b_marktree, (mtpos_t){ l_row, l_col }, - itr, reverse, false, NULL); + + if (overlap) { + // Find all the marks overlapping the start position + if (!marktree_itr_get_overlap(buf->b_marktree, l_row, l_col, itr)) { + return array; + } + + MTPair pair; + while (marktree_itr_step_overlap(buf->b_marktree, itr, &pair)) { + push_mark(&array, ns_id, type_filter, pair); + } + } else { + // Find all the marks beginning with the start position + marktree_itr_get_ext(buf->b_marktree, MTPos(l_row, l_col), + itr, reverse, false, NULL); + } + int order = reverse ? -1 : 1; while ((int64_t)kv_size(array) < amount) { - mtkey_t mark = marktree_itr_current(itr); + MTKey mark = marktree_itr_current(itr); if (mark.pos.row < 0 || (mark.pos.row - u_row) * order > 0 || (mark.pos.row == u_row && (mark.pos.col - u_col) * order > 0)) { @@ -319,17 +233,8 @@ ExtmarkInfoArray extmark_get(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_co goto next_mark; } - if (mark.ns == ns_id) { - mtkey_t end = marktree_get_alt(buf->b_marktree, mark, NULL); - kv_push(array, ((ExtmarkInfo) { .ns_id = mark.ns, - .mark_id = mark.id, - .row = mark.pos.row, .col = mark.pos.col, - .end_row = end.pos.row, - .end_col = end.pos.col, - .right_gravity = mt_right(mark), - .end_right_gravity = mt_right(end), - .decor = get_decor(mark) })); - } + MTKey end = marktree_get_alt(buf->b_marktree, mark, NULL); + push_mark(&array, ns_id, type_filter, mtpair_from(mark, end)); next_mark: if (reverse) { marktree_itr_prev(buf->b_marktree, itr); @@ -340,28 +245,36 @@ next_mark: return array; } +static void push_mark(ExtmarkInfoArray *array, uint32_t ns_id, ExtmarkType type_filter, MTPair mark) +{ + if (!(ns_id == UINT32_MAX || mark.start.ns == ns_id)) { + return; + } + if (type_filter != kExtmarkNone) { + if (!mt_decor_any(mark.start)) { + return; + } + uint16_t type_flags = decor_type_flags(mt_decor(mark.start)); + + if (!(type_flags & type_filter)) { + return; + } + } + + kv_push(*array, mark); +} + /// Lookup an extmark by id -ExtmarkInfo extmark_from_id(buf_T *buf, uint32_t ns_id, uint32_t id) +MTPair extmark_from_id(buf_T *buf, uint32_t ns_id, uint32_t id) { - ExtmarkInfo ret = { 0, 0, -1, -1, -1, -1, false, false, DECORATION_INIT }; - mtkey_t mark = marktree_lookup_ns(buf->b_marktree, ns_id, id, false, NULL); + MTKey mark = marktree_lookup_ns(buf->b_marktree, ns_id, id, false, NULL); if (!mark.id) { - return ret; + return mtpair_from(mark, mark); // invalid } assert(mark.pos.row >= 0); - mtkey_t end = marktree_get_alt(buf->b_marktree, mark, NULL); - - ret.ns_id = ns_id; - ret.mark_id = id; - ret.row = mark.pos.row; - ret.col = mark.pos.col; - ret.end_row = end.pos.row; - ret.end_col = end.pos.col; - ret.right_gravity = mt_right(mark); - ret.end_right_gravity = mt_right(end); - ret.decor = get_decor(mark); - - return ret; + MTKey end = marktree_get_alt(buf->b_marktree, mark, NULL); + + return mtpair_from(mark, end); } /// free extmarks from the buffer @@ -374,14 +287,14 @@ void extmark_free_all(buf_T *buf) MarkTreeIter itr[1] = { 0 }; marktree_itr_get(buf->b_marktree, 0, 0, itr); while (true) { - mtkey_t mark = marktree_itr_current(itr); + MTKey mark = marktree_itr_current(itr); if (mark.pos.row < 0) { 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); @@ -389,63 +302,65 @@ void extmark_free_all(buf_T *buf) marktree_clear(buf->b_marktree); - map_destroy(uint32_t, uint32_t)(buf->b_extmark_ns); - map_init(uint32_t, uint32_t, buf->b_extmark_ns); + map_destroy(uint32_t, buf->b_extmark_ns); + *buf->b_extmark_ns = (Map(uint32_t, uint32_t)) MAP_INIT; } -/// Save info for undo/redo of set marks -static void u_extmark_set(buf_T *buf, uint64_t mark, int row, colnr_T col) -{ - u_header_T *uhp = u_force_get_undo_header(buf); - if (!uhp) { - return; - } - - ExtmarkSavePos pos; - pos.mark = mark; - pos.old_row = -1; - pos.old_col = -1; - pos.row = row; - pos.col = col; - - ExtmarkUndoObject undo = { .type = kExtmarkSavePos, - .data.savepos = pos }; - - kv_push(uhp->uh_extmark, undo); -} - -/// 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_t mark = marktree_itr_current(itr); + MTKey mark = marktree_itr_current(itr); if (mark.pos.row < 0 || mark.pos.row > u_row || (mark.pos.row == u_row && mark.pos.col > u_col)) { break; } - ExtmarkSavePos pos; - pos.mark = mt_lookup_key(mark); - pos.old_row = mark.pos.row; - pos.old_col = mark.pos.col; - pos.row = -1; - pos.col = -1; - undo.data.savepos = pos; - undo.type = kExtmarkSavePos; - kv_push(uhp->uh_extmark, undo); + 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; + mt_itr_rawkey(itr).flags |= MT_FLAG_INVALID; + buf_decor_remove(buf, mark.pos.row, endpos.row, mt_decor(mark), false); + } + } + } + + // 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; + pos.col = -1; + + undo.data.savepos = pos; + undo.type = kExtmarkSavePos; + kv_push(uhp->uh_extmark, undo); + } marktree_itr_next(buf->b_marktree, itr); } @@ -475,6 +390,13 @@ 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); + mt_itr_rawkey(itr).flags &= (uint16_t) ~MT_FLAG_INVALID; + MTPos end = marktree_get_altpos(curbuf->b_marktree, mark, itr); + buf_put_decor(curbuf, mt_decor(mark), mark.pos.row, end.row < 0 ? mark.pos.row : end.row); + } if (pos.old_row >= 0) { extmark_setraw(curbuf, pos.mark, pos.old_row, pos.old_col); } @@ -516,7 +438,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 @@ -557,7 +478,7 @@ void extmark_splice(buf_T *buf, int start_row, colnr_T start_col, int old_row, c bcount_t old_byte, int new_row, colnr_T new_col, bcount_t new_byte, ExtmarkOp undo) { - long offset = ml_find_line_or_offset(buf, start_row + 1, NULL, true); + int offset = ml_find_line_or_offset(buf, start_row + 1, NULL, true); // On empty buffers, when editing the first line, the line is buffered, // causing offset to be < 0. While the buffer is not actually empty, the @@ -582,15 +503,29 @@ 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); + } + + // Move the signcolumn sentinel line + if (buf->b_signs_with_text && buf->b_signcols.sentinel) { + linenr_T se_lnum = buf->b_signcols.sentinel; + if (se_lnum >= start_row) { + if (old_row != 0 && se_lnum > old_row + start_row) { + buf->b_signcols.sentinel += new_row - old_row; + } else if (new_row == 0) { + buf->b_signcols.sentinel = 0; + } else { + buf->b_signcols.sentinel += new_row; + } + } } marktree_splice(buf->b_marktree, (int32_t)start_row, start_col, |