aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/api/buffer.c86
-rw-r--r--src/nvim/api/deprecated.c2
-rw-r--r--src/nvim/api/private/helpers.c2
-rw-r--r--src/nvim/buffer.c8
-rw-r--r--src/nvim/buffer_defs.h6
-rw-r--r--src/nvim/charset.c16
-rw-r--r--src/nvim/decoration.c34
-rw-r--r--src/nvim/decoration.h8
-rw-r--r--src/nvim/diff.c28
-rw-r--r--src/nvim/edit.c14
-rw-r--r--src/nvim/eval/funcs.c4
-rw-r--r--src/nvim/extmark.c25
-rw-r--r--src/nvim/extmark_defs.h10
-rw-r--r--src/nvim/fold.c2
-rw-r--r--src/nvim/mouse.c17
-rw-r--r--src/nvim/move.c68
-rw-r--r--src/nvim/normal.c7
-rw-r--r--src/nvim/plines.c32
-rw-r--r--src/nvim/popupmnu.c2
-rw-r--r--src/nvim/screen.c195
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);