diff options
author | Björn Linse <bjorn.linse@gmail.com> | 2020-01-20 19:29:12 +0100 |
---|---|---|
committer | Björn Linse <bjorn.linse@gmail.com> | 2020-01-20 19:36:35 +0100 |
commit | 48a869dc6d29514e943070da9f22f702f5179826 (patch) | |
tree | a419f94caa361cbc79480e3468fea437e710f312 /src/nvim/mark_extended.c | |
parent | 4d4035400ea9cc349fa77d5ac6128c9249c5cb7a (diff) | |
download | rneovim-48a869dc6d29514e943070da9f22f702f5179826.tar.gz rneovim-48a869dc6d29514e943070da9f22f702f5179826.tar.bz2 rneovim-48a869dc6d29514e943070da9f22f702f5179826.zip |
shed biking: it's always extmarks, never marks extended
Diffstat (limited to 'src/nvim/mark_extended.c')
-rw-r--r-- | src/nvim/mark_extended.c | 910 |
1 files changed, 0 insertions, 910 deletions
diff --git a/src/nvim/mark_extended.c b/src/nvim/mark_extended.c deleted file mode 100644 index b60d847676..0000000000 --- a/src/nvim/mark_extended.c +++ /dev/null @@ -1,910 +0,0 @@ -// 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. -// -// 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. -// -// 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. -// -// 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. - -#include <assert.h> -#include "nvim/api/vim.h" -#include "nvim/vim.h" -#include "nvim/charset.h" -#include "nvim/mark_extended.h" -#include "nvim/buffer_updates.h" -#include "nvim/memline.h" -#include "nvim/pos.h" -#include "nvim/globals.h" -#include "nvim/map.h" -#include "nvim/lib/kbtree.h" -#include "nvim/undo.h" -#include "nvim/buffer.h" -#include "nvim/syntax.h" -#include "nvim/highlight.h" - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "mark_extended.c.generated.h" -#endif - -static ExtmarkNs *buf_ns_ref(buf_T *buf, uint64_t ns_id, bool put) { - if (!buf->b_extmark_ns) { - if (!put) { - return NULL; - } - buf->b_extmark_ns = map_new(uint64_t, ExtmarkNs)(); - buf->b_extmark_index = map_new(uint64_t, ExtmarkItem)(); - } - - ExtmarkNs *ns = map_ref(uint64_t, ExtmarkNs)(buf->b_extmark_ns, ns_id, put); - if (put && ns->map == NULL) { - ns->map = map_new(uint64_t, uint64_t)(); - ns->free_id = 1; - } - return ns; -} - - -/// Create or update an extmark -/// -/// 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) -{ - ExtmarkNs *ns = buf_ns_ref(buf, ns_id, true); - mtpos_t old_pos; - uint64_t mark = 0; - - if (id == 0) { - id = ns->free_id++; - } else { - uint64_t old_mark = map_get(uint64_t, uint64_t)(ns->map, id); - if (old_mark) { - if (old_mark & MARKTREE_PAIRED_FLAG) { - extmark_del(buf, ns_id, id); - } else { - // TODO(bfredl): we need to do more if "revising" a decoration mark. - MarkTreeIter itr[1]; - 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); - mark = marktree_revise(buf->b_marktree, itr); - goto revised; - } - marktree_del_itr(buf->b_marktree, itr, false); - } - } else { - ns->free_id = MAX(ns->free_id, id+1); - } - } - - 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 }); - map_put(uint64_t, uint64_t)(ns->map, id, mark); - - 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); - } - return id; -} - -static bool extmark_setraw(buf_T *buf, uint64_t mark, int row, colnr_T col) -{ - MarkTreeIter itr[1]; - mtpos_t pos = marktree_lookup(buf->b_marktree, mark, itr); - if (pos.row == -1) { - return false; - } - - if (pos.row == row && pos.col == col) { - return true; - } - - marktree_move(buf->b_marktree, itr, row, col); - return true; -} - -// Remove an extmark -// Returns 0 on missing id -bool extmark_del(buf_T *buf, uint64_t ns_id, uint64_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) { - return false; - } - - MarkTreeIter itr[1]; - mtpos_t pos = marktree_lookup(buf->b_marktree, mark, itr); - assert(pos.row >= 0); - marktree_del_itr(buf->b_marktree, itr, false); - ExtmarkItem item = map_get(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark); - - if (mark & MARKTREE_PAIRED_FLAG) { - mtpos_t 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); - } - 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; -} - -// 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) -{ - if (!buf->b_extmark_ns) { - return false; - } - - bool marks_cleared = false; - - bool all_ns = (ns_id == 0); - ExtmarkNs *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, uint64_t) *delete_set = NULL; - if (delete_set == NULL) { - delete_set = map_new(uint64_t, uint64_t)(); - } - - MarkTreeIter itr[1]; - 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)) { - break; - } - uint64_t *del_status = map_ref(uint64_t, uint64_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); - } - 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)) { - 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); - } - 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, mark.id); - marktree_del_itr(buf->b_marktree, itr, false); - } else { - marktree_itr_next(buf->b_marktree, itr); - } - } - uint64_t id, status; - map_foreach(delete_set, id, status, { - 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); - } - }); - map_clear(uint64_t, uint64_t)(delete_set); - 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 -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) -{ - ExtmarkArray 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); - if (mark.row < 0 - || (mark.row - u_row) * order > 0 - || (mark.row == u_row && (mark.col - u_col) * order > 0)) { - break; - } - 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 })); - } - if (reverse) { - marktree_itr_prev(buf->b_marktree, itr); - } else { - marktree_itr_next(buf->b_marktree, itr); - } - } - return array; -} - -// Lookup an extmark by 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 }; - if (!ns) { - return ret; - } - - uint64_t mark = map_get(uint64_t, uint64_t)(ns->map, id); - if (!mark) { - return ret; - } - - mtpos_t pos = marktree_lookup(buf->b_marktree, mark, NULL); - assert(pos.row >= 0); - - ret.ns_id = ns_id; - ret.mark_id = id; - ret.row = pos.row; - ret.col = pos.col; - - return ret; -} - - -// free extmarks from the buffer -void extmark_free_all(buf_T *buf) -{ - if (!buf->b_extmark_ns) { - return; - } - - uint64_t id; - ExtmarkNs ns; - ExtmarkItem item; - - marktree_clear(buf->b_marktree); - - map_foreach(buf->b_extmark_ns, id, ns, { - (void)id; - map_free(uint64_t, uint64_t)(ns.map); - }); - map_free(uint64_t, ExtmarkNs)(buf->b_extmark_ns); - buf->b_extmark_ns = NULL; - - map_foreach(buf->b_extmark_index, id, item, { - (void)id; - clear_virttext(&item.virt_text); - }); - map_free(uint64_t, ExtmarkItem)(buf->b_extmark_index); - buf->b_extmark_index = NULL; -} - - -// 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 -/// -/// 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) -{ - u_header_T *uhp = u_force_get_undo_header(buf); - if (!uhp) { - return; - } - - ExtmarkUndoObject undo; - - MarkTreeIter itr[1]; - 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)) { - break; - } - ExtmarkSavePos pos; - pos.mark = mark.id; - pos.old_row = mark.row; - pos.old_col = mark.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); - } -} - -/// undo or redo an extmark operation -void extmark_apply_undo(ExtmarkUndoObject undo_info, bool undo) -{ - // splice: any text operation changing position (except :move) - if (undo_info.type == kExtmarkSplice) { - // 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); - - } else { - extmark_splice(curbuf, - splice.start_row, splice.start_col, - splice.oldextent_row, splice.oldextent_col, - splice.newextent_row, splice.newextent_col, - kExtmarkNoUndo); - } - // kExtmarkSavePos - } else if (undo_info.type == kExtmarkSavePos) { - ExtmarkSavePos pos = undo_info.data.savepos; - if (undo) { - if (pos.old_row >= 0) { - extmark_setraw(curbuf, pos.mark, pos.old_row, pos.old_col); - } - // Redo - } else { - if (pos.row >= 0) { - extmark_setraw(curbuf, pos.mark, pos.row, pos.col); - } - } - } else if (undo_info.type == kExtmarkMove) { - 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, - kExtmarkNoUndo); - } else { - extmark_move_region(curbuf, - move.start_row, move.start_col, - move.extent_row, move.extent_col, - move.new_row, move.new_col, - kExtmarkNoUndo); - } - } -} - - -// 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) -{ - 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); - } -} - -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, - ExtmarkOp undo) -{ - buf_updates_send_splice(buf, start_row, start_col, - oldextent_row, oldextent_col, - newextent_row, newextent_col); - - if (undo == kExtmarkUndo && (oldextent_row > 0 || oldextent_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; - 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); - - if (undo == kExtmarkUndo) { - u_header_T *uhp = u_force_get_undo_header(buf); - if (!uhp) { - return; - } - - bool merged = false; - // 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)) { - 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; - merged = true; - } else if (newextent_col == 0 - && start_col == splice->start_col+splice->newextent_col) { - splice->oldextent_col += oldextent_col; - merged = true; - } else if (newextent_col == 0 - && start_col + oldextent_col == splice->start_col) { - splice->start_col = start_col; - splice->oldextent_col += oldextent_col; - merged = true; - } - } - } - } - - if (!merged) { - 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; - - kv_push(uhp->uh_extmark, - ((ExtmarkUndoObject){ .type = kExtmarkSplice, - .data.splice = splice })); - } - } -} - - -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) -{ - // 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); - - 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); - - - if (undo == kExtmarkUndo) { - u_header_T *uhp = u_force_get_undo_header(buf); - if (!uhp) { - return; - } - - ExtmarkMove move; - move.start_row = start_row; - move.start_col = start_col; - move.extent_row = extent_row; - move.extent_col = extent_col; - move.new_row = new_row; - move.new_col = new_col; - - kv_push(uhp->uh_extmark, - ((ExtmarkUndoObject){ .type = kExtmarkMove, - .data.move = move })); - } -} - -uint64_t src2ns(Integer *src_id) -{ - if (*src_id == 0) { - *src_id = (Integer)nvim_create_namespace((String)STRING_INIT); - } - if (*src_id < 0) { - return UINT64_MAX; - } else { - return (uint64_t)(*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. -/// -/// @param buf Buffer to add highlights to -/// @param src_id src_id to use or 0 to use a new src_id group, -/// or -1 for ungrouped highlight. -/// @param hl_id Highlight group id -/// @param pos_start Cursor position to start the hightlighting at -/// @param pos_end Cursor position to end the highlighting at -/// @param offset Move the whole highlighting this many columns to the right -void bufhl_add_hl_pos_offset(buf_T *buf, - int src_id, - int hl_id, - lpos_T pos_start, - lpos_T pos_end, - colnr_T offset) -{ - colnr_T hl_start = 0; - colnr_T hl_end = 0; - - // TODO(bfredl): if decoration had blocky mode, we could avoid this loop - for (linenr_T lnum = pos_start.lnum; lnum <= pos_end.lnum; lnum ++) { - if (pos_start.lnum < lnum && lnum < pos_end.lnum) { - hl_start = offset-1; - hl_end = MAXCOL; - } else if (lnum == pos_start.lnum && lnum < pos_end.lnum) { - hl_start = pos_start.col + offset; - hl_end = MAXCOL; - } else if (pos_start.lnum < lnum && lnum == pos_end.lnum) { - hl_start = offset-1; - hl_end = pos_end.col + offset; - } else if (pos_start.lnum == lnum && pos_end.lnum == lnum) { - 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, hl_end, - VIRTTEXT_EMPTY); - } -} - -void clear_virttext(VirtText *text) -{ - for (size_t i = 0; i < kv_size(*text); i++) { - xfree(kv_A(*text, i).text); - } - kv_destroy(*text); - *text = (VirtText)KV_INITIAL_VALUE; -} - -VirtText *extmark_find_virttext(buf_T *buf, int row, uint64_t ns_id) -{ - MarkTreeIter itr[1]; - marktree_itr_get(buf->b_marktree, row, 0, itr); - while (true) { - mtmark_t mark = marktree_itr_current(itr); - if (mark.row < 0 || mark.row > row) { - break; - } - 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; - } - marktree_itr_next(buf->b_marktree, itr); - } - return NULL; -} - - -bool extmark_decorations_reset(buf_T *buf, DecorationState *state) -{ - state->row = -1; - return buf->b_extmark_index; -} - - -bool extmark_decorations_start(buf_T *buf, int top_row, DecorationState *state) -{ - kv_size(state->active) = 0; - state->top_row = top_row; - marktree_itr_get(buf->b_marktree, top_row, 0, state->itr); - if (!state->itr->node) { - return false; - } - marktree_itr_rewind(buf->b_marktree, state->itr); - while (true) { - mtmark_t mark = marktree_itr_current(state->itr); - 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; - } - mtpos_t altpos = marktree_lookup(buf->b_marktree, - mark.id^MARKTREE_END_FLAG, NULL); - - uint64_t start_id = mark.id & ~MARKTREE_END_FLAG; - ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index, - start_id, false); - if ((!(mark.id&MARKTREE_END_FLAG) && altpos.row < top_row - && !kv_size(item->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); - } -next_mark: - if (marktree_itr_node_done(state->itr)) { - break; - } - marktree_itr_next(buf->b_marktree, state->itr); - } - - return true; // TODO(bfredl): check if available in the region -} - -bool extmark_decorations_line(buf_T *buf, int row, DecorationState *state) -{ - if (state->row == -1) { - extmark_decorations_start(buf, row, state); - } - state->row = row; - state->col_until = -1; - return true; // TODO(bfredl): be more precise -} - -int extmark_decorations_col(buf_T *buf, int col, DecorationState *state) -{ - if (col <= state->col_until) { - return state->current; - } - state->col_until = MAXCOL; - while (true) { - mtmark_t mark = marktree_itr_current(state->itr); - if (mark.row < 0 || mark.row > state->row) { - break; - } else if (mark.row == state->row && mark.col > col) { - state->col_until = mark.col-1; - break; - } - - if ((mark.id&MARKTREE_END_FLAG)) { - // TODO(bfredl): check decorations flag - goto next_mark; - } - mtpos_t endpos = marktree_lookup(buf->b_marktree, - mark.id|MARKTREE_END_FLAG, NULL); - - ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index, - mark.id, false); - - if (endpos.row < mark.row - || (endpos.row == mark.row && endpos.col <= mark.col)) { - if (!kv_size(item->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 })); - } - -next_mark: - marktree_itr_next(buf->b_marktree, state->itr); - } - - int attr = 0; - size_t j = 0; - for (size_t i = 0; i < kv_size(state->active); i++) { - HlRange item = kv_A(state->active, i); - bool active = false, keep = true; - if (item.end_row < state->row - || (item.end_row == state->row && item.end_col <= col)) { - if (!(item.start_row >= state->row && item.virt_text)) { - keep = false; - } - } else { - if (item.start_row < state->row - || (item.start_row == state->row && item.start_col <= col)) { - active = true; - if (item.end_row == state->row) { - state->col_until = MIN(state->col_until, item.end_col-1); - } - } else { - if (item.start_row == state->row) { - state->col_until = MIN(state->col_until, item.start_col-1); - } - } - } - if (active && item.attr_id > 0) { - attr = hl_combine_attr(attr, item.attr_id); - } - if (keep) { - kv_A(state->active, j++) = kv_A(state->active, i); - } - } - kv_size(state->active) = j; - state->current = attr; - return attr; -} - -VirtText *extmark_decorations_virt_text(buf_T *buf, DecorationState *state) -{ - extmark_decorations_col(buf, MAXCOL, state); - for (size_t i = 0; i < kv_size(state->active); i++) { - HlRange item = kv_A(state->active, i); - if (item.start_row == state->row && item.virt_text) { - return item.virt_text; - } - } - return NULL; -} |