diff options
Diffstat (limited to 'src/nvim/extmark.c')
-rw-r--r-- | src/nvim/extmark.c | 481 |
1 files changed, 282 insertions, 199 deletions
diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index 1457a1172d..3a04908ccb 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -1,29 +1,24 @@ // 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. Each mark exists in a btree of -// lines containing btrees of columns. +// 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. // -// The btree provides efficient range lookups. // A map of pointers to the marks is used for fast lookup by mark id. // -// Marks are moved by calls to extmark_splice. Additionally mark_adjust -// might adjust extmarks to line inserts/deletes. +// Marks are moved by calls to extmark_splice. Some standard interfaces +// mark_adjust and inserted_bytes already adjust marks, check if these are +// being used before adding extmark_splice calls! // // Undo/Redo of marks is implemented by storing the call arguments to // extmark_splice. The list of arguments is applied in extmark_apply_undo. -// The only case where we have to copy extmarks is for the area being effected -// by a delete. +// We have to copy extmark positions when the extmarks are within a +// deleted/changed region. // // Marks live in namespaces that allow plugins/users to segregate marks // from other users. // -// For possible ideas for efficency improvements see: -// http://blog.atom.io/2015/06/16/optimizing-an-important-atom-primitive.html -// TODO(bfredl): These ideas could be used for an enhanced btree, which -// wouldn't need separate line and column layers. -// Other implementations exist in gtk and tk toolkits. -// // Deleting marks only happens when explicitly calling extmark_del, deleteing // over a range of marks will only move the marks. Deleting on a mark will // leave it in same position unless it is on the EOL of a line. @@ -48,6 +43,13 @@ # include "extmark.c.generated.h" #endif +static PMap(uint64_t) *hl_decors; + +void extmark_init(void) +{ + hl_decors = pmap_new(uint64_t)(); +} + static ExtmarkNs *buf_ns_ref(buf_T *buf, uint64_t ns_id, bool put) { if (!buf->b_extmark_ns) { if (!put) { @@ -71,7 +73,8 @@ static ExtmarkNs *buf_ns_ref(buf_T *buf, uint64_t ns_id, bool put) { /// must not be used during iteration! /// @returns the mark id uint64_t extmark_set(buf_T *buf, uint64_t ns_id, uint64_t id, - int row, colnr_T col, ExtmarkOp op) + int row, colnr_T col, int end_row, colnr_T end_col, + Decoration *decor, ExtmarkOp op) { ExtmarkNs *ns = buf_ns_ref(buf, ns_id, true); mtpos_t old_pos; @@ -82,7 +85,7 @@ uint64_t extmark_set(buf_T *buf, uint64_t ns_id, uint64_t id, } else { uint64_t old_mark = map_get(uint64_t, uint64_t)(ns->map, id); if (old_mark) { - if (old_mark & MARKTREE_PAIRED_FLAG) { + if (old_mark & MARKTREE_PAIRED_FLAG || end_row > -1) { extmark_del(buf, ns_id, id); } else { // TODO(bfredl): we need to do more if "revising" a decoration mark. @@ -90,7 +93,12 @@ uint64_t extmark_set(buf_T *buf, uint64_t ns_id, uint64_t id, old_pos = marktree_lookup(buf->b_marktree, old_mark, itr); assert(itr->node); if (old_pos.row == row && old_pos.col == col) { - map_del(uint64_t, ExtmarkItem)(buf->b_extmark_index, old_mark); + ExtmarkItem it = map_del(uint64_t, ExtmarkItem)(buf->b_extmark_index, + old_mark); + if (it.decor) { + decoration_redraw(buf, row, row, it.decor); + free_decoration(it.decor); + } mark = marktree_revise(buf->b_marktree, itr); goto revised; } @@ -101,11 +109,17 @@ uint64_t extmark_set(buf_T *buf, uint64_t ns_id, uint64_t id, } } - mark = marktree_put(buf->b_marktree, row, col, true); + if (end_row > -1) { + mark = marktree_put_pair(buf->b_marktree, + row, col, true, + end_row, end_col, false); + } else { + mark = marktree_put(buf->b_marktree, row, col, true); + } + revised: map_put(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark, - (ExtmarkItem){ ns_id, id, 0, - KV_INITIAL_VALUE }); + (ExtmarkItem){ ns_id, id, decor }); map_put(uint64_t, uint64_t)(ns->map, id, mark); if (op != kExtmarkNoUndo) { @@ -114,6 +128,10 @@ revised: // adding new marks to old undo headers. u_extmark_set(buf, mark, row, col); } + + if (decor) { + decoration_redraw(buf, row, end_row > -1 ? end_row : row, decor); + } return id; } @@ -152,27 +170,23 @@ bool extmark_del(buf_T *buf, uint64_t ns_id, uint64_t id) assert(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) { - mtpos_t pos2 = marktree_lookup(buf->b_marktree, - mark|MARKTREE_END_FLAG, itr); + pos2 = marktree_lookup(buf->b_marktree, mark|MARKTREE_END_FLAG, itr); assert(pos2.row >= 0); marktree_del_itr(buf->b_marktree, itr, false); - if (item.hl_id && pos2.row >= pos.row) { - redraw_buf_range_later(buf, pos.row+1, pos2.row+1); - } } - if (kv_size(item.virt_text)) { - redraw_buf_line_later(buf, pos.row+1); + if (item.decor) { + decoration_redraw(buf, pos.row, pos2.row, item.decor); + free_decoration(item.decor); } - clear_virttext(&item.virt_text); 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; } @@ -202,9 +216,11 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, } // the value is either zero or the lnum (row+1) if highlight was present. - static Map(uint64_t, uint64_t) *delete_set = NULL; + static Map(uint64_t, ssize_t) *delete_set = NULL; + typedef struct { Decoration *decor; int row1; } DecorItem; + static kvec_t(DecorItem) decors; if (delete_set == NULL) { - delete_set = map_new(uint64_t, uint64_t)(); + delete_set = map_new(uint64_t, ssize_t)(); } MarkTreeIter itr[1] = { 0 }; @@ -216,14 +232,16 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, || (mark.row == u_row && mark.col > u_col)) { break; } - uint64_t *del_status = map_ref(uint64_t, uint64_t)(delete_set, mark.id, - false); + ssize_t *del_status = map_ref(uint64_t, ssize_t)(delete_set, mark.id, + false); if (del_status) { marktree_del_itr(buf->b_marktree, itr, false); - map_del(uint64_t, uint64_t)(delete_set, mark.id); - if (*del_status > 0) { - redraw_buf_range_later(buf, (linenr_T)(*del_status), mark.row+1); + if (*del_status >= 0) { // we had a decor_id + DecorItem it = kv_A(decors, *del_status); + decoration_redraw(buf, it.row1, mark.row, it.decor); + free_decoration(it.decor); } + map_del(uint64_t, ssize_t)(delete_set, mark.id); continue; } @@ -233,15 +251,21 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, assert(item.ns_id > 0 && item.mark_id > 0); if (item.mark_id > 0 && (item.ns_id == ns_id || all_ns)) { - if (kv_size(item.virt_text)) { - redraw_buf_line_later(buf, mark.row+1); - } - clear_virttext(&item.virt_text); marks_cleared = true; if (mark.id & MARKTREE_PAIRED_FLAG) { uint64_t other = mark.id ^ MARKTREE_END_FLAG; - uint64_t status = item.hl_id ? ((uint64_t)mark.row+1) : 0; - map_put(uint64_t, uint64_t)(delete_set, other, status); + ssize_t decor_id = -1; + if (item.decor) { + // 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 })); + } + map_put(uint64_t, ssize_t)(delete_set, other, decor_id); + } else if (item.decor) { + decoration_redraw(buf, mark.row, mark.row, item.decor); + free_decoration(item.decor); } 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); @@ -251,16 +275,20 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, marktree_itr_next(buf->b_marktree, itr); } } - uint64_t id, status; - map_foreach(delete_set, id, status, { + uint64_t id; + ssize_t decor_id; + map_foreach(delete_set, id, decor_id, { mtpos_t pos = marktree_lookup(buf->b_marktree, id, itr); assert(itr->node); marktree_del_itr(buf->b_marktree, itr, false); - if (status > 0) { - redraw_buf_range_later(buf, (linenr_T)status, pos.row+1); + if (decor_id >= 0) { + DecorItem it = kv_A(decors, decor_id); + decoration_redraw(buf, it.row1, pos.row, it.decor); + free_decoration(it.decor); } }); - map_clear(uint64_t, uint64_t)(delete_set); + map_clear(uint64_t, ssize_t)(delete_set); + kv_size(decors) = 0; return marks_cleared; } @@ -270,31 +298,44 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, // 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 -ExtmarkArray extmark_get(buf_T *buf, uint64_t ns_id, - int l_row, colnr_T l_col, - int u_row, colnr_T u_col, - int64_t amount, bool reverse) +ExtmarkInfoArray extmark_get(buf_T *buf, uint64_t ns_id, + int l_row, colnr_T l_col, + int u_row, colnr_T u_col, + int64_t amount, bool reverse) { - ExtmarkArray array = KV_INITIAL_VALUE; - MarkTreeIter itr[1] = { 0 }; + 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); 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)) { break; } + if (mark.id & MARKTREE_END_FLAG) { + 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 })); + .row = mark.row, .col = mark.col, + .end_row = endpos.row, + .end_col = endpos.col, + .decor = item.decor })); } +next_mark: if (reverse) { marktree_itr_prev(buf->b_marktree, itr); } else { @@ -308,7 +349,7 @@ ExtmarkArray extmark_get(buf_T *buf, uint64_t ns_id, ExtmarkInfo extmark_from_id(buf_T *buf, uint64_t ns_id, uint64_t id) { ExtmarkNs *ns = buf_ns_ref(buf, ns_id, false); - ExtmarkInfo ret = { 0, 0, -1, -1 }; + ExtmarkInfo ret = { 0, 0, -1, -1, -1, -1, NULL }; if (!ns) { return ret; } @@ -319,12 +360,22 @@ ExtmarkInfo extmark_from_id(buf_T *buf, uint64_t ns_id, uint64_t id) } 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); + 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; return ret; } @@ -352,7 +403,7 @@ void extmark_free_all(buf_T *buf) map_foreach(buf->b_extmark_index, id, item, { (void)id; - clear_virttext(&item.virt_text); + free_decoration(item.decor); }); map_free(uint64_t, ExtmarkItem)(buf->b_extmark_index); buf->b_extmark_index = NULL; @@ -428,18 +479,18 @@ void extmark_apply_undo(ExtmarkUndoObject undo_info, bool undo) // Undo ExtmarkSplice splice = undo_info.data.splice; if (undo) { - extmark_splice(curbuf, - splice.start_row, splice.start_col, - splice.newextent_row, splice.newextent_col, - splice.oldextent_row, splice.oldextent_col, - kExtmarkNoUndo); + extmark_splice_impl(curbuf, + splice.start_row, splice.start_col, splice.start_byte, + splice.new_row, splice.new_col, splice.new_byte, + splice.old_row, splice.old_col, splice.old_byte, + kExtmarkNoUndo); } else { - extmark_splice(curbuf, - splice.start_row, splice.start_col, - splice.oldextent_row, splice.oldextent_col, - splice.newextent_row, splice.newextent_col, - kExtmarkNoUndo); + extmark_splice_impl(curbuf, + splice.start_row, splice.start_col, splice.start_byte, + splice.old_row, splice.old_col, splice.old_byte, + splice.new_row, splice.new_col, splice.new_byte, + kExtmarkNoUndo); } // kExtmarkSavePos } else if (undo_info.type == kExtmarkSavePos) { @@ -458,15 +509,15 @@ void extmark_apply_undo(ExtmarkUndoObject undo_info, bool undo) ExtmarkMove move = undo_info.data.move; if (undo) { extmark_move_region(curbuf, - move.new_row, move.new_col, - move.extent_row, move.extent_col, - move.start_row, move.start_col, + move.new_row, move.new_col, move.new_byte, + move.extent_row, move.extent_col, move.extent_byte, + move.start_row, move.start_col, move.start_byte, kExtmarkNoUndo); } else { extmark_move_region(curbuf, - move.start_row, move.start_col, - move.extent_row, move.extent_col, - move.new_row, move.new_col, + move.start_row, move.start_col, move.start_byte, + move.extent_row, move.extent_col, move.extent_byte, + move.new_row, move.new_col, move.new_byte, kExtmarkNoUndo); } } @@ -481,51 +532,74 @@ void extmark_adjust(buf_T *buf, long amount_after, ExtmarkOp undo) { - if (!curbuf_splice_pending) { - int old_extent, new_extent; - if (amount == MAXLNUM) { - old_extent = (int)(line2 - line1+1); - new_extent = (int)(amount_after + old_extent); - } else { - // A region is either deleted (amount == MAXLNUM) or - // added (line2 == MAXLNUM). The only other case is :move - // which is handled by a separate entry point extmark_move_region. - assert(line2 == MAXLNUM); - old_extent = 0; - new_extent = (int)amount; - } - extmark_splice(buf, - (int)line1-1, 0, - old_extent, 0, - new_extent, 0, undo); + if (curbuf_splice_pending) { + return; } + bcount_t start_byte = ml_find_line_or_offset(buf, line1, NULL, true); + bcount_t old_byte = 0, new_byte = 0; + int old_row, new_row; + if (amount == MAXLNUM) { + old_row = (int)(line2 - line1+1); + // TODO(bfredl): ej kasta? + old_byte = (bcount_t)buf->deleted_bytes2; + + new_row = (int)(amount_after + old_row); + } else { + // A region is either deleted (amount == MAXLNUM) or + // added (line2 == MAXLNUM). The only other case is :move + // which is handled by a separate entry point extmark_move_region. + assert(line2 == MAXLNUM); + old_row = 0; + new_row = (int)amount; + } + if (new_row > 0) { + 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, + old_row, 0, old_byte, + new_row, 0, new_byte, undo); } void extmark_splice(buf_T *buf, int start_row, colnr_T start_col, - int oldextent_row, colnr_T oldextent_col, - int newextent_row, colnr_T newextent_col, + int old_row, colnr_T old_col, bcount_t old_byte, + int new_row, colnr_T new_col, bcount_t new_byte, ExtmarkOp undo) { - buf_updates_send_splice(buf, start_row, start_col, - oldextent_row, oldextent_col, - newextent_row, newextent_col); + long offset = ml_find_line_or_offset(buf, start_row+1, NULL, true); + extmark_splice_impl(buf, start_row, start_col, offset+start_col, + old_row, old_col, old_byte, new_row, new_col, new_byte, + undo); +} + +void extmark_splice_impl(buf_T *buf, + int start_row, colnr_T start_col, bcount_t start_byte, + int old_row, colnr_T old_col, bcount_t old_byte, + int new_row, colnr_T new_col, bcount_t new_byte, + ExtmarkOp undo) +{ + curbuf->deleted_bytes2 = 0; + buf_updates_send_splice(buf, start_row, start_col, start_byte, + old_row, old_col, old_byte, + new_row, new_col, new_byte); - if (undo == kExtmarkUndo && (oldextent_row > 0 || oldextent_col > 0)) { + if (undo == kExtmarkUndo && (old_row > 0 || old_col > 0)) { // Copy 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 + oldextent_row; - int end_col = (oldextent_row ? 0 : start_col) + oldextent_col; + 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); } marktree_splice(buf->b_marktree, start_row, start_col, - oldextent_row, oldextent_col, - newextent_row, newextent_col); + old_row, old_col, + new_row, new_col); if (undo == kExtmarkUndo) { u_header_T *uhp = u_force_get_undo_header(buf); @@ -537,25 +611,29 @@ void extmark_splice(buf_T *buf, // TODO(bfredl): this is quite rudimentary. We merge small (within line) // inserts with each other and small deletes with each other. Add full // merge algorithm later. - if (oldextent_row == 0 && newextent_row == 0 && kv_size(uhp->uh_extmark)) { + 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); if (item->type == kExtmarkSplice) { ExtmarkSplice *splice = &item->data.splice; - if (splice->start_row == start_row && splice->oldextent_row == 0 - && splice->newextent_row == 0) { - if (oldextent_col == 0 && start_col >= splice->start_col - && start_col <= splice->start_col+splice->newextent_col) { - splice->newextent_col += newextent_col; + 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) { + splice->new_col += new_col; + splice->new_byte += new_byte; merged = true; - } else if (newextent_col == 0 - && start_col == splice->start_col+splice->newextent_col) { - splice->oldextent_col += oldextent_col; + } else if (new_col == 0 + && start_col == splice->start_col+splice->new_col) { + splice->old_col += old_col; + splice->old_byte += old_byte; merged = true; - } else if (newextent_col == 0 - && start_col + oldextent_col == splice->start_col) { + } else if (new_col == 0 + && start_col + old_col == splice->start_col) { splice->start_col = start_col; - splice->oldextent_col += oldextent_col; + splice->start_byte = start_byte; + splice->old_col += old_col; + splice->old_byte += old_byte; merged = true; } } @@ -566,10 +644,13 @@ void extmark_splice(buf_T *buf, ExtmarkSplice splice; splice.start_row = start_row; splice.start_col = start_col; - splice.oldextent_row = oldextent_row; - splice.oldextent_col = oldextent_col; - splice.newextent_row = newextent_row; - splice.newextent_col = newextent_col; + splice.start_byte = start_byte; + splice.old_row = old_row; + splice.old_col = old_col; + splice.old_byte = old_byte; + splice.new_row = new_row; + splice.new_col = new_col; + splice.new_byte = new_byte; kv_push(uhp->uh_extmark, ((ExtmarkUndoObject){ .type = kExtmarkSplice, @@ -584,30 +665,31 @@ void extmark_splice_cols(buf_T *buf, ExtmarkOp undo) { extmark_splice(buf, start_row, start_col, - 0, old_col, - 0, new_col, undo); + 0, old_col, old_col, + 0, new_col, new_col, undo); } -void extmark_move_region(buf_T *buf, - int start_row, colnr_T start_col, - int extent_row, colnr_T extent_col, - int new_row, colnr_T new_col, - ExtmarkOp undo) +void extmark_move_region( + buf_T *buf, + int start_row, colnr_T start_col, bcount_t start_byte, + int extent_row, colnr_T extent_col, bcount_t extent_byte, + int new_row, colnr_T new_col, bcount_t new_byte, + ExtmarkOp undo) { // TODO(bfredl): this is not synced to the buffer state inside the callback. // But unless we make the undo implementation smarter, this is not ensured // anyway. - buf_updates_send_splice(buf, start_row, start_col, - extent_row, extent_col, - 0, 0); + buf_updates_send_splice(buf, start_row, start_col, start_byte, + extent_row, extent_col, extent_byte, + 0, 0, 0); marktree_move_region(buf->b_marktree, start_row, start_col, extent_row, extent_col, new_row, new_col); - buf_updates_send_splice(buf, new_row, new_col, - 0, 0, - extent_row, extent_col); + buf_updates_send_splice(buf, new_row, new_col, new_byte, + 0, 0, 0, + extent_row, extent_col, extent_byte); if (undo == kExtmarkUndo) { @@ -619,10 +701,13 @@ void extmark_move_region(buf_T *buf, ExtmarkMove move; move.start_row = start_row; move.start_col = start_col; + move.start_byte = start_byte; move.extent_row = extent_row; move.extent_col = extent_col; + move.extent_byte = extent_byte; move.new_row = new_row; move.new_col = new_col; + move.new_byte = new_byte; kv_push(uhp->uh_extmark, ((ExtmarkUndoObject){ .type = kExtmarkMove, @@ -642,50 +727,6 @@ uint64_t src2ns(Integer *src_id) } } -/// Adds a decoration to a buffer. -/// -/// Unlike matchaddpos() highlights, these follow changes to the the buffer -/// texts. Decorations are represented internally and in the API as extmarks. -/// -/// @param buf The buffer to add decorations to -/// @param ns_id A valid namespace id. -/// @param hl_id Id of the highlight group to use (or zero) -/// @param start_row The line to highlight -/// @param start_col First column to highlight -/// @param end_row The line to highlight -/// @param end_col The last column to highlight -/// @param virt_text Virtual text (currently placed at the EOL of start_row) -/// @return The extmark id inside the namespace -uint64_t extmark_add_decoration(buf_T *buf, uint64_t ns_id, int hl_id, - int start_row, colnr_T start_col, - int end_row, colnr_T end_col, - VirtText virt_text) -{ - ExtmarkNs *ns = buf_ns_ref(buf, ns_id, true); - ExtmarkItem item; - item.ns_id = ns_id; - item.mark_id = ns->free_id++; - item.hl_id = hl_id; - item.virt_text = virt_text; - - uint64_t mark; - - if (end_row > -1) { - mark = marktree_put_pair(buf->b_marktree, - start_row, start_col, true, - end_row, end_col, false); - } else { - mark = marktree_put(buf->b_marktree, start_row, start_col, true); - } - - map_put(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark, item); - map_put(uint64_t, uint64_t)(ns->map, item.mark_id, mark); - - redraw_buf_range_later(buf, start_row+1, - (end_row >= 0 ? end_row : start_row) + 1); - return item.mark_id; -} - /// Add highlighting to a buffer, bounded by two cursor positions, /// with an offset. /// @@ -705,6 +746,7 @@ void bufhl_add_hl_pos_offset(buf_T *buf, { colnr_T hl_start = 0; colnr_T hl_end = 0; + Decoration *decor = decoration_hl(hl_id); // TODO(bfredl): if decoration had blocky mode, we could avoid this loop for (linenr_T lnum = pos_start.lnum; lnum <= pos_end.lnum; lnum ++) { @@ -729,10 +771,44 @@ void bufhl_add_hl_pos_offset(buf_T *buf, hl_start = pos_start.col + offset; hl_end = pos_end.col + offset; } - (void)extmark_add_decoration(buf, (uint64_t)src_id, hl_id, - (int)lnum-1, hl_start, - (int)lnum-1+end_off, hl_end, - VIRTTEXT_EMPTY); + (void)extmark_set(buf, (uint64_t)src_id, 0, + (int)lnum-1, hl_start, (int)lnum-1+end_off, hl_end, + decor, kExtmarkUndo); + } +} + +Decoration *decoration_hl(int hl_id) +{ + assert(hl_id > 0); + Decoration **dp = (Decoration **)pmap_ref(uint64_t)(hl_decors, + (uint64_t)hl_id, true); + if (*dp) { + return *dp; + } + + Decoration *decor = xcalloc(1, sizeof(*decor)); + decor->hl_id = hl_id; + decor->shared = true; + *dp = decor; + return decor; +} + +void decoration_redraw(buf_T *buf, int row1, int row2, Decoration *decor) +{ + if (decor->hl_id && row2 >= row1) { + redraw_buf_range_later(buf, row1+1, row2+1); + } + + if (kv_size(decor->virt_text)) { + redraw_buf_line_later(buf, row1+1); + } +} + +void free_decoration(Decoration *decor) +{ + if (decor && !decor->shared) { + clear_virttext(&decor->virt_text); + xfree(decor); } } @@ -757,8 +833,8 @@ VirtText *extmark_find_virttext(buf_T *buf, int row, uint64_t ns_id) ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark.id, false); if (item && (ns_id == 0 || ns_id == item->ns_id) - && kv_size(item->virt_text)) { - return &item->virt_text; + && item->decor && kv_size(item->decor->virt_text)) { + return &item->decor->virt_text; } marktree_itr_next(buf->b_marktree, itr); } @@ -787,7 +863,6 @@ bool decorations_redraw_start(buf_T *buf, int top_row, if (mark.row < 0) { // || mark.row > end_row break; } - // TODO(bfredl): dedicated flag for being a decoration? if ((mark.row < top_row && mark.id&MARKTREE_END_FLAG)) { goto next_mark; } @@ -797,25 +872,30 @@ bool decorations_redraw_start(buf_T *buf, int top_row, uint64_t start_id = mark.id & ~MARKTREE_END_FLAG; ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index, start_id, false); + if (!item || !item->decor) { + // TODO(bfredl): dedicated flag for being a decoration? + goto next_mark; + } + Decoration *decor = item->decor; + if ((!(mark.id&MARKTREE_END_FLAG) && altpos.row < top_row - && item && !kv_size(item->virt_text)) + && item && !kv_size(decor->virt_text)) || ((mark.id&MARKTREE_END_FLAG) && altpos.row >= top_row)) { goto next_mark; } - if (item && (item->hl_id > 0 || kv_size(item->virt_text))) { - int attr_id = item->hl_id > 0 ? syn_id2attr(item->hl_id) : 0; - VirtText *vt = kv_size(item->virt_text) ? &item->virt_text : NULL; - HlRange range; - if (mark.id&MARKTREE_END_FLAG) { - range = (HlRange){ altpos.row, altpos.col, mark.row, mark.col, - attr_id, vt }; - } else { - range = (HlRange){ mark.row, mark.col, altpos.row, - altpos.col, attr_id, vt }; - } - kv_push(state->active, range); + int attr_id = decor->hl_id > 0 ? syn_id2attr(decor->hl_id) : 0; + VirtText *vt = kv_size(decor->virt_text) ? &decor->virt_text : NULL; + HlRange range; + if (mark.id&MARKTREE_END_FLAG) { + range = (HlRange){ altpos.row, altpos.col, mark.row, mark.col, + attr_id, vt }; + } else { + range = (HlRange){ mark.row, mark.col, altpos.row, + altpos.col, attr_id, vt }; } + kv_push(state->active, range); + next_mark: if (marktree_itr_node_done(state->itr)) { break; @@ -860,21 +940,24 @@ int decorations_redraw_col(buf_T *buf, int col, DecorationRedrawState *state) ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark.id, false); + if (!item || !item->decor) { + // TODO(bfredl): dedicated flag for being a decoration? + goto next_mark; + } + Decoration *decor = item->decor; if (endpos.row < mark.row || (endpos.row == mark.row && endpos.col <= mark.col)) { - if (item && !kv_size(item->virt_text)) { + if (item && !kv_size(decor->virt_text)) { goto next_mark; } } - if (item && (item->hl_id > 0 || kv_size(item->virt_text))) { - int attr_id = item->hl_id > 0 ? syn_id2attr(item->hl_id) : 0; - VirtText *vt = kv_size(item->virt_text) ? &item->virt_text : NULL; - kv_push(state->active, ((HlRange){ mark.row, mark.col, - endpos.row, endpos.col, - attr_id, vt })); - } + int attr_id = decor->hl_id > 0 ? syn_id2attr(decor->hl_id) : 0; + VirtText *vt = kv_size(decor->virt_text) ? &decor->virt_text : NULL; + kv_push(state->active, ((HlRange){ mark.row, mark.col, + endpos.row, endpos.col, + attr_id, vt })); next_mark: marktree_itr_next(buf->b_marktree, state->itr); |