0
0
mirror of https://github.com/vim/vim.git synced 2025-08-26 20:03:41 -04:00

patch 9.0.0247: cannot add padding to virtual text without highlight

Problem:    Cannot add padding to virtual text without highlight.
Solution:   Add the "text_padding_left" argument. (issue #10906)
This commit is contained in:
Bram Moolenaar 2022-08-23 18:39:37 +01:00
parent adce965162
commit f396ce83ee
15 changed files with 401 additions and 187 deletions

View File

@ -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"

View File

@ -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;
int n_extra = (int)STRLEN(p);
// 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;
}
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

View File

@ -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;
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);
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)
if (wrap || right || below || padding > 0 || n_used < *n_extra)
{
int flags1 = 0;
int flags2 = 0;
// 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;
}
// 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;
// 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;
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;
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;
}
}
}
// 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(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;
}
// 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;
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);
char_u *prev_p_extra = wlv.p_extra;
int start_line;
if (wrap || right || below
|| n_used < wlv.n_extra)
// 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);

View File

@ -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

View File

@ -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 : */

View File

@ -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);

View File

@ -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

View File

@ -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|

View File

@ -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|

View File

@ -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|

View File

@ -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|

View File

@ -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

View File

@ -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

View File

@ -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);
}
/*
* Find text property "type_id" in the visible lines of window "wp".
* Match "id" when it is > 0.

View File

@ -731,6 +731,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
247,
/**/
246,
/**/