diff options
author | Björn Linse <bjorn.linse@gmail.com> | 2021-02-15 14:00:34 +0100 |
---|---|---|
committer | Björn Linse <bjorn.linse@gmail.com> | 2021-02-22 09:40:06 +0100 |
commit | 4781333a7a9669a7e28efc60f23a513c8a47b325 (patch) | |
tree | 17b4d2f628400aba2ad194db3401beaa315f9dee /src | |
parent | d623400cadea173fd6f146e4d3e27443166b6e57 (diff) | |
download | rneovim-4781333a7a9669a7e28efc60f23a513c8a47b325.tar.gz rneovim-4781333a7a9669a7e28efc60f23a513c8a47b325.tar.bz2 rneovim-4781333a7a9669a7e28efc60f23a513c8a47b325.zip |
decorations: allow virt_text overlay at any column
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/api/buffer.c | 58 | ||||
-rw-r--r-- | src/nvim/decoration.c | 91 | ||||
-rw-r--r-- | src/nvim/decoration.h | 8 | ||||
-rw-r--r-- | src/nvim/screen.c | 55 |
4 files changed, 143 insertions, 69 deletions
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 48eef20d59..c1f2842b52 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -1425,6 +1425,11 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, /// - hl_group : name of the highlight group used to highlight /// this mark. /// - virt_text : virtual text to link to this mark. +/// - virt_text_pos : positioning of virtual text. Possible +/// values: +/// - "eol": right after eol character (default) +/// - "overlay": display over the specified column, without +/// shifting the underlying text. /// - ephemeral : for use with |nvim_set_decoration_provider| /// callbacks. The mark will only be used for the current /// redraw cycle, and not be permantently stored in the @@ -1474,8 +1479,9 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, uint64_t id = 0; int line2 = -1, hl_id = 0; DecorPriority priority = DECOR_PRIORITY_BASE; - colnr_T col2 = 0; + colnr_T col2 = -1; VirtText virt_text = KV_INITIAL_VALUE; + VirtTextPos virt_text_pos = kVTEndOfLine; bool right_gravity = true; bool end_right_gravity = false; bool end_gravity_set = false; @@ -1544,6 +1550,22 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, if (ERROR_SET(err)) { goto error; } + } else if (strequal("virt_text_pos", k.data)) { + if (v->type != kObjectTypeString) { + api_set_error(err, kErrorTypeValidation, + "virt_text_pos is not a String"); + goto error; + } + String str = v->data.string; + if (strequal("eol", str.data)) { + virt_text_pos = kVTEndOfLine; + } else if (strequal("overlay", str.data)) { + virt_text_pos = kVTOverlay; + } else { + api_set_error(err, kErrorTypeValidation, + "virt_text_pos: invalid value"); + goto error; + } } else if (strequal("ephemeral", k.data)) { ephemeral = api_object_to_bool(*v, "ephemeral", false, err); if (ERROR_SET(err)) { @@ -1585,7 +1607,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, // Only error out if they try to set end_right_gravity without // setting end_col or end_line - if (line2 == -1 && col2 == 0 && end_gravity_set) { + if (line2 == -1 && col2 == -1 && end_gravity_set) { api_set_error(err, kErrorTypeValidation, "cannot set end_right_gravity " "without setting end_line or end_col"); @@ -1609,30 +1631,28 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, col2 = 0; } + Decoration *decor = NULL, tmp = { 0 }; + + if (kv_size(virt_text) || priority != DECOR_PRIORITY_BASE) { + // TODO(bfredl): this is a bit sketchy. eventually we should + // have predefined decorations for both marks/ephemerals + decor = ephemeral ? &tmp : xcalloc(1, sizeof(*decor)); + decor->hl_id = hl_id; + decor->virt_text = virt_text; + decor->priority = priority; + decor->virt_text_pos = virt_text_pos; + } else if (hl_id) { + decor = decor_hl(hl_id); + } + // TODO(bfredl): synergize these two branches even more if (ephemeral && decor_state.buf == buf) { - int attr_id = hl_id > 0 ? syn_id2attr(hl_id) : 0; - VirtText *vt_allocated = NULL; - if (kv_size(virt_text)) { - vt_allocated = xmalloc(sizeof *vt_allocated); - *vt_allocated = virt_text; - } - decor_add_ephemeral(attr_id, (int)line, (colnr_T)col, - (int)line2, (colnr_T)col2, priority, vt_allocated); + decor_add_ephemeral((int)line, (int)col, line2, col2, decor, 0); } else { if (ephemeral) { api_set_error(err, kErrorTypeException, "not yet implemented"); goto error; } - Decoration *decor = NULL; - if (kv_size(virt_text)) { - decor = xcalloc(1, sizeof(*decor)); - decor->hl_id = hl_id; - decor->virt_text = virt_text; - } else if (hl_id) { - decor = decor_hl(hl_id); - decor->priority = priority; - } id = extmark_set(buf, (uint64_t)ns_id, id, (int)line, (colnr_T)col, line2, col2, decor, right_gravity, diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index f3ee42fab1..a1289f202a 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -188,20 +188,21 @@ bool decor_redraw_start(buf_T *buf, int top_row, DecorState *state) 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, decor->priority, vt, false }; + decor_add(state, altpos.row, altpos.col, mark.row, mark.col, + decor, false, 0); } else { - range = (HlRange){ mark.row, mark.col, altpos.row, - altpos.col, attr_id, decor->priority, vt, false }; + if (altpos.row == -1) { + altpos.row = mark.row; + altpos.col = mark.col; + } + decor_add(state, mark.row, mark.col, altpos.row, altpos.col, + decor, false, 0); } - hlrange_activate(range, state); next_mark: if (marktree_itr_node_done(state->itr)) { + marktree_itr_next(buf->b_marktree, state->itr); break; } marktree_itr_next(buf->b_marktree, state->itr); @@ -220,41 +221,39 @@ bool decor_redraw_line(buf_T *buf, int row, DecorState *state) return true; // TODO(bfredl): be more precise } -static void hlrange_activate(HlRange range, DecorState *state) +static void decor_add(DecorState *state, int start_row, int start_col, + int end_row, int end_col, Decoration *decor, bool owned, + DecorPriority priority) { - // Get size before preparing the push, to have the number of elements - size_t s = kv_size(state->active); - - kv_pushp(state->active); + int attr_id = decor->hl_id > 0 ? syn_id2attr(decor->hl_id) : 0; - size_t dest_index = 0; + HlRange range = { start_row, start_col, end_row, end_col, + attr_id, MAX(priority, decor->priority), + kv_size(decor->virt_text) ? &decor->virt_text : NULL, + decor->virt_text_pos, + kv_size(decor->virt_text) && owned, -1 }; - // Determine insertion dest_index - while (dest_index < s) { - HlRange item = kv_A(state->active, dest_index); - if (item.priority > range.priority) { + kv_pushp(state->active); + size_t index; + for (index = kv_size(state->active)-1; index > 0; index--) { + HlRange item = kv_A(state->active, index-1); + if (item.priority <= range.priority) { break; } - - dest_index++; - } - - // Splice - for (size_t index = s; index > dest_index; index--) { kv_A(state->active, index) = kv_A(state->active, index-1); } - - // Insert - kv_A(state->active, dest_index) = range; + kv_A(state->active, index) = range; } -int decor_redraw_col(buf_T *buf, int col, DecorState *state) +int decor_redraw_col(buf_T *buf, int col, int virt_col, DecorState *state) { if (col <= state->col_until) { return state->current; } state->col_until = MAXCOL; 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) { break; @@ -278,6 +277,11 @@ int decor_redraw_col(buf_T *buf, int col, DecorState *state) } Decoration *decor = item->decor; + if (endpos.row == -1) { + endpos.row = mark.row; + endpos.col = mark.col; + } + if (endpos.row < mark.row || (endpos.row == mark.row && endpos.col <= mark.col)) { if (!kv_size(decor->virt_text)) { @@ -285,12 +289,8 @@ int decor_redraw_col(buf_T *buf, int col, DecorState *state) } } - 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_activate((HlRange){ mark.row, mark.col, - endpos.row, endpos.col, - attr_id, decor->priority, - vt, false }, state); + decor_add(state, mark.row, mark.col, endpos.row, endpos.col, + decor, false, 0); next_mark: marktree_itr_next(buf->b_marktree, state->itr); @@ -310,7 +310,7 @@ next_mark: if (item.start_row < state->row || (item.start_row == state->row && item.start_col <= col)) { active = true; - if (item.end_row == state->row) { + if (item.end_row == state->row && item.end_col > col) { state->col_until = MIN(state->col_until, item.end_col-1); } } else { @@ -322,8 +322,12 @@ next_mark: if (active && item.attr_id > 0) { attr = hl_combine_attr(attr, item.attr_id); } + if ((item.start_row == state->row && item.start_col <= col) + && item.virt_text && item.virt_col == -1) { + item.virt_col = virt_col; + } if (keep) { - kv_A(state->active, j++) = kv_A(state->active, i); + kv_A(state->active, j++) = item; } else if (item.virt_text_owned) { clear_virttext(item.virt_text); xfree(item.virt_text); @@ -341,21 +345,20 @@ void decor_redraw_end(DecorState *state) VirtText *decor_redraw_virt_text(buf_T *buf, DecorState *state) { - decor_redraw_col(buf, MAXCOL, state); + decor_redraw_col(buf, MAXCOL, 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) { + if (item.start_row == state->row && item.virt_text + && item.virt_text_pos == kVTEndOfLine) { return item.virt_text; } } return NULL; } -void decor_add_ephemeral(int attr_id, int start_row, int start_col, - int end_row, int end_col, DecorPriority priority, - VirtText *virt_text) +void decor_add_ephemeral(int start_row, int start_col, int end_row, int end_col, + Decoration *decor, DecorPriority priority) { -hlrange_activate(((HlRange){ start_row, start_col, end_row, end_col, attr_id, - priority, virt_text, virt_text != NULL }), - &decor_state); + decor_add(&decor_state, start_row, start_col, end_row, end_col, decor, true, + priority); } diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index 2533a641dd..47bd9abbc3 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -18,10 +18,16 @@ typedef kvec_t(VirtTextChunk) VirtText; typedef uint16_t DecorPriority; #define DECOR_PRIORITY_BASE 0x1000 +typedef enum { + kVTEndOfLine, + kVTOverlay, +} VirtTextPos; + struct Decoration { int hl_id; // highlight group VirtText virt_text; + VirtTextPos virt_text_pos; // TODO(bfredl): style, signs, etc DecorPriority priority; bool shared; // shared decoration, don't free @@ -35,7 +41,9 @@ typedef struct { int attr_id; DecorPriority priority; VirtText *virt_text; + VirtTextPos virt_text_pos; bool virt_text_owned; + int virt_col; } HlRange; typedef struct { diff --git a/src/nvim/screen.c b/src/nvim/screen.c index c16fe46955..dd28c6fb35 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -143,7 +143,7 @@ long tab_page_click_defs_size = 0; // for line_putchar. Contains the state that needs to be remembered from // putting one character to the next. typedef struct { - const char_u *p; + const char *p; int prev_c; // previous Arabic character int prev_c1; // first composing char for prev_c } LineState; @@ -1857,7 +1857,7 @@ static int compute_foldcolumn(win_T *wp, int col) /// Handles composing chars and arabic shaping state. static int line_putchar(LineState *s, schar_T *dest, int maxcells, bool rl) { - const char_u *p = s->p; + const char_u *p = (char_u *)s->p; int cells = utf_ptr2cells(p); int c_len = utfc_ptr2len(p); int u8c, u8cc[MAX_MCO]; @@ -2870,6 +2870,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, && vcol >= (long)wp->w_virtcol) || (number_only && draw_state > WL_NR)) && filler_todo <= 0) { + draw_virt_text(buf, &col, grid->Columns); grid_put_linebuf(grid, row, 0, col, -grid->Columns, wp->w_p_rl, wp, wp->w_hl_attr_normal, false); // Pretend we have finished updating the window. Except when @@ -3397,7 +3398,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } if (has_decor && v > 0) { - int extmark_attr = decor_redraw_col(wp->w_buffer, (colnr_T)v-1, + int extmark_attr = decor_redraw_col(wp->w_buffer, (colnr_T)v-1, off, &decor_state); if (extmark_attr != 0) { if (!attr_pri) { @@ -3919,7 +3920,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int i; size_t virt_pos = 0; - LineState s = LINE_STATE((char_u *)""); + LineState s = LINE_STATE(""); int virt_attr = 0; // Make sure alignment is the same regardless @@ -3963,7 +3964,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, if (do_virttext && !delay_virttext) { if (*s.p == NUL) { if (virt_pos < virt_text.size) { - s.p = (char_u *)kv_A(virt_text, virt_pos).text; + s.p = kv_A(virt_text, virt_pos).text; int hl_id = kv_A(virt_text, virt_pos).hl_id; virt_attr = hl_id > 0 ? syn_id2attr(hl_id) : 0; virt_pos++; @@ -4029,6 +4030,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, col += n; } } + + draw_virt_text(buf, &col, grid->Columns); grid_put_linebuf(grid, row, 0, col, grid->Columns, wp->w_p_rl, wp, wp->w_hl_attr_normal, false); row++; @@ -4247,7 +4250,10 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, && (grid->Columns == Columns // Window spans the width of the screen, || ui_has(kUIMultigrid)) // or has dedicated grid. && !wp->w_p_rl; // Not right-to-left. - grid_put_linebuf(grid, row, 0, col - boguscols, grid->Columns, wp->w_p_rl, + + int draw_col = col - boguscols; + draw_virt_text(buf, &draw_col, grid->Columns); + grid_put_linebuf(grid, row, 0, draw_col, grid->Columns, wp->w_p_rl, wp, wp->w_hl_attr_normal, wrap); if (wrap) { ScreenGrid *current_grid = grid; @@ -4323,6 +4329,43 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, return row; } +void draw_virt_text(buf_T *buf, int *end_col, int max_col) +{ + DecorState *state = &decor_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 + && item->virt_text_pos == kVTOverlay + && item->virt_col >= 0) { + VirtText vt = *item->virt_text; + LineState s = LINE_STATE(""); + int virt_attr = 0; + int col = item->virt_col; + size_t virt_pos = 0; + item->virt_col = -2; // deactivate + + while (col < max_col) { + if (!*s.p) { + if (virt_pos == kv_size(vt)) { + break; + } + s.p = kv_A(vt, virt_pos).text; + int hl_id = kv_A(vt, virt_pos).hl_id; + virt_attr = hl_id > 0 ? syn_id2attr(hl_id) : 0; + virt_pos++; + continue; + } + int cells = line_putchar(&s, &linebuf_char[col], 2, false); + linebuf_attr[col++] = virt_attr; + if (cells > 1) { + linebuf_attr[col++] = virt_attr; + } + } + *end_col = MAX(*end_col, col); + } + } +} + /// Determine if dedicated window grid should be used or the default_grid /// /// If UI did not request multigrid support, draw all windows on the |