diff options
Diffstat (limited to 'src/nvim/decoration.c')
-rw-r--r-- | src/nvim/decoration.c | 432 |
1 files changed, 262 insertions, 170 deletions
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index c0f3c32f93..e7c76fe38e 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -1,20 +1,21 @@ // 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 +#include "nvim/api/ui.h" +#include "nvim/buffer.h" #include "nvim/decoration.h" #include "nvim/extmark.h" #include "nvim/highlight.h" +#include "nvim/highlight_group.h" #include "nvim/lua/executor.h" +#include "nvim/move.h" #include "nvim/screen.h" -#include "nvim/syntax.h" #include "nvim/vim.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "decoration.c.generated.h" #endif -static PMap(uint64_t) hl_decors; - /// Add highlighting to a buffer, bounded by two cursor positions, /// with an offset. /// @@ -33,9 +34,9 @@ void bufhl_add_hl_pos_offset(buf_T *buf, int src_id, int hl_id, lpos_T pos_start { colnr_T hl_start = 0; colnr_T hl_end = 0; - Decoration *decor = decor_hl(hl_id); + Decoration decor = DECORATION_INIT; + decor.hl_id = hl_id; - decor->priority = DECOR_PRIORITY_BASE; // 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; @@ -45,7 +46,7 @@ void bufhl_add_hl_pos_offset(buf_T *buf, int src_id, int hl_id, lpos_T pos_start // 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); + hl_start = MAX(offset - 1, 0); end_off = 1; hl_end = 0; } else if (lnum == pos_start.lnum && lnum < pos_end.lnum) { @@ -53,69 +54,64 @@ void bufhl_add_hl_pos_offset(buf_T *buf, int src_id, int hl_id, lpos_T pos_start end_off = 1; hl_end = 0; } else if (pos_start.lnum < lnum && lnum == pos_end.lnum) { - hl_start = MAX(offset-1, 0); + 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, NULL, - (int)lnum-1, hl_start, (int)lnum-1+end_off, hl_end, - decor, true, false, kExtmarkNoUndo); - } -} - -Decoration *decor_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; + extmark_set(buf, (uint32_t)src_id, NULL, + (int)lnum - 1, hl_start, (int)lnum - 1 + end_off, hl_end, + &decor, true, false, kExtmarkNoUndo); } - - Decoration *decor = xcalloc(1, sizeof(*decor)); - decor->hl_id = hl_id; - decor->shared = true; - decor->priority = DECOR_PRIORITY_BASE; - *dp = decor; - return decor; } void decor_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 (row2 >= row1) { + if (!decor || decor->hl_id || decor_has_sign(decor) || decor->conceal) { + redraw_buf_range_later(buf, row1 + 1, row2 + 1); + } } - if (kv_size(decor->virt_text)) { - redraw_buf_line_later(buf, row1+1); + if (decor && decor_virt_pos(*decor)) { + redraw_buf_line_later(buf, row1 + 1); } - if (kv_size(decor->virt_lines)) { + if (decor && kv_size(decor->virt_lines)) { redraw_buf_line_later(buf, MIN(buf->b_ml.ml_line_count, - row1+1+(decor->virt_lines_above?0:1))); + row1 + 1 + (decor->virt_lines_above?0:1))); } } void decor_remove(buf_T *buf, int row, int row2, Decoration *decor) { - if (kv_size(decor->virt_lines)) { - assert(buf->b_virt_line_blocks > 0); - buf->b_virt_line_blocks--; - } decor_redraw(buf, row, row2, decor); + if (decor) { + if (kv_size(decor->virt_lines)) { + assert(buf->b_virt_line_blocks > 0); + buf->b_virt_line_blocks--; + } + if (decor_has_sign(decor)) { + assert(buf->b_signs > 0); + buf->b_signs--; + } + if (row2 >= row && decor->sign_text) { + buf_signcols_del_check(buf, row + 1, row2 + 1); + } + } decor_free(decor); } void decor_free(Decoration *decor) { - if (decor && !decor->shared) { + if (decor) { clear_virttext(&decor->virt_text); for (size_t i = 0; i < kv_size(decor->virt_lines); i++) { clear_virttext(&kv_A(decor->virt_lines, i).line); } kv_destroy(decor->virt_lines); + xfree(decor->sign_text); xfree(decor); } } @@ -134,17 +130,16 @@ Decoration *decor_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) { + mtkey_t mark = marktree_itr_current(itr); + if (mark.pos.row < 0 || mark.pos.row > row) { break; - } else if (marktree_decor_level(mark.id) < kDecorLevelVisible) { + } else if (marktree_decor_level(mark) < kDecorLevelVisible) { goto next_mark; } - 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; + Decoration *decor = mark.decor_full; + if ((ns_id == 0 || ns_id == mark.ns) + && decor && kv_size(decor->virt_text)) { + return decor; } next_mark: marktree_itr_next(buf->b_marktree, itr); @@ -163,9 +158,27 @@ bool decor_redraw_reset(buf_T *buf, DecorState *state) } } kv_size(state->active) = 0; - return map_size(buf->b_extmark_index); + return buf->b_marktree->n_keys; +} + +Decoration get_decor(mtkey_t mark) +{ + if (mark.decor_full) { + return *mark.decor_full; + } else { + Decoration fake = DECORATION_INIT; + fake.hl_id = mark.hl_id; + fake.priority = mark.priority; + fake.hl_eol = (mark.flags & MT_FLAG_HL_EOL); + return fake; + } } +/// @return true if decor has a virtual position (virtual text or ui_watched) +static bool decor_virt_pos(Decoration decor) +{ + return kv_size(decor.virt_text) || decor.ui_watched; +} bool decor_redraw_start(buf_T *buf, int top_row, DecorState *state) { @@ -176,42 +189,36 @@ bool decor_redraw_start(buf_T *buf, int top_row, DecorState *state) } 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 + mtkey_t mark = marktree_itr_current(state->itr); + if (mark.pos.row < 0) { // || mark.row > end_row break; } - if ((mark.row < top_row && mark.id&MARKTREE_END_FLAG) - || marktree_decor_level(mark.id) < kDecorLevelVisible) { + if ((mark.pos.row < top_row && mt_end(mark)) + || marktree_decor_level(mark) < kDecorLevelVisible) { goto next_mark; } - 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) { - goto next_mark; - } - Decoration *decor = item->decor; + Decoration decor = get_decor(mark); - mtpos_t altpos = marktree_lookup(buf->b_marktree, - mark.id^MARKTREE_END_FLAG, NULL); + mtpos_t altpos = marktree_get_altpos(buf->b_marktree, mark, NULL); - if ((!(mark.id&MARKTREE_END_FLAG) && altpos.row < top_row - && !kv_size(decor->virt_text)) - || ((mark.id&MARKTREE_END_FLAG) && altpos.row >= top_row)) { + // Exclude start marks if the end mark position is above the top row + // Exclude end marks if we have already added the start mark + if ((mt_start(mark) && altpos.row < top_row && !decor_virt_pos(decor)) + || (mt_end(mark) && altpos.row >= top_row)) { goto next_mark; } - if (mark.id&MARKTREE_END_FLAG) { - decor_add(state, altpos.row, altpos.col, mark.row, mark.col, - decor, false); + if (mt_end(mark)) { + decor_add(state, altpos.row, altpos.col, mark.pos.row, mark.pos.col, + &decor, false, mark.ns, mark.id); } else { if (altpos.row == -1) { - altpos.row = mark.row; - altpos.col = mark.col; + altpos.row = mark.pos.row; + altpos.col = mark.pos.col; } - decor_add(state, mark.row, mark.col, altpos.row, altpos.col, - decor, false); + decor_add(state, mark.pos.row, mark.pos.col, altpos.row, altpos.col, + &decor, false, mark.ns, mark.id); } next_mark: @@ -237,22 +244,22 @@ bool decor_redraw_line(buf_T *buf, int row, DecorState *state) } static void decor_add(DecorState *state, int start_row, int start_col, int end_row, int end_col, - Decoration *decor, bool owned) + Decoration *decor, bool owned, uint64_t ns_id, uint64_t mark_id) { int attr_id = decor->hl_id > 0 ? syn_id2attr(decor->hl_id) : 0; DecorRange range = { start_row, start_col, end_row, end_col, *decor, attr_id, - kv_size(decor->virt_text) && owned, -1 }; + kv_size(decor->virt_text) && owned, -1, ns_id, mark_id }; kv_pushp(state->active); size_t index; - for (index = kv_size(state->active)-1; index > 0; index--) { - DecorRange item = kv_A(state->active, index-1); + for (index = kv_size(state->active) - 1; index > 0; index--) { + DecorRange item = kv_A(state->active, index - 1); if (item.decor.priority <= range.decor.priority) { break; } - kv_A(state->active, index) = kv_A(state->active, index-1); + kv_A(state->active, index) = kv_A(state->active, index - 1); } kv_A(state->active, index) = range; } @@ -266,43 +273,29 @@ int decor_redraw_col(buf_T *buf, int col, int win_col, bool hidden, DecorState * while (true) { // TODO(bfredl): check duplicate entry in "intersection" // branch - mtmark_t mark = marktree_itr_current(state->itr); - if (mark.row < 0 || mark.row > state->row) { + mtkey_t mark = marktree_itr_current(state->itr); + if (mark.pos.row < 0 || mark.pos.row > state->row) { break; - } else if (mark.row == state->row && mark.col > col) { - state->col_until = mark.col-1; + } else if (mark.pos.row == state->row && mark.pos.col > col) { + state->col_until = mark.pos.col - 1; break; } - if ((mark.id&MARKTREE_END_FLAG) - || marktree_decor_level(mark.id) < kDecorLevelVisible) { + if (mt_end(mark) + || marktree_decor_level(mark) < kDecorLevelVisible) { goto next_mark; } - ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index, - mark.id, false); - if (!item || !item->decor) { - goto next_mark; - } - Decoration *decor = item->decor; + Decoration decor = get_decor(mark); - mtpos_t endpos = marktree_lookup(buf->b_marktree, - mark.id|MARKTREE_END_FLAG, NULL); + mtpos_t endpos = marktree_get_altpos(buf->b_marktree, mark, NULL); if (endpos.row == -1) { - endpos.row = mark.row; - endpos.col = mark.col; + endpos = mark.pos; } - if (endpos.row < mark.row - || (endpos.row == mark.row && endpos.col <= mark.col)) { - if (!kv_size(decor->virt_text)) { - goto next_mark; - } - } - - decor_add(state, mark.row, mark.col, endpos.row, endpos.col, - decor, false); + decor_add(state, mark.pos.row, mark.pos.col, endpos.row, endpos.col, + &decor, false, mark.ns, mark.id); next_mark: marktree_itr_next(buf->b_marktree, state->itr); @@ -310,12 +303,16 @@ next_mark: int attr = 0; size_t j = 0; + bool conceal = 0; + int conceal_char = 0; + int conceal_attr = 0; + for (size_t i = 0; i < kv_size(state->active); i++) { DecorRange 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 && kv_size(item.decor.virt_text))) { + if (!(item.start_row >= state->row && decor_virt_pos(item.decor))) { keep = false; } } else { @@ -323,19 +320,27 @@ next_mark: || (item.start_row == state->row && item.start_col <= col)) { active = true; if (item.end_row == state->row && item.end_col > col) { - state->col_until = MIN(state->col_until, item.end_col-1); + 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); + 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 (active && item.decor.conceal) { + conceal = true; + if (item.start_row == state->row && item.start_col == col && item.decor.conceal_char) { + conceal_char = item.decor.conceal_char; + state->col_until = MIN(state->col_until, item.start_col); + conceal_attr = item.attr_id; + } + } if ((item.start_row == state->row && item.start_col <= col) - && kv_size(item.decor.virt_text) + && decor_virt_pos(item.decor) && item.decor.virt_text_pos == kVTOverlay && item.win_col == -1) { item.win_col = (item.decor.virt_text_hide && hidden) ? -2 : win_col; } @@ -347,9 +352,150 @@ next_mark: } kv_size(state->active) = j; state->current = attr; + state->conceal = conceal; + state->conceal_char = conceal_char; + state->conceal_attr = conceal_attr; return attr; } +void decor_redraw_signs(buf_T *buf, int row, int *num_signs, sign_attrs_T sattrs[]) +{ + if (!buf->b_signs) { + return; + } + + MarkTreeIter itr[1] = { 0 }; + marktree_itr_get(buf->b_marktree, row, 0, itr); + + while (true) { + mtkey_t mark = marktree_itr_current(itr); + if (mark.pos.row < 0 || mark.pos.row > row) { + break; + } + + if (mt_end(mark) || marktree_decor_level(mark) < kDecorLevelVisible) { + goto next_mark; + } + + Decoration *decor = mark.decor_full; + + if (!decor || !decor_has_sign(decor)) { + goto next_mark; + } + + int j; + for (j = (*num_signs); j > 0; j--) { + if (sattrs[j].sat_prio <= decor->priority) { + break; + } + sattrs[j] = sattrs[j - 1]; + } + if (j < SIGN_SHOW_MAX) { + memset(&sattrs[j], 0, sizeof(sign_attrs_T)); + sattrs[j].sat_text = decor->sign_text; + if (decor->sign_hl_id != 0) { + sattrs[j].sat_texthl = syn_id2attr(decor->sign_hl_id); + } + if (decor->number_hl_id != 0) { + sattrs[j].sat_numhl = syn_id2attr(decor->number_hl_id); + } + if (decor->line_hl_id != 0) { + sattrs[j].sat_linehl = syn_id2attr(decor->line_hl_id); + } + if (decor->cursorline_hl_id != 0) { + sattrs[j].sat_culhl = syn_id2attr(decor->cursorline_hl_id); + } + sattrs[j].sat_prio = decor->priority; + (*num_signs)++; + } + +next_mark: + marktree_itr_next(buf->b_marktree, itr); + } +} + +// Get the maximum required amount of sign columns needed between row and +// end_row. +int decor_signcols(buf_T *buf, DecorState *state, int row, int end_row, int max) +{ + int count = 0; // count for the number of signs on a given row + int count_remove = 0; // how much to decrement count by when iterating marks for a new row + int signcols = 0; // highest value of count + int currow = -1; // current row + + if (max <= 1 && buf->b_signs >= (size_t)max) { + return max; + } + + if (buf->b_signs == 0) { + return 0; + } + + MarkTreeIter itr[1] = { 0 }; + marktree_itr_get(buf->b_marktree, 0, -1, itr); + while (true) { + mtkey_t mark = marktree_itr_current(itr); + if (mark.pos.row < 0 || mark.pos.row > end_row) { + break; + } + + if ((mark.pos.row < row && mt_end(mark)) + || marktree_decor_level(mark) < kDecorLevelVisible + || !mark.decor_full) { + goto next_mark; + } + + Decoration decor = get_decor(mark); + + if (!decor.sign_text) { + goto next_mark; + } + + if (mark.pos.row > currow) { + count -= count_remove; + count_remove = 0; + currow = mark.pos.row; + } + + if (!mt_paired(mark)) { + if (mark.pos.row >= row) { + count++; + if (count > signcols) { + signcols = count; + if (signcols >= max) { + return max; + } + } + count_remove++; + } + goto next_mark; + } + + mtpos_t altpos = marktree_get_altpos(buf->b_marktree, mark, NULL); + + if (mt_end(mark)) { + if (mark.pos.row >= row && altpos.row <= end_row) { + count_remove++; + } + } else { + if (altpos.row >= row) { + count++; + if (count > signcols) { + signcols = count; + if (signcols >= max) { + return max; + } + } + } + } + +next_mark: + marktree_itr_next(buf->b_marktree, itr); + } + + return signcols; +} + void decor_redraw_end(DecorState *state) { state->buf = NULL; @@ -362,7 +508,7 @@ bool decor_redraw_eol(buf_T *buf, DecorState *state, int *eol_attr, int eol_col) bool has_virttext = false; for (size_t i = 0; i < kv_size(state->active); i++) { DecorRange item = kv_A(state->active, i); - if (item.start_row == state->row && kv_size(item.decor.virt_text)) { + if (item.start_row == state->row && decor_virt_pos(item.decor)) { has_virttext = true; } @@ -373,70 +519,16 @@ bool decor_redraw_eol(buf_T *buf, DecorState *state, int *eol_attr, int eol_col) return has_virttext; } -void decor_add_ephemeral(int start_row, int start_col, int end_row, int end_col, Decoration *decor) +void decor_add_ephemeral(int start_row, int start_col, int end_row, int end_col, Decoration *decor, + uint64_t ns_id, uint64_t mark_id) { if (end_row == -1) { end_row = start_row; end_col = start_col; } - decor_add(&decor_state, start_row, start_col, end_row, end_col, decor, true); + decor_add(&decor_state, start_row, start_col, end_row, end_col, decor, true, ns_id, mark_id); } - -DecorProvider *get_decor_provider(NS ns_id, bool force) -{ - size_t i; - size_t len = kv_size(decor_providers); - for (i = 0; i < len; i++) { - DecorProvider *item = &kv_A(decor_providers, i); - if (item->ns_id == ns_id) { - return item; - } else if (item->ns_id > ns_id) { - break; - } - } - - if (!force) { - return NULL; - } - - // Adding a new provider, so allocate room in the vector - (void)kv_a(decor_providers, len); - if (i < len) { - // New ns_id needs to be inserted between existing providers to maintain - // ordering, so shift other providers with larger ns_id - memmove(&kv_A(decor_providers, i + 1), - &kv_A(decor_providers, i), - (len - i) * sizeof(kv_a(decor_providers, i))); - } - DecorProvider *item = &kv_a(decor_providers, i); - *item = DECORATION_PROVIDER_INIT(ns_id); - - return item; -} - -void decor_provider_clear(DecorProvider *p) -{ - if (p == NULL) { - return; - } - NLUA_CLEAR_REF(p->redraw_start); - NLUA_CLEAR_REF(p->redraw_buf); - NLUA_CLEAR_REF(p->redraw_win); - NLUA_CLEAR_REF(p->redraw_line); - NLUA_CLEAR_REF(p->redraw_end); - p->active = false; -} - -void decor_free_all_mem(void) -{ - for (size_t i = 0; i < kv_size(decor_providers); i++) { - decor_provider_clear(&kv_A(decor_providers, i)); - } - kv_destroy(decor_providers); -} - - int decor_virt_lines(win_T *wp, linenr_T lnum, VirtLines *lines) { buf_T *buf = wp->w_buffer; @@ -452,18 +544,18 @@ int decor_virt_lines(win_T *wp, linenr_T lnum, VirtLines *lines) 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 >= end_row) { + mtkey_t mark = marktree_itr_current(itr); + if (mark.pos.row < 0 || mark.pos.row >= end_row) { break; - } else if (marktree_decor_level(mark.id) < kDecorLevelVirtLine) { + } else if (marktree_decor_level(mark) < kDecorLevelVirtLine) { goto next_mark; } - bool above = mark.row > (int)(lnum - 2); - ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark.id, false); - if (item && item->decor && item->decor->virt_lines_above == above) { - virt_lines += (int)kv_size(item->decor->virt_lines); + bool above = mark.pos.row > (int)(lnum - 2); + Decoration *decor = mark.decor_full; + if (decor && decor->virt_lines_above == above) { + virt_lines += (int)kv_size(decor->virt_lines); if (lines) { - kv_splice(*lines, item->decor->virt_lines); + kv_splice(*lines, decor->virt_lines); } } next_mark: |