summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2022-08-23 18:39:37 +0100
committerBram Moolenaar <Bram@vim.org>2022-08-23 18:39:37 +0100
commitf396ce83eebf6c61596184231d39ce4d41eeac04 (patch)
treed3a624411eaea5463025bd186c39616688cc0876
parentadce965162dd89bf29ee0e5baf53652e7515762c (diff)
patch 9.0.0247: cannot add padding to virtual text without highlightv9.0.0247
Problem: Cannot add padding to virtual text without highlight. Solution: Add the "text_padding_left" argument. (issue #10906)
-rw-r--r--runtime/doc/textprop.txt16
-rw-r--r--src/charset.c93
-rw-r--r--src/drawline.c278
-rw-r--r--src/errors.h4
-rw-r--r--src/proto/drawline.pro1
-rw-r--r--src/proto/textprop.pro1
-rw-r--r--src/structs.h3
-rw-r--r--src/testdir/dumps/Test_prop_right_align_twice_2.dump6
-rw-r--r--src/testdir/dumps/Test_prop_text_with_padding_1.dump8
-rw-r--r--src/testdir/dumps/Test_prop_text_with_padding_2.dump8
-rw-r--r--src/testdir/dumps/Test_prop_text_with_padding_3.dump8
-rw-r--r--src/testdir/dumps/Test_prop_with_text_after_wraps_1.dump4
-rw-r--r--src/testdir/test_textprop.vim50
-rw-r--r--src/textprop.c110
-rw-r--r--src/version.c2
15 files changed, 403 insertions, 189 deletions
diff --git a/runtime/doc/textprop.txt b/runtime/doc/textprop.txt
index 9a148455e0..846915f787 100644
--- a/runtime/doc/textprop.txt
+++ b/runtime/doc/textprop.txt
@@ -126,6 +126,7 @@ prop_add({lnum}, {col}, {props})
If {col} is invalid an error is given. *E964*
{props} is a dictionary with these fields:
+ type name of the text property type
length length of text in bytes, can only be used
for a property that does not continue in
another line; can be zero
@@ -142,9 +143,10 @@ prop_add({lnum}, {col}, {props})
automatically to a negative number; otherwise
zero is used
text text to be displayed before {col}, or after the
- line if {col} is zero
+ line if {col} is zero; prepend and/or append
+ spaces for padding with highlighting
*E1294*
- text_align when "text" is present and {col} is zero
+ text_align when "text" is present and {col} is zero;
specifies where to display the text:
after after the end of the line
right right aligned in the window (unless
@@ -152,14 +154,20 @@ prop_add({lnum}, {col}, {props})
line)
below in the next screen line
When omitted "after" is used. Only one
- "right" property can fit in earch line.
+ "right" property can fit in each line, if
+ there are two ore more these will go in a
+ separate line (still right aligned).
+ text_padding_left *E1296*
+ used when "text" is present and {col} is zero;
+ padding between the end of the text line
+ (leftmost column for "below") and the virtual
+ text, not highlighted
text_wrap when "text" is present and {col} is zero,
specifies what happens if the text doesn't
fit:
wrap wrap the text to the next line
truncate truncate the text to make it fit
When omitted "truncate" is used.
- type name of the text property type
All fields except "type" are optional.
It is an error when both "length" and "end_lnum" or "end_col"
diff --git a/src/charset.c b/src/charset.c
index 32539bf7ac..299de54779 100644
--- a/src/charset.c
+++ b/src/charset.c
@@ -957,26 +957,26 @@ init_chartabsize_arg(
#ifdef FEAT_PROP_POPUP
if (lnum > 0)
{
- char_u *prop_start;
+ char_u *prop_start;
+ int count;
- cts->cts_text_prop_count = get_text_props(wp->w_buffer, lnum,
- &prop_start, FALSE);
- if (cts->cts_text_prop_count > 0)
+ count = get_text_props(wp->w_buffer, lnum, &prop_start, FALSE);
+ cts->cts_text_prop_count = count;
+ if (count > 0)
{
// Make a copy of the properties, so that they are properly
- // aligned.
- cts->cts_text_props = ALLOC_MULT(textprop_T,
- cts->cts_text_prop_count);
+ // aligned. Make it twice as long for the sorting below.
+ cts->cts_text_props = ALLOC_MULT(textprop_T, count * 2);
if (cts->cts_text_props == NULL)
cts->cts_text_prop_count = 0;
else
{
- int i;
+ int i;
- mch_memmove(cts->cts_text_props, prop_start,
- cts->cts_text_prop_count * sizeof(textprop_T));
- for (i = 0; i < cts->cts_text_prop_count; ++i)
- if (cts->cts_text_props[i].tp_id < 0)
+ mch_memmove(cts->cts_text_props + count, prop_start,
+ count * sizeof(textprop_T));
+ for (i = 0; i < count; ++i)
+ if (cts->cts_text_props[i + count].tp_id < 0)
{
cts->cts_has_prop_with_text = TRUE;
break;
@@ -987,6 +987,27 @@ init_chartabsize_arg(
VIM_CLEAR(cts->cts_text_props);
cts->cts_text_prop_count = 0;
}
+ else
+ {
+ int *text_prop_idxs;
+
+ // Need to sort the array to get any truncation right.
+ // Do the sorting in the second part of the array, then
+ // move the sorted props to the first part of the array.
+ text_prop_idxs = ALLOC_MULT(int, count);
+ if (text_prop_idxs != NULL)
+ {
+ for (i = 0; i < count; ++i)
+ text_prop_idxs[i] = i + count;
+ sort_text_props(curbuf, cts->cts_text_props,
+ text_prop_idxs, count);
+ // Here we want the reverse order.
+ for (i = 0; i < count; ++i)
+ cts->cts_text_props[count - i - 1] =
+ cts->cts_text_props[text_prop_idxs[i]];
+ vim_free(text_prop_idxs);
+ }
+ }
}
}
}
@@ -1159,6 +1180,11 @@ win_lbr_chartabsize(
int col = (int)(s - line);
garray_T *gap = &wp->w_buffer->b_textprop_text;
+ // The "$" for 'list' mode will go between the EOL and
+ // the text prop, account for that.
+ if (wp->w_p_list && wp->w_lcs_chars.eol != NUL)
+ ++vcol;
+
for (i = 0; i < cts->cts_text_prop_count; ++i)
{
textprop_T *tp = cts->cts_text_props + i;
@@ -1176,46 +1202,21 @@ win_lbr_chartabsize(
if (p != NULL)
{
- int cells = vim_strsize(p);
+ int cells;
if (tp->tp_col == MAXCOL)
{
- int below = (tp->tp_flags & TP_FLAG_ALIGN_BELOW);
- int right = (tp->tp_flags & TP_FLAG_ALIGN_RIGHT);
- int wrap = (tp->tp_flags & TP_FLAG_WRAP);
- int len = (int)STRLEN(p);
- int n_used = len;
-
- // The "$" for 'list' mode will go between the EOL and
- // the text prop, account for that.
- if (wp->w_p_list && wp->w_lcs_chars.eol != NUL)
- ++vcol;
-
- // Keep in sync with where textprop_size_after_trunc()
- // is called in win_line().
- if (!wrap)
- {
- added = wp->w_width - (vcol + size) % wp->w_width;
- cells = textprop_size_after_trunc(wp,
- below, added, p, &n_used);
- }
- if (below)
- cells += wp->w_width - (vcol + size) % wp->w_width;
- else if (right)
- {
- len = wp->w_width - vcol % wp->w_width;
- if (len > cells + size)
- // add the padding for right-alignment
- cells = len - size;
- else if (len == 0)
- // padding to right-align in the next line
- cells += cells > wp->w_width ? 0
- :wp->w_width - cells;
- }
+ int n_extra = (int)STRLEN(p);
+
+ cells = text_prop_position(wp, tp,
+ (vcol + size) % wp->w_width,
+ &n_extra, &p, NULL, NULL);
#ifdef FEAT_LINEBREAK
no_sbr = TRUE; // don't use 'showbreak' now
#endif
}
+ else
+ cells = vim_strsize(p);
cts->cts_cur_text_width += cells;
cts->cts_start_incl = tp->tp_flags & TP_FLAG_START_INCL;
size += cells;
@@ -1231,6 +1232,8 @@ win_lbr_chartabsize(
if (tp->tp_col != MAXCOL && tp->tp_col - 1 > col)
break;
}
+ if (wp->w_p_list && wp->w_lcs_chars.eol != NUL)
+ --vcol;
}
# endif
diff --git a/src/drawline.c b/src/drawline.c
index b29f5b19ae..039a8c94d7 100644
--- a/src/drawline.c
+++ b/src/drawline.c
@@ -277,74 +277,123 @@ get_sign_display_info(
}
#endif
-#ifdef FEAT_PROP_POPUP
-static textprop_T *current_text_props = NULL;
-static buf_T *current_buf = NULL;
-
+#if defined(FEAT_PROP_POPUP) || defined(PROTO)
/*
- * Function passed to qsort() to sort text properties.
- * Return 1 if "s1" has priority over "s2", -1 if the other way around, zero if
- * both have the same priority.
+ * Take care of padding, right-align and truncation of virtual text after a
+ * line.
+ * if "n_attr" is not NULL then "n_extra" and "p_extra" are adjusted for any
+ * padding, right-align and truncation. Otherwise only the size is computed.
+ * When "n_attr" is NULL returns the number of screen cells used.
+ * Otherwise returns TRUE when drawing continues on the next line.
*/
- static int
-text_prop_compare(const void *s1, const void *s2)
+ int
+text_prop_position(
+ win_T *wp,
+ textprop_T *tp,
+ int vcol, // current screen column
+ int *n_extra, // nr of bytes for virtual text
+ char_u **p_extra, // virtual text
+ int *n_attr, // attribute cells, NULL if not used
+ int *n_attr_skip) // cells to skip attr, NULL if not used
{
- int idx1, idx2;
- textprop_T *tp1, *tp2;
- proptype_T *pt1, *pt2;
- colnr_T col1, col2;
-
- idx1 = *(int *)s1;
- idx2 = *(int *)s2;
- tp1 = &current_text_props[idx1];
- tp2 = &current_text_props[idx2];
- col1 = tp1->tp_col;
- col2 = tp2->tp_col;
- if (col1 == MAXCOL && col2 == MAXCOL)
+ int right = (tp->tp_flags & TP_FLAG_ALIGN_RIGHT);
+ int below = (tp->tp_flags & TP_FLAG_ALIGN_BELOW);
+ int wrap = (tp->tp_flags & TP_FLAG_WRAP);
+ int padding = tp->tp_col == MAXCOL && tp->tp_len > 1
+ ? tp->tp_len - 1 : 0;
+ int col_with_padding = vcol + (below ? 0 : padding);
+ int room = wp->w_width - col_with_padding;
+ int added = room;
+ int n_used = *n_extra;
+ char_u *l = NULL;
+ int strsize = vim_strsize(*p_extra);
+ int cells = wrap ? strsize
+ : textprop_size_after_trunc(wp, below, added, *p_extra, &n_used);
+
+ if (wrap || right || below || padding > 0 || n_used < *n_extra)
{
- int flags1 = 0;
- int flags2 = 0;
-
- // both props add text are after the line, order on 0: after (default),
- // 1: right, 2: below (comes last)
- if (tp1->tp_flags & TP_FLAG_ALIGN_RIGHT)
- flags1 = 1;
- if (tp1->tp_flags & TP_FLAG_ALIGN_BELOW)
- flags1 = 2;
- if (tp2->tp_flags & TP_FLAG_ALIGN_RIGHT)
- flags2 = 1;
- if (tp2->tp_flags & TP_FLAG_ALIGN_BELOW)
- flags2 = 2;
- if (flags1 != flags2)
- return flags1 < flags2 ? 1 : -1;
- }
-
- // property that inserts text has priority over one that doesn't
- if ((tp1->tp_id < 0) != (tp2->tp_id < 0))
- return tp1->tp_id < 0 ? 1 : -1;
+ // Right-align: fill with spaces
+ if (right)
+ added -= cells;
+ if (added < 0
+ || !(right || below)
+ || (below
+ ? (col_with_padding == 0 || !wp->w_p_wrap)
+ : (n_used < *n_extra)))
+ {
+ if (right && (wrap || room < PROP_TEXT_MIN_CELLS))
+ {
+ // right-align on next line instead of wrapping if possible
+ added = wp->w_width - strsize + room;
+ if (added < 0)
+ added = 0;
+ else
+ n_used = *n_extra;
+ }
+ else
+ added = 0;
+ }
- // check highest priority, defined by the type
- pt1 = text_prop_type_by_id(current_buf, tp1->tp_type);
- pt2 = text_prop_type_by_id(current_buf, tp2->tp_type);
- if (pt1 != pt2)
- {
- if (pt1 == NULL)
- return -1;
- if (pt2 == NULL)
- return 1;
- if (pt1->pt_priority != pt2->pt_priority)
- return pt1->pt_priority > pt2->pt_priority ? 1 : -1;
- }
+ // With 'nowrap' add one to show the "extends" character if needed (it
+ // doesn't show if the text just fits).
+ if (!wp->w_p_wrap
+ && n_used < *n_extra
+ && wp->w_lcs_chars.ext != NUL
+ && wp->w_p_list)
+ ++n_used;
+
+ // add 1 for NUL, 2 for when '…' is used
+ if (n_attr != NULL)
+ l = alloc(n_used + added + padding + 3);
+ if (n_attr == NULL || l != NULL)
+ {
+ int off = 0;
- // same priority, one that starts first wins
- if (col1 != col2)
- return col1 < col2 ? 1 : -1;
+ if (n_attr != NULL)
+ {
+ vim_memset(l, ' ', added);
+ off += added;
+ if (padding > 0)
+ {
+ vim_memset(l + off, ' ', padding);
+ off += padding;
+ }
+ vim_strncpy(l + off, *p_extra, n_used);
+ off += n_used;
+ }
+ else
+ {
+ off = added + padding + n_used;
+ cells += added + padding;
+ }
+ if (n_attr != NULL)
+ {
+ if (n_used < *n_extra && wp->w_p_wrap)
+ {
+ char_u *lp = l + off - 1;
- // for a property with text the id can be used as tie breaker
- if (tp1->tp_id < 0)
- return tp1->tp_id > tp2->tp_id ? 1 : -1;
+ if (has_mbyte)
+ {
+ // change last character to '…'
+ lp -= (*mb_head_off)(l, lp);
+ STRCPY(lp, "…");
+ n_used = lp - l + 3 - padding;
+ }
+ else
+ // change last character to '>'
+ *lp = '>';
+ }
+ *p_extra = l;
+ *n_extra = n_used + added + padding;
+ *n_attr = mb_charlen(*p_extra);
+ *n_attr_skip = added + padding;
+ }
+ }
+ }
- return 0;
+ if (n_attr == NULL)
+ return cells;
+ return (below && col_with_padding > win_col_off(wp) && !wp->w_p_wrap);
}
#endif
@@ -1219,6 +1268,9 @@ win_line(
// Allocate an array for the indexes.
text_prop_idxs = ALLOC_MULT(int, text_prop_count);
+ if (text_prop_idxs == NULL)
+ VIM_CLEAR(text_props);
+
area_highlighting = TRUE;
extra_check = TRUE;
}
@@ -1609,8 +1661,9 @@ win_line(
{
int tpi = text_prop_idxs[pi];
- if (bcol >= text_props[tpi].tp_col - 1
- + text_props[tpi].tp_len)
+ if (text_props[tpi].tp_col != MAXCOL
+ && bcol >= text_props[tpi].tp_col - 1
+ + text_props[tpi].tp_len)
{
if (pi + 1 < text_props_active)
mch_memmove(text_prop_idxs + pi,
@@ -1674,10 +1727,8 @@ win_line(
// Sort the properties on priority and/or starting last.
// Then combine the attributes, highest priority last.
text_prop_follows = FALSE;
- current_text_props = text_props;
- current_buf = wp->w_buffer;
- qsort((void *)text_prop_idxs, (size_t)text_props_active,
- sizeof(int), text_prop_compare);
+ sort_text_props(wp->w_buffer, text_props,
+ text_prop_idxs, text_props_active);
for (pi = 0; pi < text_props_active; ++pi)
{
@@ -1704,23 +1755,28 @@ win_line(
&& -text_prop_id
<= wp->w_buffer->b_textprop_text.ga_len)
{
- char_u *p = ((char_u **)wp->w_buffer
+ textprop_T *tp = &text_props[used_tpi];
+ char_u *p = ((char_u **)wp->w_buffer
->b_textprop_text.ga_data)[
-text_prop_id - 1];
// reset the ID in the copy to avoid it being used
// again
- text_props[used_tpi].tp_id = -MAXCOL;
+ tp->tp_id = -MAXCOL;
if (p != NULL)
{
- int right = (text_props[used_tpi].tp_flags
+ int right = (tp->tp_flags
& TP_FLAG_ALIGN_RIGHT);
- int below = (text_props[used_tpi].tp_flags
+ int below = (tp->tp_flags
& TP_FLAG_ALIGN_BELOW);
- int wrap = (text_props[used_tpi].tp_flags
- & TP_FLAG_WRAP);
+ int wrap = (tp->tp_flags & TP_FLAG_WRAP);
+ int padding = tp->tp_col == MAXCOL
+ && tp->tp_len > 1
+ ? tp->tp_len - 1 : 0;
+ // Insert virtual text before the current
+ // character, or add after the end of the line.
wlv.p_extra = p;
wlv.c_extra = NUL;
wlv.c_final = NUL;
@@ -1746,72 +1802,30 @@ win_line(
// Keep in sync with where
// textprop_size_after_trunc() is called in
// win_lbr_chartabsize().
- if ((right || below || !wrap) && wp->w_width > 2)
+ if ((right || below || !wrap || padding > 0)
+ && wp->w_width > 2)
{
- int added = wp->w_width - wlv.col;
- int n_used = wlv.n_extra;
- char_u *l;
- int strsize = wrap
- ? vim_strsize(wlv.p_extra)
- : textprop_size_after_trunc(wp,
- below, added, wlv.p_extra, &n_used);
-
- if (wrap || right || below
- || n_used < wlv.n_extra)
+ char_u *prev_p_extra = wlv.p_extra;
+ int start_line;
+
+ // Take care of padding, right-align and
+ // truncation.
+ // Shared with win_lbr_chartabsize(), must do
+ // exactly the same.
+ start_line = text_prop_position(wp, tp,
+ wlv.col,
+ &wlv.n_extra, &wlv.p_extra,
+ &n_attr, &n_attr_skip);
+ if (wlv.p_extra != prev_p_extra)
{
- // Right-align: fill with spaces
- if (right)
- added -= strsize;
- if (added < 0
- || (below
- ? wlv.col == 0 || !wp->w_p_wrap
- : n_used < wlv.n_extra))
- added = 0;
-
- // With 'nowrap' add one to show the
- // "extends" character if needed (it
- // doesn't show it the text just fits).
- if (!wp->w_p_wrap
- && n_used < wlv.n_extra
- && wp->w_lcs_chars.ext != NUL
- && wp->w_p_list)
- ++n_used;
-
- // add 1 for NUL, 2 for when '…' is used
- l = alloc(n_used + added + 3);
- if (l != NULL)
- {
- vim_memset(l, ' ', added);
- vim_strncpy(l + added, wlv.p_extra,
- n_used);
- if (n_used < wlv.n_extra
- && wp->w_p_wrap)
- {
- char_u *lp = l + added + n_used - 1;
-
- if (has_mbyte)
- {
- // change last character to '…'
- lp -= (*mb_head_off)(l, lp);
- STRCPY(lp, "…");
- n_used = lp - l + 3;
- }
- else
- // change last character to '>'
- *lp = '>';
- }
- vim_free(p_extra_free2);
- wlv.p_extra = p_extra_free2 = l;
- wlv.n_extra = n_used + added;
- n_attr_skip = added;
- n_attr = mb_charlen(wlv.p_extra);
- }
+ // wlv.p_extra was allocated
+ vim_free(p_extra_free2);
+ p_extra_free2 = wlv.p_extra;
}
// When 'wrap' is off then for "below" we need
// to start a new line explictly.
- if (below && wlv.col > win_col_off(wp)
- && !wp->w_p_wrap)
+ if (start_line)
{
draw_screen_line(wp, &wlv);
diff --git a/src/errors.h b/src/errors.h
index 716ec32d8e..99682e4247 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -1218,6 +1218,8 @@ EXTERN char e_pattern_not_found_str[]
INIT(= N_("E486: Pattern not found: %s"));
EXTERN char e_argument_must_be_positive[]
INIT(= N_("E487: Argument must be positive"));
+EXTERN char e_argument_must_be_positive_str[]
+ INIT(= N_("E487: Argument must be positive: %s"));
EXTERN char e_trailing_characters[]
INIT(= N_("E488: Trailing characters"));
EXTERN char e_trailing_characters_str[]
@@ -3319,4 +3321,6 @@ EXTERN char e_can_only_use_text_align_when_column_is_zero[]
#ifdef FEAT_PROP_POPUP
EXTERN char e_cannot_specify_both_type_and_types[]
INIT(= N_("E1295: Cannot specify both 'type' and 'types'"));
+EXTERN char e_can_only_use_left_padding_when_column_is_zero[]
+ INIT(= N_("E1296: Can only use left padding when column is zero"));
#endif
diff --git a/src/proto/drawline.pro b/src/proto/drawline.pro
index 99163acf8e..781cdf15d1 100644
--- a/src/proto/drawline.pro
+++ b/src/proto/drawline.pro
@@ -1,3 +1,4 @@
/* drawline.c */
+int text_prop_position(win_T *wp, textprop_T *tp, int vcol, int *n_extra, char_u **p_extra, int *n_attr, int *n_attr_skip);
int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int nochange, int number_only);
/* vim: set ft=c : */
diff --git a/src/proto/textprop.pro b/src/proto/textprop.pro
index c72b0ef260..2b239673d0 100644
--- a/src/proto/textprop.pro
+++ b/src/proto/textprop.pro
@@ -6,6 +6,7 @@ int prop_add_common(linenr_T start_lnum, colnr_T start_col, dict_T *dict, buf_T
int get_text_props(buf_T *buf, linenr_T lnum, char_u **props, int will_change);
int prop_count_below(buf_T *buf, linenr_T lnum);
int count_props(linenr_T lnum, int only_starting, int last_line);
+void sort_text_props(buf_T *buf, textprop_T *props, int *idxs, int count);
int find_visible_prop(win_T *wp, int type_id, int id, textprop_T *prop, linenr_T *found_lnum);
void add_text_props(linenr_T lnum, textprop_T *text_props, int text_prop_count);
proptype_T *text_prop_type_by_id(buf_T *buf, int id);
diff --git a/src/structs.h b/src/structs.h
index 39c5ef2fc7..5b1a5cc937 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -800,7 +800,8 @@ typedef struct memline
typedef struct textprop_S
{
colnr_T tp_col; // start column (one based, in bytes)
- colnr_T tp_len; // length in bytes
+ colnr_T tp_len; // length in bytes, when tp_id is negative used
+ // for left padding plus one
int tp_id; // identifier
int tp_type; // property type
int tp_flags; // TP_FLAG_ values
diff --git a/src/testdir/dumps/Test_prop_right_align_twice_2.dump b/src/testdir/dumps/Test_prop_right_align_twice_2.dump
index dd41c86cf5..c4cd617f08 100644
--- a/src/testdir/dumps/Test_prop_right_align_twice_2.dump
+++ b/src/testdir/dumps/Test_prop_right_align_twice_2.dump
@@ -1,8 +1,8 @@
-|s+0&#ffffff0|o|m|e| |m|o|r|e| |t|e|x|t|s|o|m|e| |t|e|x|t| |s|o|m|e| |t|e|x|t| |s|o|m|e| |t|e|x|t| |s|o|m|e| |t|e|x|t| +0&#ffd7ff255|n|o|t|h|i|n|g| |h|e|r|e|S+0#ffffff16#e000002|o|m|e| |e|r@1|o
-|r| +0#0000000#ffffff0@60|A+0#ffffff16#e000002|n|o|t|h|e|r| |e|r@1|o|r
+|s+0&#ffffff0|o|m|e| |m|o|r|e| |t|e|x|t|s|o|m|e| |t|e|x|t| |s|o|m|e| |t|e|x|t| |s|o|m|e| |t|e|x|t| |s|o|m|e| |t|e|x|t| +0&#ffd7ff255|n|o|t|h|i|n|g| |h|e|r|e| +0&#ffffff0@8
+@65|S+0#ffffff16#e000002|o|m|e| |e|r@1|o|r
+| +0#0000000#ffffff0@61|A+0#ffffff16#e000002|n|o|t|h|e|r| |e|r@1|o|r
|l+0#0000000#ffffff0|i|n|e| |t|w>o| @66
|~+0#4040ff13&| @73
|~| @73
|~| @73
-|~| @73
| +0#0000000&@56|2|,|8| @10|A|l@1|
diff --git a/src/testdir/dumps/Test_prop_text_with_padding_1.dump b/src/testdir/dumps/Test_prop_text_with_padding_1.dump
new file mode 100644
index 0000000000..1fde5e991b
--- /dev/null
+++ b/src/testdir/dumps/Test_prop_text_with_padding_1.dump
@@ -0,0 +1,8 @@
+>S+0&#ffffff0|o|m|e| |t|e|x|t| |t|o| |a|d@1| |v|i|r|t|u|a|l| |t|e|x|t| |t|o|.| @2|a+0&#ffd7ff255|f|t|e|r| +0&#ffffff0@5|r+0&#ffd7ff255|i|g|h|t| |a|l|i|g|n|e|d
+| +0&#ffffff0@3|b+0&#ffd7ff255|e|l|o|w| |t|h|e| |l|i|n|e| +0&#ffffff0@41
+|s|e|c|o|n|d| |l|i|n|e| @48
+|A|n|o|t|h|e|r| |l|i|n|e| |w|i|t|h| |s|o|m|e| |t|e|x|t| |t|o| |m|a|k|e| |t|h|e| |w|r|a|p|.| @5|r+0&#ffd7ff255|i|g|h|t|m|o|s|t
+|~+0#4040ff13#ffffff0| @58
+|~| @58
+|~| @58
+| +0#0000000&@41|1|,|1| @10|A|l@1|
diff --git a/src/testdir/dumps/Test_prop_text_with_padding_2.dump b/src/testdir/dumps/Test_prop_text_with_padding_2.dump
new file mode 100644
index 0000000000..abedaae6ad
--- /dev/null
+++ b/src/testdir/dumps/Test_prop_text_with_padding_2.dump
@@ -0,0 +1,8 @@
+|x+0&#ffffff0@9|S|o|m|e| |t|e|x|t| |t|o| |a|d@1| |v|i|r|t|u|a|l| |t|e|x|t| |t|o|.| @2|a+0&#ffd7ff255|f|t|e|r| +0&#ffffff0@4|r+0&#ffd7ff255|i|g|…
+| +0&#ffffff0@3|b+0&#ffd7ff255|e|l|o|w| |t|h|e| |l|i|n|e| +0&#ffffff0@41
+|s|e|c|o|n|d| |l|i|n|e| @48
+>x|A|n|o|t|h|e|r| |l|i|n|e| |w|i|t|h| |s|o|m|e| |t|e|x|t| |t|o| |m|a|k|e| |t|h|e| |w|r|a|p|.| @13
+@51|r+0&#ffd7ff255|i|g|h|t|m|o|s|t
+|~+0#4040ff13#ffffff0| @58
+|~| @58
+| +0#0000000&@41|3|,|1| @10|A|l@1|
diff --git a/src/testdir/dumps/Test_prop_text_with_padding_3.dump b/src/testdir/dumps/Test_prop_text_with_padding_3.dump
new file mode 100644
index 0000000000..2911162dc6
--- /dev/null
+++ b/src/testdir/dumps/Test_prop_text_with_padding_3.dump
@@ -0,0 +1,8 @@
+>x+0&#ffffff0@10|S|o|m|e| |t|e|x|t| |t|o| |a|d@1| |v|i|r|t|u|a|l| |t|e|x|t| |t|o|.| @2|a+0&#ffd7ff255|f|t|e|r| +0&#ffffff0@7
+@47|r+0&#ffd7ff255|i|g|h|t| |a|l|i|g|n|e|d
+| +0&#ffffff0@3|b+0&#ffd7ff255|e|l|o|w| |t|h|e| |l|i|n|e| +0&#ffffff0@41
+|s|e|c|o|n|d| |l|i|n|e| @48
+|x|A|n|o|t|h|e|r| |l|i|n|e| |w|i|t|h| |s|o|m|e| |t|e|x|t| |t|o| |m|a|k|e| |t|h|e| |w|r|a|p|.| @13
+@51|r+0&#ffd7ff255|i|g|h|t|m|o|s|t
+|~+0#4040ff13#ffffff0| @58
+| +0#0000000&@41|1|,|1| @10|A|l@1|
diff --git a/src/testdir/dumps/Test_prop_with_text_after_wraps_1.dump b/src/testdir/dumps/Test_prop_with_text_after_wraps_1.dump
index 674ea84c50..7642f8fb57 100644
--- a/src/testdir/dumps/Test_prop_with_text_after_wraps_1.dump
+++ b/src/testdir/dumps/Test_prop_with_text_after_wraps_1.dump
@@ -1,8 +1,8 @@
|o+0&#ffffff0|n|e| |t|w|o| |t|h|r|e@1| |f|o|u|r| |f|i|v|e| |s|i|x| |s|e|v|e|n| +0&#ffff4012|O|N|E| |a|n|d| |T|W|O| |a|n|d| |T|H|R|E@1| |a|n|d|
|F|O|U|R| |a|n|d| |F|I|V|E| +0&#ffffff0@46
-|o|n|e| |t|w|o| |t|h|r|e@1| |f|o|u|r| |f|i|v|e| |s|i|x| |s|e|v|e|n| +0&#ffff4012|o|n|e| |A|N|D| |t|w|o| |A|N|D| |t|h|r|e@1| |A|N|D|
-|f|o|u|r| |A|N|D| |f|i|v|e| +0&#ffffff0@46
|o|n|e| |t|w|o| |t|h|r|e@1| |f|o|u|r| |f|i|v|e| |s|i|x| |s|e|v|e|n| @26
+@20| +0&#ffff4012|o|n|e| |A|N|D| |t|w|o| |A|N|D| |t|h|r|e@1| |A|N|D| |f|o|u|r| |A|N|D| |f|i|v|e
+|o+0&#ffffff0|n|e| |t|w|o| |t|h|r|e@1| |f|o|u|r| |f|i|v|e| |s|i|x| |s|e|v|e|n| @26
| +0&#ffff4012|o|n|e| |A|N|D| |t|w|o| |A|N|D| |t|h|r|e@1| |A|N|D| |f|o|u|r| |A|N|D| |f|i|v|e| |l|e|t|s| |w|r|a|p| |a|f|t|e|r| |s|o|m
|e| |m|o|r|e| |t|e|x|t| +0&#ffffff0@48
|c|u|r|s|o|r| >h|e|r|e| @48
diff --git a/src/testdir/test_textprop.vim b/src/testdir/test_textprop.vim
index a7646209e8..0c7e91d095 100644
--- a/src/testdir/test_textprop.vim
+++ b/src/testdir/test_textprop.vim
@@ -3041,4 +3041,54 @@ func Test_insert_text_list_mode()
call delete('XscriptPropsListMode')
endfunc
+func Test_insert_text_with_padding()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ vim9script
+ setline(1, ['Some text to add virtual text to.',
+ 'second line',
+ 'Another line with some text to make the wrap.'])
+ prop_type_add('theprop', {highlight: 'DiffChange'})
+ prop_add(1, 0, {
+ type: 'theprop',
+ text: 'after',
+ text_align: 'after',
+ text_padding_left: 3,
+ })
+ prop_add(1, 0, {
+ type: 'theprop',
+ text: 'right aligned',
+ text_align: 'right',
+ text_padding_left: 5,
+ })
+ prop_add(1, 0, {
+ type: 'theprop',
+ text: 'below the line',
+ text_align: 'below',
+ text_padding_left: 4,
+ })
+ prop_add(3, 0, {
+ type: 'theprop',
+ text: 'rightmost',
+ text_align: 'right',
+ text_padding_left: 6,
+ text_wrap: 'wrap',
+ })
+ END
+ call writefile(lines, 'XscriptPropsPadded')
+ let buf = RunVimInTerminal('-S XscriptPropsPadded', #{rows: 8, cols: 60})
+ call VerifyScreenDump(buf, 'Test_prop_text_with_padding_1', {})
+
+ call term_sendkeys(buf, "ggixxxxxxxxxx\<Esc>")
+ call term_sendkeys(buf, "3Gix\<Esc>")
+ call VerifyScreenDump(buf, 'Test_prop_text_with_padding_2', {})
+
+ call term_sendkeys(buf, "ggix\<Esc>")
+ call VerifyScreenDump(buf, 'Test_prop_text_with_padding_3', {})
+
+ call StopVimInTerminal(buf)
+ call delete('XscriptPropsPadded')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/textprop.c b/src/textprop.c
index 2fb8a53122..31e1be3bbf 100644
--- a/src/textprop.c
+++ b/src/textprop.c
@@ -171,6 +171,7 @@ prop_add_one(
char_u *type_name,
int id,
char_u *text_arg,
+ int text_padding_left,
int text_flags,
linenr_T start_lnum,
linenr_T end_lnum,
@@ -264,7 +265,10 @@ prop_add_one(
{
length = 1; // text is placed on one character
if (col == 0)
+ {
col = MAXCOL; // after the line
+ length += text_padding_left;
+ }
}
// Allocate the new line with space for the new property.
@@ -390,7 +394,7 @@ f_prop_add_list(typval_T *argvars, typval_T *rettv UNUSED)
emsg(_(e_invalid_argument));
return;
}
- if (prop_add_one(buf, type_name, id, NULL, 0, start_lnum, end_lnum,
+ if (prop_add_one(buf, type_name, id, NULL, 0, 0, start_lnum, end_lnum,
start_col, end_col) == FAIL)
return;
}
@@ -428,6 +432,7 @@ prop_add_common(
buf_T *buf = default_buf;
int id = 0;
char_u *text = NULL;
+ int text_padding_left = 0;
int flags = 0;
if (dict == NULL || !dict_has_key(dict, "type"))
@@ -507,9 +512,20 @@ prop_add_common(
}
}
+ if (dict_has_key(dict, "text_padding_left"))
+ {
+ text_padding_left = dict_get_number(dict, "text_padding_left");
+ if (text_padding_left < 0)
+ {
+ semsg(_(e_argument_must_be_positive_str), "text_padding_left");
+ goto theend;
+ }
+ }
+
if (dict_has_key(dict, "text_wrap"))
{
char_u *p = dict_get_string(dict, "text_wrap", FALSE);
+
if (p == NULL)
goto theend;
if (STRCMP(p, "wrap") == 0)
@@ -529,6 +545,11 @@ prop_add_common(
semsg(_(e_invalid_column_number_nr), (long)start_col);
goto theend;
}
+ if (start_col > 0 && text_padding_left > 0)
+ {
+ emsg(_(e_can_only_use_left_padding_when_column_is_zero));
+ goto theend;
+ }
if (dict_arg != NULL && get_bufnr_from_arg(dict_arg, &buf) == FAIL)
goto theend;
@@ -546,7 +567,7 @@ prop_add_common(
// correctly set.
buf->b_has_textprop = TRUE; // this is never reset
- prop_add_one(buf, type_name, id, text, flags,
+ prop_add_one(buf, type_name, id, text, text_padding_left, flags,
start_lnum, end_lnum, start_col, end_col);
text = NULL;
@@ -655,6 +676,91 @@ count_props(linenr_T lnum, int only_starting, int last_line)
return result;
}
+static textprop_T *text_prop_compare_props;
+static buf_T *text_prop_compare_buf;
+
+/*
+ * Function passed to qsort() to sort text properties.
+ * Return 1 if "s1" has priority over "s2", -1 if the other way around, zero if
+ * both have the same priority.
+ */
+ static int
+text_prop_compare(const void *s1, const void *s2)
+{
+ int idx1, idx2;
+ textprop_T *tp1, *tp2;
+ proptype_T *pt1, *pt2;
+ colnr_T col1, col2;
+
+ idx1 = *(int *)s1;
+ idx2 = *(int *)s2;
+ tp1 = &text_prop_compare_props[idx1];
+ tp2 = &text_prop_compare_props[idx2];
+ col1 = tp1->tp_col;
+ col2 = tp2->tp_col;
+ if (col1 == MAXCOL && col2 == MAXCOL)
+ {
+ int flags1 = 0;
+ int flags2 = 0;
+
+ // both props add text are after the line, order on 0: after (default),
+ // 1: right, 2: below (comes last)
+ if (tp1->tp_flags & TP_FLAG_ALIGN_RIGHT)
+ flags1 = 1;
+ if (tp1->tp_flags & TP_FLAG_ALIGN_BELOW)
+ flags1 = 2;
+ if (tp2->tp_flags & TP_FLAG_ALIGN_RIGHT)
+ flags2 = 1;
+ if (tp2->tp_flags & TP_FLAG_ALIGN_BELOW)
+ flags2 = 2;
+ if (flags1 != flags2)
+ return flags1 < flags2 ? 1 : -1;
+ }
+
+ // property that inserts text has priority over one that doesn't
+ if ((tp1->tp_id < 0) != (tp2->tp_id < 0))
+ return tp1->tp_id < 0 ? 1 : -1;
+
+ // check highest priority, defined by the type
+ pt1 = text_prop_type_by_id(text_prop_compare_buf, tp1->tp_type);
+ pt2 = text_prop_type_by_id(text_prop_compare_buf, tp2->tp_type);
+ if (pt1 != pt2)
+ {
+ if (pt1 == NULL)
+ return -1;
+ if (pt2 == NULL)
+ return 1;
+ if (pt1->pt_priority != pt2->pt_priority)
+ return pt1->pt_priority > pt2->pt_priority ? 1 : -1;
+ }
+
+ // same priority, one that starts first wins
+ if (col1 != col2)
+ return col1 < col2 ? 1 : -1;
+
+ // for a property with text the id can be used as tie breaker
+ if (tp1->tp_id < 0)
+ return tp1->tp_id > tp2->tp_id ? 1 : -1;
+
+ return 0;
+}
+
+/*
+ * Sort "count" text properties using an array if indexes "idxs" into the list
+ * of text props "props" for buffer "buf".
+ */
+ void
+sort_text_props(
+ buf_T *buf,
+ textprop_T *props,
+ int *idxs,
+ int count)
+{
+ text_prop_compare_buf = buf;
+ text_prop_compare_props = props;
+ qsort((void *)idxs, (size_t)count, sizeof(int), text_prop_compare);