diff options
author | Björn Linse <bjorn.linse@gmail.com> | 2021-09-26 16:56:19 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-26 16:56:19 +0200 |
commit | d4fa1649fbafe7bedb4bee0b014dd4723ad0ee97 (patch) | |
tree | 55324b1771b69ff551677801e328d76215d1d4cd /src | |
parent | c273eb310098a4ddae577401aca5e07b45107f48 (diff) | |
parent | 392c658d4d0f9457f143748adf98ecd4cdc8dc85 (diff) | |
download | rneovim-d4fa1649fbafe7bedb4bee0b014dd4723ad0ee97.tar.gz rneovim-d4fa1649fbafe7bedb4bee0b014dd4723ad0ee97.tar.bz2 rneovim-d4fa1649fbafe7bedb4bee0b014dd4723ad0ee97.zip |
Merge pull request #15351 from bfredl/virt_line
feat(screen): virtual lines
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/api/buffer.c | 86 | ||||
-rw-r--r-- | src/nvim/api/deprecated.c | 2 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.c | 2 | ||||
-rw-r--r-- | src/nvim/buffer.c | 8 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 6 | ||||
-rw-r--r-- | src/nvim/charset.c | 16 | ||||
-rw-r--r-- | src/nvim/decoration.c | 34 | ||||
-rw-r--r-- | src/nvim/decoration.h | 8 | ||||
-rw-r--r-- | src/nvim/diff.c | 28 | ||||
-rw-r--r-- | src/nvim/edit.c | 14 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 4 | ||||
-rw-r--r-- | src/nvim/extmark.c | 25 | ||||
-rw-r--r-- | src/nvim/extmark_defs.h | 10 | ||||
-rw-r--r-- | src/nvim/fold.c | 2 | ||||
-rw-r--r-- | src/nvim/mouse.c | 17 | ||||
-rw-r--r-- | src/nvim/move.c | 68 | ||||
-rw-r--r-- | src/nvim/normal.c | 7 | ||||
-rw-r--r-- | src/nvim/plines.c | 32 | ||||
-rw-r--r-- | src/nvim/popupmnu.c | 2 | ||||
-rw-r--r-- | src/nvim/screen.c | 195 |
20 files changed, 376 insertions, 190 deletions
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 8973f8fef6..0ef2776263 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -1415,6 +1415,10 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// - end_col : ending col of the mark, 0-based exclusive. /// - hl_group : name of the highlight group used to highlight /// this mark. +/// - hl_eol : when true, for a multiline highlight covering the +/// EOL of a line, continue the highlight for the rest +/// of the screen line (just like for diff and +/// cursorline highlight). /// - virt_text : virtual text to link to this mark. /// A list of [text, highlight] tuples, each representing a /// text chunk with specified highlight. `highlight` element @@ -1442,10 +1446,28 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// default /// - "combine": combine with background text color /// - "blend": blend with background text color. -/// - hl_eol : when true, for a multiline highlight covering the -/// EOL of a line, continue the highlight for the rest -/// of the screen line (just like for diff and -/// cursorline highlight). +/// +/// - virt_lines : virtual lines to add next to this mark +/// This should be an array over lines, where each line in +/// turn is an array over [text, highlight] tuples. In +/// general, buffer and window options do not affect the +/// display of the text. In particular 'wrap' +/// and 'linebreak' options do not take effect, so +/// the number of extra screen lines will always match +/// the size of the array. However the 'tabstop' buffer +/// option is still used for hard tabs. By default lines are +/// placed below the buffer line containing the mark. +/// +/// Note: currently virtual lines are limited to one block +/// per buffer. Thus setting a new mark disables any previous +/// `virt_lines` decoration. However plugins should not rely +/// on this behaviour, as this limitation is planned to be +/// removed. +/// +/// - virt_lines_above: place virtual lines above instead. +/// - virt_lines_leftcol: Place extmarks in the leftmost +/// column of the window, bypassing +/// sign and number columns. /// /// - ephemeral : for use with |nvim_set_decoration_provider| /// callbacks. The mark will only be used for the current @@ -1487,6 +1509,10 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer bool end_right_gravity = false; bool end_gravity_set = false; + VirtLines virt_lines = KV_INITIAL_VALUE; + bool virt_lines_above = false; + bool virt_lines_leftcol = false; + for (size_t i = 0; i < opts.size; i++) { String k = opts.items[i].key; Object *v = &opts.items[i].value; @@ -1584,6 +1610,36 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer if (ERROR_SET(err)) { goto error; } + } else if (strequal("virt_lines", k.data)) { + if (v->type != kObjectTypeArray) { + api_set_error(err, kErrorTypeValidation, + "virt_lines is not an Array"); + goto error; + } + Array a = v->data.array; + for (size_t j = 0; j < a.size; j++) { + if (a.items[j].type != kObjectTypeArray) { + api_set_error(err, kErrorTypeValidation, + "virt_text_line item is not an Array"); + goto error; + } + int dummig; + VirtText jtem = parse_virt_text(a.items[j].data.array, err, &dummig); + kv_push(virt_lines, jtem); + if (ERROR_SET(err)) { + goto error; + } + } + } else if (strequal("virt_lines_above", k.data)) { + virt_lines_above = api_object_to_bool(*v, "virt_lines_above", false, err); + if (ERROR_SET(err)) { + goto error; + } + } else if (strequal("virt_lines_leftcol", k.data)) { + virt_lines_leftcol = api_object_to_bool(*v, "virt_lines_leftcol", false, err); + if (ERROR_SET(err)) { + goto error; + } } else if (strequal("hl_eol", k.data)) { decor.hl_eol = api_object_to_bool(*v, "hl_eol", false, err); if (ERROR_SET(err)) { @@ -1721,9 +1777,23 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer goto error; } - id = extmark_set(buf, (uint64_t)ns_id, id, (int)line, (colnr_T)col, - line2, col2, d, right_gravity, - end_right_gravity, kExtmarkNoUndo); + if (kv_size(virt_lines) && buf->b_virt_line_mark) { + mtpos_t pos = marktree_lookup(buf->b_marktree, buf->b_virt_line_mark, NULL); + clear_virt_lines(buf, pos.row); // handles pos.row == -1 + } + + uint64_t mark = extmark_set(buf, (uint64_t)ns_id, &id, (int)line, (colnr_T)col, + line2, col2, d, right_gravity, + end_right_gravity, kExtmarkNoUndo); + + if (kv_size(virt_lines)) { + buf->b_virt_lines = virt_lines; + buf->b_virt_line_mark = mark; + buf->b_virt_line_pos = -1; + buf->b_virt_line_above = virt_lines_above; + buf->b_virt_line_leftcol = virt_lines_leftcol; + redraw_buf_line_later(buf, MIN(buf->b_ml.ml_line_count, line+1+(virt_lines_above?0:1))); + } } return (Integer)id; @@ -1827,7 +1897,7 @@ Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, In end_line++; } - extmark_set(buf, ns, 0, + extmark_set(buf, ns, NULL, (int)line, (colnr_T)col_start, end_line, (colnr_T)col_end, decor_hl(hl_id), true, false, kExtmarkNoUndo); diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index 21b9db85c0..332fc0ba96 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -150,7 +150,7 @@ Integer nvim_buf_set_virtual_text(Buffer buffer, Integer src_id, Integer line, A decor->virt_text = virt_text; decor->virt_text_width = width; - extmark_set(buf, ns_id, 0, (int)line, 0, -1, -1, decor, true, + extmark_set(buf, ns_id, NULL, (int)line, 0, -1, -1, decor, true, false, kExtmarkNoUndo); return src_id; } diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 24d7c92ce3..193f1dd572 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1627,7 +1627,7 @@ VirtText parse_virt_text(Array chunks, Error *err, int *width) } } - char *text = transstr(str.size > 0 ? str.data : ""); // allocates + char *text = transstr(str.size > 0 ? str.data : "", false); // allocates w += (int)mb_string2cells((char_u *)text); kv_push(virt_text, ((VirtTextChunk){ .text = text, .hl_id = hl_id })); diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index cd84073460..54b5f8283f 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -65,6 +65,7 @@ #include "nvim/os/time.h" #include "nvim/os_unix.h" #include "nvim/path.h" +#include "nvim/plines.h" #include "nvim/quickfix.h" #include "nvim/regexp.h" #include "nvim/screen.h" @@ -820,6 +821,7 @@ static void free_buffer_stuff(buf_T *buf, int free_flags) uc_clear(&buf->b_ucmds); // clear local user commands buf_delete_signs(buf, (char_u *)"*"); // delete any signs extmark_free_all(buf); // delete any extmarks + clear_virt_lines(buf, -1); map_clear_int(buf, MAP_ALL_MODES, true, false); // clear local mappings map_clear_int(buf, MAP_ALL_MODES, true, true); // clear local abbrevs XFREE_CLEAR(buf->b_start_fenc); @@ -3262,7 +3264,7 @@ void maketitle(void) buf_p += MIN(size, SPACE_FOR_FNAME); } else { buf_p += transstr_buf((const char *)path_tail(curbuf->b_fname), - buf_p, SPACE_FOR_FNAME + 1); + buf_p, SPACE_FOR_FNAME + 1, true); } switch (bufIsChanged(curbuf) @@ -3311,7 +3313,7 @@ void maketitle(void) // room for the server name. When there is no room (very long // file name) use (...). if ((size_t)(buf_p - buf) < SPACE_FOR_DIR) { - char *const tbuf = transstr(buf_p); + char *const tbuf = transstr(buf_p, true); const size_t free_space = SPACE_FOR_DIR - (size_t)(buf_p - buf) + 1; const size_t dir_len = xstrlcpy(buf_p, tbuf, free_space); buf_p += MIN(dir_len, free_space - 1); @@ -4657,7 +4659,7 @@ void get_rel_pos(win_T *wp, char_u *buf, int buflen) long below; // number of lines below window above = wp->w_topline - 1; - above += diff_check_fill(wp, wp->w_topline) - wp->w_topfill; + above += win_get_fill(wp, wp->w_topline) - wp->w_topfill; if (wp->w_topline == 1 && wp->w_topfill >= 1) { // All buffer lines are displayed and there is an indication // of filler lines, that can be considered seeing all lines. diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index ba2bcd7223..0264a60117 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -868,6 +868,12 @@ struct file_buffer { Map(uint64_t, ExtmarkItem) b_extmark_index[1]; Map(uint64_t, ExtmarkNs) b_extmark_ns[1]; // extmark namespaces + VirtLines b_virt_lines; + uint64_t b_virt_line_mark; + int b_virt_line_pos; + bool b_virt_line_above; + bool b_virt_line_leftcol; + // array of channel_id:s which have asked to receive updates for this // buffer. kvec_t(uint64_t) update_channels; diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 0ad7dddd33..f899ebf57c 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -310,7 +310,7 @@ void trans_characters(char_u *buf, int bufsize) /// /// @return number of bytes needed to hold a translation of `s`, NUL byte not /// included. -size_t transstr_len(const char *const s) +size_t transstr_len(const char *const s, bool untab) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE { const char *p = s; @@ -331,6 +331,9 @@ size_t transstr_len(const char *const s) } } p += l; + } else if (*p == TAB && !untab) { + len += 1; + p++; } else { const int b2c_l = byte2cells((uint8_t)(*p++)); // Illegal byte sequence may occupy up to 4 characters. @@ -346,9 +349,10 @@ size_t transstr_len(const char *const s) /// @param[out] buf Buffer to which result should be saved. /// @param[in] len Buffer length. Resulting string may not occupy more then /// len - 1 bytes (one for trailing NUL byte). +/// @param[in] untab remove tab characters /// /// @return length of the resulting string, without the NUL byte. -size_t transstr_buf(const char *const s, char *const buf, const size_t len) +size_t transstr_buf(const char *const s, char *const buf, const size_t len, bool untab) FUNC_ATTR_NONNULL_ALL { const char *p = s; @@ -379,6 +383,8 @@ size_t transstr_buf(const char *const s, char *const buf, const size_t len) } } p += l; + } else if (*p == TAB && !untab) { + *buf_p++ = *p++; } else { const char *const tb = (const char *)transchar_byte((uint8_t)(*p++)); const size_t tb_len = strlen(tb); @@ -401,14 +407,14 @@ size_t transstr_buf(const char *const s, char *const buf, const size_t len) /// @param[in] s String to replace characters from. /// /// @return [allocated] translated string -char *transstr(const char *const s) +char *transstr(const char *const s, bool untab) FUNC_ATTR_NONNULL_RET { // Compute the length of the result, taking account of unprintable // multi-byte characters. - const size_t len = transstr_len((const char *)s) + 1; + const size_t len = transstr_len((const char *)s, untab) + 1; char *const buf = xmalloc(len); - transstr_buf(s, buf, len); + transstr_buf(s, buf, len, untab); return buf; } diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index 0f21b47261..7e2b6a666e 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -59,7 +59,7 @@ void bufhl_add_hl_pos_offset(buf_T *buf, int src_id, int hl_id, lpos_T pos_start hl_start = pos_start.col + offset; hl_end = pos_end.col + offset; } - (void)extmark_set(buf, (uint64_t)src_id, 0, + (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); } @@ -412,3 +412,35 @@ void decor_free_all_mem(void) } kv_destroy(decor_providers); } + + +int decor_virtual_lines(win_T *wp, linenr_T lnum) +{ + buf_T *buf = wp->w_buffer; + if (!buf->b_virt_line_mark) { + return 0; + } + if (buf->b_virt_line_pos < 0) { + mtpos_t pos = marktree_lookup(buf->b_marktree, buf->b_virt_line_mark, NULL); + if (pos.row < 0) { + buf->b_virt_line_mark = 0; + } + buf->b_virt_line_pos = pos.row + (buf->b_virt_line_above ? 0 : 1); + } + + return (lnum-1 == buf->b_virt_line_pos) ? (int)kv_size(buf->b_virt_lines) : 0; +} + +void clear_virt_lines(buf_T *buf, int row) +{ + if (row > -1) { + redraw_buf_line_later(buf, MIN(buf->b_ml.ml_line_count, + row+1+(buf->b_virt_line_above?0:1))); + } + for (size_t i = 0; i < kv_size(buf->b_virt_lines); i++) { + clear_virttext(&kv_A(buf->b_virt_lines, i)); + } + kv_destroy(buf->b_virt_lines); // re-initializes + buf->b_virt_line_pos = -1; + buf->b_virt_line_mark = 0; +} diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index 28dabeeada..35f5af87ed 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -7,14 +7,6 @@ // actual Decoration data is in extmark_defs.h -typedef struct { - char *text; - int hl_id; -} VirtTextChunk; - -typedef kvec_t(VirtTextChunk) VirtText; -#define VIRTTEXT_EMPTY ((VirtText)KV_INITIAL_VALUE) - typedef uint16_t DecorPriority; #define DECOR_PRIORITY_BASE 0x1000 diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 93c0d636fa..5c43b2498e 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -1985,26 +1985,6 @@ static int diff_cmp(char_u *s1, char_u *s2) return 0; } -/// Return the number of filler lines above "lnum". -/// -/// @param wp -/// @param lnum -/// -/// @return Number of filler lines above lnum -int diff_check_fill(win_T *wp, linenr_T lnum) -{ - // be quick when there are no filler lines - if (!(diff_flags & DIFF_FILLER)) { - return 0; - } - int n = diff_check(wp, lnum); - - if (n <= 0) { - return 0; - } - return n; -} - /// Set the topline of "towin" to match the position in "fromwin", so that they /// show the same diff'ed lines. /// @@ -2030,6 +2010,7 @@ void diff_set_topline(win_T *fromwin, win_T *towin) } towin->w_topfill = 0; + // search for a change that includes "lnum" in the list of diffblocks. for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) { if (lnum <= dp->df_lnum[fromidx] + dp->df_count[fromidx]) { @@ -2255,6 +2236,13 @@ bool diffopt_closeoff(void) return (diff_flags & DIFF_CLOSE_OFF) != 0; } +// Return true if 'diffopt' contains "filler". +bool diffopt_filler(void) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + return (diff_flags & DIFF_FILLER) != 0; +} + /// Find the difference within a changed line. /// /// @param wp window whose current buffer to check diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 5c4030d8d5..085bbc2409 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -3344,12 +3344,10 @@ static char_u *ins_compl_mode(void) */ static int ins_compl_bs(void) { - char_u *line; - char_u *p; - - line = get_cursor_line_ptr(); - p = line + curwin->w_cursor.col; + char_u *line = get_cursor_line_ptr(); + char_u *p = line + curwin->w_cursor.col; MB_PTR_BACK(line, p); + ptrdiff_t p_off = p - line; // Stop completion when the whole word was deleted. For Omni completion // allow the word to be deleted, we won't match everything. @@ -3369,8 +3367,12 @@ static int ins_compl_bs(void) ins_compl_restart(); } + // ins_compl_restart() calls update_screen(0) which may invalidate the pointer + // TODO(bfredl): get rid of random update_screen() calls deep inside completion logic + line = get_cursor_line_ptr(); + xfree(compl_leader); - compl_leader = vim_strnsave(line + compl_col, (int)(p - line) - compl_col); + compl_leader = vim_strnsave(line + compl_col, (int)p_off - compl_col); ins_compl_new_leader(); if (compl_shown_match != NULL) { // Make sure current match is not a hidden item. diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index e2d485d892..9feecadb6f 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -1802,7 +1802,7 @@ static void f_did_filetype(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_diff_filler(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = diff_check_fill(curwin, tv_get_lnum(argvars)); + rettv->vval.v_number = MAX(0, diff_check(curwin, tv_get_lnum(argvars))); } /* @@ -10801,7 +10801,7 @@ static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_strtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; - rettv->vval.v_string = (char_u *)transstr(tv_get_string(&argvars[0])); + rettv->vval.v_string = (char_u *)transstr(tv_get_string(&argvars[0]), true); } /* diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index 819b8ad3dc..cf01c305d7 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -56,8 +56,8 @@ static ExtmarkNs *buf_ns_ref(buf_T *buf, uint64_t ns_id, bool put) { /// 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, int end_row, +/// @returns the internal mark id +uint64_t extmark_set(buf_T *buf, uint64_t ns_id, uint64_t *idp, int row, colnr_T col, int end_row, colnr_T end_col, Decoration *decor, bool right_gravity, bool end_right_gravity, ExtmarkOp op) { @@ -65,6 +65,7 @@ uint64_t extmark_set(buf_T *buf, uint64_t ns_id, uint64_t id, int row, colnr_T c assert(ns != NULL); mtpos_t old_pos; uint64_t mark = 0; + uint64_t id = idp ? *idp : 0; if (id == 0) { id = ns->free_id++; @@ -118,7 +119,11 @@ revised: if (decor) { decor_redraw(buf, row, end_row > -1 ? end_row : row, decor); } - return id; + + if (idp) { + *idp = id; + } + return mark; } static bool extmark_setraw(buf_T *buf, uint64_t mark, int row, colnr_T col) @@ -169,6 +174,10 @@ bool extmark_del(buf_T *buf, uint64_t ns_id, uint64_t id) decor_free(item.decor); } + if (mark == buf->b_virt_line_mark) { + clear_virt_lines(buf, pos.row); + } + map_del(uint64_t, uint64_t)(ns->map, id); map_del(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark); @@ -227,6 +236,9 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, int l_row, colnr_T l_col, int u_r } uint64_t start_id = mark.id & ~MARKTREE_END_FLAG; + if (start_id == buf->b_virt_line_mark) { + clear_virt_lines(buf, mark.row); + } ExtmarkItem item = map_get(uint64_t, ExtmarkItem)(buf->b_extmark_index, start_id); @@ -496,6 +508,7 @@ void extmark_apply_undo(ExtmarkUndoObject undo_info, bool undo) kExtmarkNoUndo); } } + curbuf->b_virt_line_pos = -1; } @@ -574,7 +587,8 @@ void extmark_splice_impl(buf_T *buf, int start_row, colnr_T start_col, bcount_t 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->deleted_bytes2 = 0; + buf->b_virt_line_pos = -1; buf_updates_send_splice(buf, start_row, start_col, start_byte, old_row, old_col, old_byte, new_row, new_col, new_byte); @@ -665,7 +679,8 @@ void extmark_move_region(buf_T *buf, int start_row, colnr_T start_col, bcount_t int extent_row, colnr_T extent_col, bcount_t extent_byte, int new_row, colnr_T new_col, bcount_t new_byte, ExtmarkOp undo) { - curbuf->deleted_bytes2 = 0; + buf->deleted_bytes2 = 0; + buf->b_virt_line_pos = -1; // 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. diff --git a/src/nvim/extmark_defs.h b/src/nvim/extmark_defs.h index b5d91382ec..2da4f3dc00 100644 --- a/src/nvim/extmark_defs.h +++ b/src/nvim/extmark_defs.h @@ -6,6 +6,16 @@ typedef struct Decoration Decoration; +typedef struct { + char *text; + int hl_id; +} VirtTextChunk; + +typedef kvec_t(VirtTextChunk) VirtText; +#define VIRTTEXT_EMPTY ((VirtText)KV_INITIAL_VALUE) +typedef kvec_t(VirtText) VirtLines; + + typedef struct { uint64_t ns_id; diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 7a017702ee..f22fa449ea 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -1875,7 +1875,7 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldin } } if (*p != NUL) { - p = (char_u *)transstr((const char *)text); + p = (char_u *)transstr((const char *)text, true); xfree(text); text = p; } diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index b65d87e617..cf463fd40a 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -241,7 +241,7 @@ retnomove: if (row < 0) { count = 0; for (first = true; curwin->w_topline > 1; ) { - if (curwin->w_topfill < diff_check(curwin, curwin->w_topline)) { + if (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline)) { count++; } else { count += plines_win(curwin, curwin->w_topline - 1, true); @@ -251,8 +251,8 @@ retnomove: } first = false; (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); - if (curwin->w_topfill < diff_check(curwin, curwin->w_topline)) { - ++curwin->w_topfill; + if (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline)) { + curwin->w_topfill++; } else { --curwin->w_topline; curwin->w_topfill = 0; @@ -283,11 +283,10 @@ retnomove: } if (curwin->w_topfill > 0) { - --curwin->w_topfill; + curwin->w_topfill--; } else { - ++curwin->w_topline; - curwin->w_topfill = - diff_check_fill(curwin, curwin->w_topline); + curwin->w_topline++; + curwin->w_topfill = win_get_fill(curwin, curwin->w_topline); } } check_topfill(curwin, false); @@ -373,12 +372,12 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump) while (row > 0) { // Don't include filler lines in "count" - if (win->w_p_diff + if (win_may_fill(win) && !hasFoldingWin(win, lnum, NULL, NULL, true, NULL)) { if (lnum == win->w_topline) { row -= win->w_topfill; } else { - row -= diff_check_fill(win, lnum); + row -= win_get_fill(win, lnum); } count = plines_win_nofill(win, lnum, true); } else { diff --git a/src/nvim/move.c b/src/nvim/move.c index 2f64f9ab29..ca3dd34204 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -197,7 +197,7 @@ void update_topline(win_T *wp) } } // Check if there are more filler lines than allowed. - if (!check_topline && wp->w_topfill > diff_check_fill(wp, wp->w_topline)) { + if (!check_topline && wp->w_topfill > win_get_fill(wp, wp->w_topline)) { check_topline = true; } @@ -582,8 +582,7 @@ static void curs_rows(win_T *wp) --i; // hold at inserted lines } } - if (valid - && (lnum != wp->w_topline || !wp->w_p_diff)) { + if (valid && (lnum != wp->w_topline || !win_may_fill(wp))) { lnum = wp->w_lines[i].wl_lastlnum + 1; // Cursor inside folded lines, don't count this row if (lnum > wp->w_cursor.lnum) { @@ -854,7 +853,7 @@ void curs_columns(win_T *wp, int may_scroll) if (wp->w_cursor.lnum == wp->w_topline) { wp->w_wrow += wp->w_topfill; } else { - wp->w_wrow += diff_check_fill(wp, wp->w_cursor.lnum); + wp->w_wrow += win_get_fill(wp, wp->w_cursor.lnum); } prev_skipcol = wp->w_skipcol; @@ -1041,7 +1040,7 @@ bool scrolldown(long line_count, int byfold) (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); validate_cursor(); // w_wrow needs to be valid while (line_count-- > 0) { - if (curwin->w_topfill < diff_check(curwin, curwin->w_topline) + if (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline) && curwin->w_topfill < curwin->w_height_inner - 1) { curwin->w_topfill++; done++; @@ -1122,7 +1121,7 @@ bool scrollup(long line_count, int byfold) linenr_T botline = curwin->w_botline; if ((byfold && hasAnyFolding(curwin)) - || curwin->w_p_diff) { + || win_may_fill(curwin)) { // count each sequence of folded lines as one logical line linenr_T lnum = curwin->w_topline; while (line_count--) { @@ -1135,8 +1134,8 @@ bool scrollup(long line_count, int byfold) if (lnum >= curbuf->b_ml.ml_line_count) { break; } - ++lnum; - curwin->w_topfill = diff_check_fill(curwin, lnum); + lnum++; + curwin->w_topfill = win_get_fill(curwin, lnum); } } // approximate w_botline @@ -1207,7 +1206,7 @@ static void max_topfill(void) if (n >= curwin->w_height_inner) { curwin->w_topfill = 0; } else { - curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline); + curwin->w_topfill = win_get_fill(curwin, curwin->w_topline); if (curwin->w_topfill + n > curwin->w_height_inner) { curwin->w_topfill = curwin->w_height_inner - n; } @@ -1220,8 +1219,7 @@ static void max_topfill(void) */ void scrolldown_clamp(void) { - int can_fill = (curwin->w_topfill - < diff_check_fill(curwin, curwin->w_topline)); + int can_fill = (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline)); if (curwin->w_topline <= 1 && !can_fill) { @@ -1302,7 +1300,7 @@ void scrollup_clamp(void) */ static void topline_back(win_T *wp, lineoff_T *lp) { - if (lp->fill < diff_check_fill(wp, lp->lnum)) { + if (lp->fill < win_get_fill(wp, lp->lnum)) { // Add a filler line lp->fill++; lp->height = 1; @@ -1328,7 +1326,7 @@ static void topline_back(win_T *wp, lineoff_T *lp) */ static void botline_forw(win_T *wp, lineoff_T *lp) { - if (lp->fill < diff_check_fill(wp, lp->lnum + 1)) { + if (lp->fill < win_get_fill(wp, lp->lnum + 1)) { // Add a filler line. lp->fill++; lp->height = 1; @@ -1355,8 +1353,8 @@ static void botline_forw(win_T *wp, lineoff_T *lp) static void botline_topline(lineoff_T *lp) { if (lp->fill > 0) { - ++lp->lnum; - lp->fill = diff_check_fill(curwin, lp->lnum) - lp->fill + 1; + lp->lnum++; + lp->fill = win_get_fill(curwin, lp->lnum) - lp->fill + 1; } } @@ -1368,8 +1366,8 @@ static void botline_topline(lineoff_T *lp) static void topline_botline(lineoff_T *lp) { if (lp->fill > 0) { - lp->fill = diff_check_fill(curwin, lp->lnum) - lp->fill + 1; - --lp->lnum; + lp->fill = win_get_fill(curwin, lp->lnum) - lp->fill + 1; + lp->lnum--; } } @@ -1417,7 +1415,7 @@ void scroll_cursor_top(int min_scroll, int always) // "used" already contains the number of filler lines above, don't add it // again. // Hide filler lines above cursor line by adding them to "extra". - int extra = diff_check_fill(curwin, curwin->w_cursor.lnum); + int extra = win_get_fill(curwin, curwin->w_cursor.lnum); /* * Check if the lines from "top" to "bot" fit in the window. If they do, @@ -1475,7 +1473,7 @@ void scroll_cursor_top(int min_scroll, int always) if (curwin->w_topline > curwin->w_cursor.lnum) { curwin->w_topline = curwin->w_cursor.lnum; } - curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline); + curwin->w_topfill = win_get_fill(curwin, curwin->w_topline); if (curwin->w_topfill > 0 && extra > off) { curwin->w_topfill -= extra - off; if (curwin->w_topfill < 0) { @@ -1505,7 +1503,7 @@ void set_empty_rows(win_T *wp, int used) } else { wp->w_empty_rows = wp->w_height_inner - used; if (wp->w_botline <= wp->w_buffer->b_ml.ml_line_count) { - wp->w_filler_rows = diff_check_fill(wp, wp->w_botline); + wp->w_filler_rows = win_get_fill(wp, wp->w_botline); if (wp->w_empty_rows > wp->w_filler_rows) { wp->w_empty_rows -= wp->w_filler_rows; } else { @@ -1590,7 +1588,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) } loff.fill = 0; boff.fill = 0; - fill_below_window = diff_check_fill(curwin, curwin->w_botline) + fill_below_window = win_get_fill(curwin, curwin->w_botline) - curwin->w_filler_rows; while (loff.lnum > 1) { @@ -1835,7 +1833,7 @@ void cursor_correct(void) // Count filler lines below this line as context. if (topline < botline) { - above += diff_check_fill(curwin, topline + 1); + above += win_get_fill(curwin, topline + 1); } ++topline; } @@ -1889,9 +1887,7 @@ int onepage(Direction dir, long count) ? ((curwin->w_topline >= curbuf->b_ml.ml_line_count - so) && curwin->w_botline > curbuf->b_ml.ml_line_count) : (curwin->w_topline == 1 - && curwin->w_topfill == - diff_check_fill(curwin, curwin->w_topline) - )) { + && curwin->w_topfill == win_get_fill(curwin, curwin->w_topline))) { beep_flush(); retval = FAIL; break; @@ -1919,7 +1915,7 @@ int onepage(Direction dir, long count) /* For the overlap, start with the line just below the window * and go upwards. */ loff.lnum = curwin->w_botline; - loff.fill = diff_check_fill(curwin, loff.lnum) + loff.fill = win_get_fill(curwin, loff.lnum) - curwin->w_filler_rows; get_scroll_overlap(&loff, -1); curwin->w_topline = loff.lnum; @@ -1956,8 +1952,7 @@ int onepage(Direction dir, long count) * line at the bottom of the window. Make sure this results in * the same line as before doing CTRL-F. */ loff.lnum = curwin->w_topline - 1; - loff.fill = diff_check_fill(curwin, loff.lnum + 1) - - curwin->w_topfill; + loff.fill = win_get_fill(curwin, loff.lnum + 1) - curwin->w_topfill; get_scroll_overlap(&loff, 1); if (loff.lnum >= curbuf->b_ml.ml_line_count) { @@ -2000,8 +1995,7 @@ int onepage(Direction dir, long count) /* First try using the maximum number of filler lines. If * that's not enough, backup one line. */ loff.fill = curwin->w_topfill; - if (curwin->w_topfill < diff_check_fill(curwin, - curwin->w_topline)) { + if (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline)) { max_topfill(); } if (curwin->w_topfill == loff.fill) { @@ -2146,8 +2140,8 @@ void halfpage(bool flag, linenr_T Prenum) break; } (void)hasFolding(curwin->w_topline, NULL, &curwin->w_topline); - ++curwin->w_topline; - curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline); + curwin->w_topline++; + curwin->w_topfill = win_get_fill(curwin, curwin->w_topline); if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { ++curwin->w_cursor.lnum; @@ -2158,11 +2152,9 @@ void halfpage(bool flag, linenr_T Prenum) curwin->w_valid &= ~(VALID_CROW|VALID_WROW); scrolled += i; - /* - * Correct w_botline for changed w_topline. - * Won't work when there are filler lines. - */ - if (curwin->w_p_diff) { + // Correct w_botline for changed w_topline. + // Won't work when there are filler lines. + if (win_may_fill(curwin)) { curwin->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP); } else { room += i; @@ -2197,7 +2189,7 @@ void halfpage(bool flag, linenr_T Prenum) * scroll the text down */ while (n > 0 && curwin->w_topline > 1) { - if (curwin->w_topfill < diff_check_fill(curwin, curwin->w_topline)) { + if (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline)) { i = 1; n--; curwin->w_topfill++; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index b8a62a8fea..17ec5cd3be 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -5261,16 +5261,15 @@ static void nv_scroll(cmdarg_T *cap) } else { if (cap->cmdchar == 'M') { // Don't count filler lines above the window. - used -= diff_check_fill(curwin, curwin->w_topline) + used -= win_get_fill(curwin, curwin->w_topline) - curwin->w_topfill; validate_botline(curwin); // make sure w_empty_rows is valid half = (curwin->w_height_inner - curwin->w_empty_rows + 1) / 2; for (n = 0; curwin->w_topline + n < curbuf->b_ml.ml_line_count; n++) { // Count half he number of filler lines to be "below this // line" and half to be "above the next line". - if (n > 0 && used + diff_check_fill(curwin, curwin->w_topline - + n) / 2 >= half) { - --n; + if (n > 0 && used + win_get_fill(curwin, curwin->w_topline + n) / 2 >= half) { + n--; break; } used += plines_win(curwin, curwin->w_topline + n, true); diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 28138af13c..5b0418ed92 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -13,6 +13,7 @@ #include "nvim/buffer.h" #include "nvim/charset.h" #include "nvim/cursor.h" +#include "nvim/decoration.h" #include "nvim/diff.h" #include "nvim/fold.h" #include "nvim/func_attr.h" @@ -41,7 +42,34 @@ int plines_win(win_T *wp, linenr_T lnum, bool winheight) { // Check for filler lines above this buffer line. When folded the result // is one line anyway. - return plines_win_nofill(wp, lnum, winheight) + diff_check_fill(wp, lnum); + return plines_win_nofill(wp, lnum, winheight) + win_get_fill(wp, lnum); +} + + +/// Return the number of filler lines above "lnum". +/// +/// @param wp +/// @param lnum +/// +/// @return Number of filler lines above lnum +int win_get_fill(win_T *wp, linenr_T lnum) +{ + int virt_lines = decor_virtual_lines(wp, lnum); + + // be quick when there are no filler lines + if (diffopt_filler()) { + int n = diff_check(wp, lnum); + + if (n > 0) { + return virt_lines+n; + } + } + return virt_lines; +} + +bool win_may_fill(win_T *wp) +{ + return (wp->w_p_diff && diffopt_filler()) || wp->w_buffer->b_virt_line_mark; } /// @param winheight when true limit to window height @@ -107,7 +135,7 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column) { // Check for filler lines above this buffer line. When folded the result // is one line anyway. - int lines = diff_check_fill(wp, lnum); + int lines = win_get_fill(wp, lnum); if (!wp->w_p_wrap) { return lines + 1; diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index aeb2c8c44a..606c03f838 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -514,7 +514,7 @@ void pum_redraw(void) char_u saved = *p; *p = NUL; - st = (char_u *)transstr((const char *)s); + st = (char_u *)transstr((const char *)s, true); *p = saved; if (pum_rl) { diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 5d887444f5..057e800ee2 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -1003,8 +1003,7 @@ static void win_update(win_T *wp, Providers *providers) i = plines_m_win(wp, wp->w_topline, wp->w_lines[0].wl_lnum - 1); // insert extra lines for previously invisible filler lines if (wp->w_lines[0].wl_lnum != wp->w_topline) { - i += diff_check_fill(wp, wp->w_lines[0].wl_lnum) - - wp->w_old_topfill; + i += win_get_fill(wp, wp->w_lines[0].wl_lnum) - wp->w_old_topfill; } if (i != 0 && i < wp->w_grid.Rows - 2) { // less than a screen off // Try to insert the correct number of lines. @@ -1067,7 +1066,7 @@ static void win_update(win_T *wp, Providers *providers) if (wp->w_lines[0].wl_lnum == wp->w_topline) { row += wp->w_old_topfill; } else { - row += diff_check_fill(wp, wp->w_topline); + row += win_get_fill(wp, wp->w_topline); } // ... but don't delete new filler lines. row -= wp->w_topfill; @@ -1101,12 +1100,12 @@ static void win_update(win_T *wp, Providers *providers) break; } } - /* Correct the first entry for filler lines at the top - * when it won't get updated below. */ - if (wp->w_p_diff && bot_start > 0) { - wp->w_lines[0].wl_size = - plines_win_nofill(wp, wp->w_topline, true) - + wp->w_topfill; + + // Correct the first entry for filler lines at the top + // when it won't get updated below. + if (win_may_fill(wp) && bot_start > 0) { + wp->w_lines[0].wl_size = (plines_win_nofill(wp, wp->w_topline, true) + + wp->w_topfill); } } } @@ -1564,7 +1563,7 @@ static void win_update(win_T *wp, Providers *providers) && lnum > wp->w_topline && !(dy_flags & (DY_LASTLINE | DY_TRUNCATE)) && srow + wp->w_lines[idx].wl_size > wp->w_grid.Rows - && diff_check_fill(wp, lnum) == 0) { + && win_get_fill(wp, lnum) == 0) { // This line is not going to fit. Don't draw anything here, // will draw "@ " lines below. row = wp->w_grid.Rows + 1; @@ -1664,7 +1663,7 @@ static void win_update(win_T *wp, Providers *providers) * Don't overwrite it, it can be edited. */ wp->w_botline = lnum + 1; - } else if (diff_check_fill(wp, lnum) >= wp->w_grid.Rows - srow) { + } else if (win_get_fill(wp, lnum) >= wp->w_grid.Rows - srow) { // Window ends in filler lines. wp->w_botline = lnum; wp->w_filler_rows = wp->w_grid.Rows - srow; @@ -1691,7 +1690,7 @@ static void win_update(win_T *wp, Providers *providers) } else { if (eof) { // we hit the end of the file wp->w_botline = buf->b_ml.ml_line_count + 1; - j = diff_check_fill(wp, wp->w_botline); + j = win_get_fill(wp, wp->w_botline); if (j > 0 && !wp->w_botfill) { // Display filler text below last line. win_line() will check // for ml_line_count+1 and only draw filler lines @@ -1866,7 +1865,7 @@ static int compute_foldcolumn(win_T *wp, int col) /// Put a single char from an UTF-8 buffer into a line buffer. /// /// Handles composing chars and arabic shaping state. -static int line_putchar(LineState *s, schar_T *dest, int maxcells, bool rl) +static int line_putchar(buf_T *buf, LineState *s, schar_T *dest, int maxcells, bool rl, int vcol) { const char_u *p = (char_u *)s->p; int cells = utf_ptr2cells(p); @@ -1876,7 +1875,13 @@ static int line_putchar(LineState *s, schar_T *dest, int maxcells, bool rl) return -1; } u8c = utfc_ptr2char(p, u8cc); - if (*p < 0x80 && u8cc[0] == 0) { + if (*p == TAB) { + cells = MIN(tabstop_padding(vcol, buf->b_p_ts, buf->b_p_vts_array), maxcells); + for (int c = 0; c < cells; c++) { + schar_from_ascii(dest[c], ' '); + } + goto done; + } else if (*p < 0x80 && u8cc[0] == 0) { schar_from_ascii(dest[0], *p); s->prev_c = u8c; } else { @@ -1909,6 +1914,7 @@ static int line_putchar(LineState *s, schar_T *dest, int maxcells, bool rl) if (cells > 1) { dest[1][0] = 0; } +done: s->p += c_len; return cells; } @@ -2366,8 +2372,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc filler_lines = 0; area_highlighting = true; } + int virtual_lines = decor_virtual_lines(wp, lnum); + filler_lines += virtual_lines; if (lnum == wp->w_topline) { filler_lines = wp->w_topfill; + virtual_lines = MIN(virtual_lines, filler_lines); } filler_todo = filler_lines; @@ -2895,7 +2904,18 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc if (draw_state == WL_SBR - 1 && n_extra == 0) { draw_state = WL_SBR; - if (filler_todo > 0) { + if (filler_todo > filler_lines - virtual_lines) { + // TODO(bfredl): check this doesn't inhibit TUI-style + // clear-to-end-of-line. + c_extra = ' '; + c_final = NUL; + if (wp->w_p_rl) { + n_extra = col + 1; + } else { + n_extra = grid->Columns - col; + } + char_attr = 0; + } else if (filler_todo > 0) { // draw "deleted" diff line(s) if (char2cells(wp->w_p_fcs_chars.diff) > 1) { c_extra = '-'; @@ -4402,7 +4422,19 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc && !wp->w_p_rl; // Not right-to-left. int draw_col = col - boguscols; - draw_virt_text(buf, win_col_offset, &draw_col, grid->Columns); + if (filler_todo > 0) { + int index = filler_todo - (filler_lines - virtual_lines); + if (index > 0) { + int fpos = kv_size(buf->b_virt_lines) - index; + assert(fpos >= 0); + int offset = buf->b_virt_line_leftcol ? 0 : win_col_offset; + draw_virt_text_item(buf, offset, kv_A(buf->b_virt_lines, fpos), + kHlModeReplace, grid->Columns, offset); + } + } else { + draw_virt_text(buf, win_col_offset, &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) { @@ -4485,67 +4517,80 @@ void draw_virt_text(buf_T *buf, int col_off, int *end_col, int max_col) bool do_eol = state->eol_col > -1; 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->win_col == -1) { - if (item->decor.virt_text_pos == kVTRightAlign) { - right_pos -= item->decor.virt_text_width; - item->win_col = right_pos; - } else if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) { - item->win_col = state->eol_col; - state->eol_col += item->decor.virt_text_width; - } else if (item->decor.virt_text_pos == kVTWinCol) { - item->win_col = MAX(item->decor.col+col_off, 0); - } - } - if (item->win_col < 0) { - continue; - } - VirtText vt = item->decor.virt_text; - HlMode hl_mode = item->decor.hl_mode; - LineState s = LINE_STATE(""); - int virt_attr = 0; - int col = item->win_col; - size_t virt_pos = 0; - item->win_col = -2; // deactivate - - while (col < max_col) { - if (!*s.p) { - if (virt_pos >= kv_size(vt)) { - break; - } - virt_attr = 0; - do { - s.p = kv_A(vt, virt_pos).text; - int hl_id = kv_A(vt, virt_pos).hl_id; - virt_attr = hl_combine_attr(virt_attr, - hl_id > 0 ? syn_id2attr(hl_id) : 0); - virt_pos++; - } while (!s.p && virt_pos < kv_size(vt)); - if (!s.p) { - break; - } - } - int attr; - bool through = false; - if (hl_mode == kHlModeCombine) { - attr = hl_combine_attr(linebuf_attr[col], virt_attr); - } else if (hl_mode == kHlModeBlend) { - through = (*s.p == ' '); - attr = hl_blend_attrs(linebuf_attr[col], virt_attr, &through); - } else { - attr = virt_attr; - } - schar_T dummy[2]; - int cells = line_putchar(&s, through ? dummy : &linebuf_char[col], - max_col-col, false); - linebuf_attr[col++] = attr; - if (cells > 1) { - linebuf_attr[col++] = attr; - } + if (!(item->start_row == state->row && kv_size(item->decor.virt_text))) { + continue; + } + if (item->win_col == -1) { + if (item->decor.virt_text_pos == kVTRightAlign) { + right_pos -= item->decor.virt_text_width; + item->win_col = right_pos; + } else if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) { + item->win_col = state->eol_col; + } else if (item->decor.virt_text_pos == kVTWinCol) { + item->win_col = MAX(item->decor.col+col_off, 0); + } + } + if (item->win_col < 0) { + continue; + } + + int col = draw_virt_text_item(buf, item->win_col, item->decor.virt_text, + item->decor.hl_mode, max_col, item->win_col-col_off); + item->win_col = -2; // deactivate + if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) { + state->eol_col = col+1; + } + + *end_col = MAX(*end_col, col); + } +} + +static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode, + int max_col, int vcol) +{ + LineState s = LINE_STATE(""); + int virt_attr = 0; + size_t virt_pos = 0; + + while (col < max_col) { + if (!*s.p) { + if (virt_pos >= kv_size(vt)) { + break; + } + virt_attr = 0; + do { + s.p = kv_A(vt, virt_pos).text; + int hl_id = kv_A(vt, virt_pos).hl_id; + virt_attr = hl_combine_attr(virt_attr, + hl_id > 0 ? syn_id2attr(hl_id) : 0); + virt_pos++; + } while (!s.p && virt_pos < kv_size(vt)); + if (!s.p) { + break; } - *end_col = MAX(*end_col, col); } + int attr; + bool through = false; + if (hl_mode == kHlModeCombine) { + attr = hl_combine_attr(linebuf_attr[col], virt_attr); + } else if (hl_mode == kHlModeBlend) { + through = (*s.p == ' '); + attr = hl_blend_attrs(linebuf_attr[col], virt_attr, &through); + } else { + attr = virt_attr; + } + schar_T dummy[2]; + int cells = line_putchar(buf, &s, through ? dummy : &linebuf_char[col], + max_col-col, false, vcol); + // if we failed to emit a char, we still need to advance + cells = MAX(cells, 1); + + for (int c = 0; c < cells; c++) { + linebuf_attr[col++] = attr; + } + vcol += cells; } + return col; } /// Determine if dedicated window grid should be used or the default_grid @@ -5489,7 +5534,7 @@ static void win_redr_custom(win_T *wp, bool draw_ruler) ewp->w_p_crb = p_crb_save; // Make all characters printable. - p = (char_u *)transstr((const char *)buf); + p = (char_u *)transstr((const char *)buf, true); len = STRLCPY(buf, p, sizeof(buf)); len = (size_t)len < sizeof(buf) ? len : (int)sizeof(buf) - 1; xfree(p); |