diff options
Diffstat (limited to 'src/nvim/extmark.c')
-rw-r--r-- | src/nvim/extmark.c | 331 |
1 files changed, 18 insertions, 313 deletions
diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index 0de396fd1f..413ea4116a 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -22,12 +22,18 @@ // 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. +// +// Extmarks are used to implement buffer decoration. Decoration is mostly +// regarded as an application of extmarks, however for practical reasons code +// that deletes an extmark with decoration will call back into the decoration +// code for redrawing the line with the deleted decoration. #include <assert.h> #include "nvim/api/vim.h" #include "nvim/vim.h" #include "nvim/charset.h" #include "nvim/extmark.h" +#include "nvim/decoration.h" #include "nvim/buffer_updates.h" #include "nvim/memline.h" #include "nvim/pos.h" @@ -36,20 +42,11 @@ #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 "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) { @@ -96,8 +93,8 @@ uint64_t extmark_set(buf_T *buf, uint64_t ns_id, uint64_t id, 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); + decor_redraw(buf, row, row, it.decor); + decor_free(it.decor); } mark = marktree_revise(buf->b_marktree, itr); goto revised; @@ -130,7 +127,7 @@ revised: } if (decor) { - decoration_redraw(buf, row, end_row > -1 ? end_row : row, decor); + decor_redraw(buf, row, end_row > -1 ? end_row : row, decor); } return id; } @@ -179,8 +176,8 @@ bool extmark_del(buf_T *buf, uint64_t ns_id, uint64_t id) } if (item.decor) { - decoration_redraw(buf, pos.row, pos2.row, item.decor); - free_decoration(item.decor); + decor_redraw(buf, pos.row, pos2.row, item.decor); + decor_free(item.decor); } map_del(uint64_t, uint64_t)(ns->map, id); @@ -238,8 +235,8 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, marktree_del_itr(buf->b_marktree, itr, false); 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); + decor_redraw(buf, it.row1, mark.row, it.decor); + decor_free(it.decor); } map_del(uint64_t, ssize_t)(delete_set, mark.id); continue; @@ -264,8 +261,8 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, } 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); + decor_redraw(buf, mark.row, mark.row, item.decor); + decor_free(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); @@ -283,8 +280,8 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, marktree_del_itr(buf->b_marktree, itr, false); if (decor_id >= 0) { DecorItem it = kv_A(decors, decor_id); - decoration_redraw(buf, it.row1, pos.row, it.decor); - free_decoration(it.decor); + decor_redraw(buf, it.row1, pos.row, it.decor); + decor_free(it.decor); } }); map_clear(uint64_t, ssize_t)(delete_set); @@ -403,7 +400,7 @@ void extmark_free_all(buf_T *buf) map_foreach(buf->b_extmark_index, id, item, { (void)id; - free_decoration(item.decor); + decor_free(item.decor); }); map_free(uint64_t, ExtmarkItem)(buf->b_extmark_index); buf->b_extmark_index = NULL; @@ -737,295 +734,3 @@ uint64_t src2ns(Integer *src_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; - 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 ++) { - int end_off = 0; - if (pos_start.lnum < lnum && lnum < pos_end.lnum) { - // TODO(bfredl): This is quite ad-hoc, but the space between |num| and - // text being highlighted is the indication of \n being part of the - // substituted text. But it would be more consistent to highlight - // a space _after_ the previous line instead (like highlight EOL list - // char) - hl_start = MAX(offset-1, 0); - end_off = 1; - hl_end = 0; - } else if (lnum == pos_start.lnum && lnum < pos_end.lnum) { - hl_start = pos_start.col + offset; - end_off = 1; - hl_end = 0; - } else if (pos_start.lnum < lnum && lnum == pos_end.lnum) { - hl_start = MAX(offset-1, 0); - 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_set(buf, (uint64_t)src_id, 0, - (int)lnum-1, hl_start, (int)lnum-1+end_off, hl_end, - decor, kExtmarkNoUndo); - } -} - -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); - } -} - -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] = { 0 }; - 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) - && item->decor && kv_size(item->decor->virt_text)) { - return &item->decor->virt_text; - } - marktree_itr_next(buf->b_marktree, itr); - } - return NULL; -} - -bool decorations_redraw_reset(buf_T *buf, DecorationRedrawState *state) -{ - state->row = -1; - for (size_t i = 0; i < kv_size(state->active); i++) { - HlRange item = kv_A(state->active, i); - if (item.virt_text_owned) { - clear_virttext(item.virt_text); - xfree(item.virt_text); - } - } - kv_size(state->active) = 0; - return buf->b_extmark_index; -} - - -bool decorations_redraw_start(buf_T *buf, int top_row, - DecorationRedrawState *state) -{ - 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; - } - 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 (!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(decor->virt_text)) - || ((mark.id&MARKTREE_END_FLAG) && altpos.row >= top_row)) { - goto next_mark; - } - - 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, false }; - } else { - range = (HlRange){ mark.row, mark.col, altpos.row, - altpos.col, attr_id, vt, false }; - } - 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 decorations_redraw_line(buf_T *buf, int row, DecorationRedrawState *state) -{ - if (state->row == -1) { - decorations_redraw_start(buf, row, state); - } - state->row = row; - state->col_until = -1; - return true; // TODO(bfredl): be more precise -} - -int decorations_redraw_col(buf_T *buf, int col, DecorationRedrawState *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 (!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(decor->virt_text)) { - goto next_mark; - } - } - - 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, false })); - -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); - } else if (item.virt_text_owned) { - clear_virttext(item.virt_text); - xfree(item.virt_text); - } - } - kv_size(state->active) = j; - state->current = attr; - return attr; -} - -VirtText *decorations_redraw_virt_text(buf_T *buf, DecorationRedrawState *state) -{ - decorations_redraw_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; -} |