diff options
Diffstat (limited to 'src/nvim/extmark.c')
-rw-r--r-- | src/nvim/extmark.c | 365 |
1 files changed, 176 insertions, 189 deletions
diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index c4d8f75a21..1639f72990 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -48,28 +48,32 @@ # include "extmark.c.generated.h" #endif -static ExtmarkNs *buf_ns_ref(buf_T *buf, uint64_t ns_id, bool put) +static uint32_t *buf_ns_ref(buf_T *buf, uint32_t ns_id, bool put) { - return map_ref(uint64_t, ExtmarkNs)(buf->b_extmark_ns, ns_id, 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! -/// @returns the internal mark id -uint64_t extmark_set(buf_T *buf, uint64_t ns_id, uint64_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) +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) { - ExtmarkNs *ns = buf_ns_ref(buf, ns_id, true); - assert(ns != NULL); - mtpos_t old_pos; - uint64_t mark = 0; - uint64_t id = idp ? *idp : 0; + uint32_t *ns = buf_ns_ref(buf, ns_id, true); + 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_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 @@ -77,74 +81,94 @@ uint64_t extmark_set(buf_T *buf, uint64_t ns_id, uint64_t *idp, int row, colnr_T } if (id == 0) { - id = ns->free_id++; + id = ++*ns; } else { - uint64_t old_mark = map_get(uint64_t, uint64_t)(ns->map, id); - if (old_mark) { - if (old_mark & MARKTREE_PAIRED_FLAG || end_row > -1) { + MarkTreeIter itr[1] = { 0 }; + mtkey_t 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); } else { - MarkTreeIter itr[1] = { 0 }; - old_pos = marktree_lookup(buf->b_marktree, old_mark, itr); + // TODO(bfredl): we need to do more if "revising" a decoration mark. assert(itr->node); - if (old_pos.row == row && old_pos.col == col) { - ExtmarkItem it = map_del(uint64_t, ExtmarkItem)(buf->b_extmark_index, - old_mark); - if (it.decor) { - decor_remove(buf, row, row, it.decor); + 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; } - mark = marktree_revise(buf->b_marktree, itr, decor_level); + marktree_revise(buf->b_marktree, itr, decor_level, old_mark); goto revised; } marktree_del_itr(buf->b_marktree, itr, false); } } else { - ns->free_id = MAX(ns->free_id, id+1); + *ns = MAX(*ns, id); } } - if (end_row > -1) { - mark = marktree_put_pair(buf->b_marktree, - row, col, right_gravity, - end_row, end_col, end_right_gravity, decor_level); - } else { - mark = marktree_put(buf->b_marktree, row, col, right_gravity, decor_level); + 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; } -revised: - map_put(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark, - (ExtmarkItem){ ns_id, id, decor }); - map_put(uint64_t, uint64_t)(ns->map, id, mark); + 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. - u_extmark_set(buf, mark, row, col); + // 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); + } decor_redraw(buf, row, end_row > -1 ? end_row : row, decor); } if (idp) { *idp = id; } - return mark; } static bool extmark_setraw(buf_T *buf, uint64_t mark, int row, colnr_T col) { MarkTreeIter itr[1] = { 0 }; - mtpos_t pos = marktree_lookup(buf->b_marktree, mark, itr); - if (pos.row == -1) { + mtkey_t key = marktree_lookup(buf->b_marktree, mark, itr); + if (key.pos.row == -1) { return false; } - if (pos.row == row && pos.col == col) { + if (key.pos.row == row && key.pos.col == col) { return true; } @@ -152,47 +176,38 @@ static bool extmark_setraw(buf_T *buf, uint64_t mark, int row, colnr_T col) return true; } -// Remove an extmark -// Returns 0 on missing id -bool extmark_del(buf_T *buf, uint64_t ns_id, uint64_t id) +/// Remove an extmark +/// +/// @return 0 on missing id +bool extmark_del(buf_T *buf, uint32_t ns_id, uint32_t id) { - ExtmarkNs *ns = buf_ns_ref(buf, ns_id, false); - if (!ns) { - return false; - } - - uint64_t mark = map_get(uint64_t, uint64_t)(ns->map, id); - if (!mark) { + MarkTreeIter itr[1] = { 0 }; + mtkey_t key = marktree_lookup_ns(buf->b_marktree, ns_id, id, false, itr); + if (!key.id) { return false; } - - MarkTreeIter itr[1] = { 0 }; - mtpos_t pos = marktree_lookup(buf->b_marktree, mark, itr); - assert(pos.row >= 0); + assert(key.pos.row >= 0); marktree_del_itr(buf->b_marktree, itr, false); - ExtmarkItem item = map_get(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark); - mtpos_t pos2 = pos; - if (mark & MARKTREE_PAIRED_FLAG) { - pos2 = marktree_lookup(buf->b_marktree, mark|MARKTREE_END_FLAG, itr); - assert(pos2.row >= 0); + mtkey_t key2 = key; + + if (mt_paired(key)) { + key2 = marktree_lookup_ns(buf->b_marktree, ns_id, id, true, itr); + assert(key2.pos.row >= 0); marktree_del_itr(buf->b_marktree, itr, false); } - if (item.decor) { - decor_remove(buf, pos.row, pos2.row, item.decor); + if (marktree_decor_level(key) > kDecorLevelNone) { + decor_remove(buf, key.pos.row, key2.pos.row, key.decor_full); } - map_del(uint64_t, uint64_t)(ns->map, id); - map_del(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark); - // TODO(bfredl): delete it from current undo header, opportunistically? return true; } -// Free extmarks in a ns between lines -// if ns = 0, it means clear all namespaces -bool extmark_clear(buf_T *buf, uint64_t ns_id, int l_row, colnr_T l_col, int u_row, colnr_T u_col) +/// Free extmarks in a ns between lines +/// if ns = 0, it means clear all namespaces +bool extmark_clear(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_row, colnr_T u_col) { if (!map_size(buf->b_extmark_ns)) { return false; @@ -201,68 +216,58 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, int l_row, colnr_T l_col, int u_r bool marks_cleared = false; bool all_ns = (ns_id == 0); - ExtmarkNs *ns = NULL; + uint32_t *ns = NULL; if (!all_ns) { ns = buf_ns_ref(buf, ns_id, false); if (!ns) { // nothing to do return false; } - - // TODO(bfredl): if map_size(ns->map) << buf->b_marktree.n_nodes - // it could be faster to iterate over the map instead } // 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 { Decoration *decor; int row1; } DecorItem; + typedef struct { int row1; } DecorItem; static kvec_t(DecorItem) decors; MarkTreeIter itr[1] = { 0 }; marktree_itr_get(buf->b_marktree, l_row, l_col, itr); while (true) { - mtmark_t mark = marktree_itr_current(itr); - if (mark.row < 0 - || mark.row > u_row - || (mark.row == u_row && mark.col > u_col)) { + mtkey_t 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, mark.id, + 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.row, it.decor); + decor_remove(buf, it.row1, mark.pos.row, mark.decor_full); } - map_del(uint64_t, ssize_t)(&delete_set, mark.id); + map_del(uint64_t, ssize_t)(&delete_set, mt_lookup_key(mark)); continue; } - uint64_t start_id = mark.id & ~MARKTREE_END_FLAG; - ExtmarkItem item = map_get(uint64_t, ExtmarkItem)(buf->b_extmark_index, - start_id); - - assert(item.ns_id > 0 && item.mark_id > 0); - if (item.mark_id > 0 && (item.ns_id == ns_id || all_ns)) { + assert(mark.ns > 0 && mark.id > 0); + if (mark.ns == ns_id || all_ns) { marks_cleared = true; - if (mark.id & MARKTREE_PAIRED_FLAG) { - uint64_t other = mark.id ^ MARKTREE_END_FLAG; + if (mt_paired(mark)) { + uint64_t other = mt_lookup_id(mark.ns, mark.id, !mt_end(mark)); ssize_t decor_id = -1; - if (item.decor) { + 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) { .decor = item.decor, .row1 = mark.row })); + ((DecorItem) { .row1 = mark.pos.row })); } map_put(uint64_t, ssize_t)(&delete_set, other, decor_id); - } else if (item.decor) { - decor_remove(buf, mark.row, mark.row, item.decor); + } else if (mark.decor_full) { + decor_remove(buf, mark.pos.row, mark.pos.row, mark.decor_full); } - ExtmarkNs *my_ns = all_ns ? buf_ns_ref(buf, item.ns_id, false) : ns; - map_del(uint64_t, uint64_t)(my_ns->map, item.mark_id); - map_del(uint64_t, ExtmarkItem)(buf->b_extmark_index, start_id); marktree_del_itr(buf->b_marktree, itr, false); } else { marktree_itr_next(buf->b_marktree, itr); @@ -271,12 +276,12 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, int l_row, colnr_T l_col, int u_r uint64_t id; ssize_t decor_id; map_foreach(&delete_set, id, decor_id, { - mtpos_t pos = marktree_lookup(buf->b_marktree, id, itr); + 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, pos.row, it.decor); + decor_remove(buf, it.row1, mark.pos.row, mark.decor_full); } }); map_clear(uint64_t, ssize_t)(&delete_set); @@ -284,13 +289,14 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, int l_row, colnr_T l_col, int u_r return marks_cleared; } -// Returns the position of marks between a range, -// marks found at the start or end index will be included, -// 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 -ExtmarkInfoArray extmark_get(buf_T *buf, uint64_t ns_id, int l_row, colnr_T l_col, int u_row, +/// @return the position of marks between a range, +/// marks found at the start or end index will be included. +/// +/// 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 +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) { ExtmarkInfoArray array = KV_INITIAL_VALUE; @@ -300,30 +306,26 @@ ExtmarkInfoArray extmark_get(buf_T *buf, uint64_t ns_id, int l_row, colnr_T l_co itr, reverse, false, NULL); int order = reverse ? -1 : 1; while ((int64_t)kv_size(array) < amount) { - mtmark_t mark = marktree_itr_current(itr); - mtpos_t endpos = { -1, -1 }; - if (mark.row < 0 - || (mark.row - u_row) * order > 0 - || (mark.row == u_row && (mark.col - u_col) * order > 0)) { + mtkey_t 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)) { break; } - if (mark.id & MARKTREE_END_FLAG) { + if (mt_end(mark)) { goto next_mark; - } else if (mark.id & MARKTREE_PAIRED_FLAG) { - endpos = marktree_lookup(buf->b_marktree, mark.id | MARKTREE_END_FLAG, - NULL); } - - ExtmarkItem item = map_get(uint64_t, ExtmarkItem)(buf->b_extmark_index, - mark.id); - if (item.ns_id == ns_id) { - kv_push(array, ((ExtmarkInfo) { .ns_id = item.ns_id, - .mark_id = item.mark_id, - .row = mark.row, .col = mark.col, - .end_row = endpos.row, - .end_col = endpos.col, - .decor = item.decor })); + 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) })); } next_mark: if (reverse) { @@ -335,72 +337,60 @@ next_mark: return array; } -// Lookup an extmark by id -ExtmarkInfo extmark_from_id(buf_T *buf, uint64_t ns_id, uint64_t id) +/// Lookup an extmark by id +ExtmarkInfo extmark_from_id(buf_T *buf, uint32_t ns_id, uint32_t id) { - ExtmarkNs *ns = buf_ns_ref(buf, ns_id, false); - ExtmarkInfo ret = { 0, 0, -1, -1, -1, -1, NULL }; - if (!ns) { - return ret; - } - - uint64_t mark = map_get(uint64_t, uint64_t)(ns->map, id); - if (!mark) { + 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); + if (!mark.id) { return ret; } - - mtpos_t pos = marktree_lookup(buf->b_marktree, mark, NULL); - mtpos_t endpos = { -1, -1 }; - if (mark & MARKTREE_PAIRED_FLAG) { - endpos = marktree_lookup(buf->b_marktree, mark | MARKTREE_END_FLAG, NULL); - } - assert(pos.row >= 0); - - ExtmarkItem item = map_get(uint64_t, ExtmarkItem)(buf->b_extmark_index, - mark); + 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 = pos.row; - ret.col = pos.col; - ret.end_row = endpos.row; - ret.end_col = endpos.col; - ret.decor = item.decor; + 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; } - -// free extmarks from the buffer +/// free extmarks from the buffer void extmark_free_all(buf_T *buf) { if (!map_size(buf->b_extmark_ns)) { return; } - uint64_t id; - ExtmarkNs ns; - ExtmarkItem item; + MarkTreeIter itr[1] = { 0 }; + marktree_itr_get(buf->b_marktree, 0, 0, itr); + while (true) { + mtkey_t mark = marktree_itr_current(itr); + if (mark.pos.row < 0) { + break; + } - marktree_clear(buf->b_marktree); + // don't free mark.decor_full twice for a paired mark. + if (!(mt_paired(mark) && mt_end(mark))) { + decor_free(mark.decor_full); + } - map_foreach(buf->b_extmark_ns, id, ns, { - (void)id; - map_destroy(uint64_t, uint64_t)(ns.map); - }); - map_destroy(uint64_t, ExtmarkNs)(buf->b_extmark_ns); - map_init(uint64_t, ExtmarkNs, buf->b_extmark_ns); + marktree_itr_next(buf->b_marktree, itr); + } - map_foreach(buf->b_extmark_index, id, item, { - (void)id; - decor_free(item.decor); - }); - map_destroy(uint64_t, ExtmarkItem)(buf->b_extmark_index); - map_init(uint64_t, ExtmarkItem, buf->b_extmark_index); -} + 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); +} -// Save info for undo/redo of set marks +/// 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); @@ -435,18 +425,18 @@ void u_extmark_copy(buf_T *buf, int l_row, colnr_T l_col, int u_row, colnr_T u_c ExtmarkUndoObject undo; MarkTreeIter itr[1] = { 0 }; - marktree_itr_get(buf->b_marktree, l_row, l_col, itr); + marktree_itr_get(buf->b_marktree, (int32_t)l_row, l_col, itr); while (true) { - mtmark_t mark = marktree_itr_current(itr); - if (mark.row < 0 - || mark.row > u_row - || (mark.row == u_row && mark.col > u_col)) { + mtkey_t 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 = mark.id; - pos.old_row = mark.row; - pos.old_col = mark.col; + pos.mark = mt_lookup_key(mark); + pos.old_row = mark.pos.row; + pos.old_col = mark.pos.col; pos.row = -1; pos.col = -1; @@ -509,10 +499,9 @@ void extmark_apply_undo(ExtmarkUndoObject undo_info, bool undo) } } - -// Adjust extmark row for inserted/deleted rows (columns stay fixed). -void extmark_adjust(buf_T *buf, linenr_T line1, linenr_T line2, long amount, long amount_after, - ExtmarkOp undo) +/// Adjust extmark row for inserted/deleted rows (columns stay fixed). +void extmark_adjust(buf_T *buf, linenr_T line1, linenr_T line2, linenr_T amount, + linenr_T amount_after, ExtmarkOp undo) { if (curbuf_splice_pending) { return; @@ -521,7 +510,7 @@ void extmark_adjust(buf_T *buf, linenr_T line1, linenr_T line2, long amount, lon bcount_t old_byte = 0, new_byte = 0; int old_row, new_row; if (amount == MAXLNUM) { - old_row = (int)(line2 - line1+1); + old_row = (int)(line2 - line1 + 1); // TODO(bfredl): ej kasta? old_byte = (bcount_t)buf->deleted_bytes2; @@ -535,11 +524,11 @@ void extmark_adjust(buf_T *buf, linenr_T line1, linenr_T line2, long amount, lon new_row = (int)amount; } if (new_row > 0) { - new_byte = ml_find_line_or_offset(buf, line1+new_row, NULL, true) + new_byte = ml_find_line_or_offset(buf, line1 + new_row, NULL, true) - start_byte; } extmark_splice_impl(buf, - (int)line1-1, 0, start_byte, + (int)line1 - 1, 0, start_byte, old_row, 0, old_byte, new_row, 0, new_byte, undo); } @@ -601,8 +590,7 @@ void extmark_splice_impl(buf_T *buf, int start_row, colnr_T start_col, bcount_t u_extmark_copy(buf, start_row, start_col, end_row, end_col); } - - marktree_splice(buf->b_marktree, start_row, start_col, + marktree_splice(buf->b_marktree, (int32_t)start_row, start_col, old_row, old_col, new_row, new_col); @@ -618,18 +606,18 @@ void extmark_splice_impl(buf_T *buf, int start_row, colnr_T start_col, bcount_t // merge algorithm later. if (old_row == 0 && new_row == 0 && kv_size(uhp->uh_extmark)) { ExtmarkUndoObject *item = &kv_A(uhp->uh_extmark, - kv_size(uhp->uh_extmark)-1); + kv_size(uhp->uh_extmark) - 1); if (item->type == kExtmarkSplice) { ExtmarkSplice *splice = &item->data.splice; if (splice->start_row == start_row && splice->old_row == 0 && splice->new_row == 0) { if (old_col == 0 && start_col >= splice->start_col - && start_col <= splice->start_col+splice->new_col) { + && start_col <= splice->start_col + splice->new_col) { splice->new_col += new_col; splice->new_byte += new_byte; merged = true; } else if (new_col == 0 - && start_col == splice->start_col+splice->new_col) { + && start_col == splice->start_col + splice->new_col) { splice->old_col += old_col; splice->old_byte += old_byte; merged = true; @@ -692,7 +680,6 @@ void extmark_move_region(buf_T *buf, int start_row, colnr_T start_col, bcount_t 0, 0, 0, extent_row, extent_col, extent_byte); - if (undo == kExtmarkUndo) { u_header_T *uhp = u_force_get_undo_header(buf); if (!uhp) { |