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* If {col} is invalid an error is given. *E964*
{props} is a dictionary with these fields: {props} is a dictionary with these fields:
type name of the text property type
length length of text in bytes, can only be used length length of text in bytes, can only be used
for a property that does not continue in for a property that does not continue in
another line; can be zero another line; can be zero
@ -142,9 +143,10 @@ prop_add({lnum}, {col}, {props})
automatically to a negative number; otherwise automatically to a negative number; otherwise
zero is used zero is used
text text to be displayed before {col}, or after the 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* *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: specifies where to display the text:
after after the end of the line after after the end of the line
right right aligned in the window (unless right right aligned in the window (unless
@ -152,14 +154,20 @@ prop_add({lnum}, {col}, {props})
line) line)
below in the next screen line below in the next screen line
When omitted "after" is used. Only one 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, text_wrap when "text" is present and {col} is zero,
specifies what happens if the text doesn't specifies what happens if the text doesn't
fit: fit:
wrap wrap the text to the next line wrap wrap the text to the next line
truncate truncate the text to make it fit truncate truncate the text to make it fit
When omitted "truncate" is used. When omitted "truncate" is used.
type name of the text property type
All fields except "type" are optional. All fields except "type" are optional.
It is an error when both "length" and "end_lnum" or "end_col" 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 #ifdef FEAT_PROP_POPUP
if (lnum > 0) 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, count = get_text_props(wp->w_buffer, lnum, &prop_start, FALSE);
&prop_start, FALSE); cts->cts_text_prop_count = count;
if (cts->cts_text_prop_count > 0) if (count > 0)
{ {
// Make a copy of the properties, so that they are properly // Make a copy of the properties, so that they are properly
// aligned. // aligned. Make it twice as long for the sorting below.
cts->cts_text_props = ALLOC_MULT(textprop_T, cts->cts_text_props = ALLOC_MULT(textprop_T, count * 2);
cts->cts_text_prop_count);
if (cts->cts_text_props == NULL) if (cts->cts_text_props == NULL)
cts->cts_text_prop_count = 0; cts->cts_text_prop_count = 0;
else else
{ {
int i; int i;
mch_memmove(cts->cts_text_props, prop_start, mch_memmove(cts->cts_text_props + count, prop_start,
cts->cts_text_prop_count * sizeof(textprop_T)); count * sizeof(textprop_T));
for (i = 0; i < cts->cts_text_prop_count; ++i) for (i = 0; i < count; ++i)
if (cts->cts_text_props[i].tp_id < 0) if (cts->cts_text_props[i + count].tp_id < 0)
{ {
cts->cts_has_prop_with_text = TRUE; cts->cts_has_prop_with_text = TRUE;
break; break;
@ -987,6 +987,27 @@ init_chartabsize_arg(
VIM_CLEAR(cts->cts_text_props); VIM_CLEAR(cts->cts_text_props);
cts->cts_text_prop_count = 0; 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); int col = (int)(s - line);
garray_T *gap = &wp->w_buffer->b_textprop_text; 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) for (i = 0; i < cts->cts_text_prop_count; ++i)
{ {
textprop_T *tp = cts->cts_text_props + i; textprop_T *tp = cts->cts_text_props + i;
@ -1176,46 +1202,21 @@ win_lbr_chartabsize(
if (p != NULL) if (p != NULL)
{ {
int cells = vim_strsize(p); int cells;
if (tp->tp_col == MAXCOL) if (tp->tp_col == MAXCOL)
{ {
int below = (tp->tp_flags & TP_FLAG_ALIGN_BELOW); int n_extra = (int)STRLEN(p);
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 cells = text_prop_position(wp, tp,
// the text prop, account for that. (vcol + size) % wp->w_width,
if (wp->w_p_list && wp->w_lcs_chars.eol != NUL) &n_extra, &p, NULL, NULL);
++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;
}
#ifdef FEAT_LINEBREAK #ifdef FEAT_LINEBREAK
no_sbr = TRUE; // don't use 'showbreak' now no_sbr = TRUE; // don't use 'showbreak' now
#endif #endif
} }
else
cells = vim_strsize(p);
cts->cts_cur_text_width += cells; cts->cts_cur_text_width += cells;
cts->cts_start_incl = tp->tp_flags & TP_FLAG_START_INCL; cts->cts_start_incl = tp->tp_flags & TP_FLAG_START_INCL;
size += cells; size += cells;
@ -1231,6 +1232,8 @@ win_lbr_chartabsize(
if (tp->tp_col != MAXCOL && tp->tp_col - 1 > col) if (tp->tp_col != MAXCOL && tp->tp_col - 1 > col)
break; break;
} }
if (wp->w_p_list && wp->w_lcs_chars.eol != NUL)
--vcol;
} }
# endif # endif

View File

@ -277,74 +277,123 @@ get_sign_display_info(
} }
#endif #endif
#ifdef FEAT_PROP_POPUP #if defined(FEAT_PROP_POPUP) || defined(PROTO)
static textprop_T *current_text_props = NULL;
static buf_T *current_buf = NULL;
/* /*
* Function passed to qsort() to sort text properties. * Take care of padding, right-align and truncation of virtual text after a
* Return 1 if "s1" has priority over "s2", -1 if the other way around, zero if * line.
* both have the same priority. * 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 int
text_prop_compare(const void *s1, const void *s2) 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; int right = (tp->tp_flags & TP_FLAG_ALIGN_RIGHT);
textprop_T *tp1, *tp2; int below = (tp->tp_flags & TP_FLAG_ALIGN_BELOW);
proptype_T *pt1, *pt2; int wrap = (tp->tp_flags & TP_FLAG_WRAP);
colnr_T col1, col2; 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; if (wrap || right || below || padding > 0 || n_used < *n_extra)
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 flags1 = 0; // Right-align: fill with spaces
int flags2 = 0; 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), // With 'nowrap' add one to show the "extends" character if needed (it
// 1: right, 2: below (comes last) // doesn't show if the text just fits).
if (tp1->tp_flags & TP_FLAG_ALIGN_RIGHT) if (!wp->w_p_wrap
flags1 = 1; && n_used < *n_extra
if (tp1->tp_flags & TP_FLAG_ALIGN_BELOW) && wp->w_lcs_chars.ext != NUL
flags1 = 2; && wp->w_p_list)
if (tp2->tp_flags & TP_FLAG_ALIGN_RIGHT) ++n_used;
flags2 = 1;
if (tp2->tp_flags & TP_FLAG_ALIGN_BELOW) // add 1 for NUL, 2 for when '…' is used
flags2 = 2; if (n_attr != NULL)
if (flags1 != flags2) l = alloc(n_used + added + padding + 3);
return flags1 < flags2 ? 1 : -1; 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 (n_attr == NULL)
if ((tp1->tp_id < 0) != (tp2->tp_id < 0)) return cells;
return tp1->tp_id < 0 ? 1 : -1; return (below && col_with_padding > win_col_off(wp) && !wp->w_p_wrap);
// 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;
} }
#endif #endif
@ -1219,6 +1268,9 @@ win_line(
// Allocate an array for the indexes. // Allocate an array for the indexes.
text_prop_idxs = ALLOC_MULT(int, text_prop_count); text_prop_idxs = ALLOC_MULT(int, text_prop_count);
if (text_prop_idxs == NULL)
VIM_CLEAR(text_props);
area_highlighting = TRUE; area_highlighting = TRUE;
extra_check = TRUE; extra_check = TRUE;
} }
@ -1609,8 +1661,9 @@ win_line(
{ {
int tpi = text_prop_idxs[pi]; int tpi = text_prop_idxs[pi];
if (bcol >= text_props[tpi].tp_col - 1 if (text_props[tpi].tp_col != MAXCOL
+ text_props[tpi].tp_len) && bcol >= text_props[tpi].tp_col - 1
+ text_props[tpi].tp_len)
{ {
if (pi + 1 < text_props_active) if (pi + 1 < text_props_active)
mch_memmove(text_prop_idxs + pi, mch_memmove(text_prop_idxs + pi,
@ -1674,10 +1727,8 @@ win_line(
// Sort the properties on priority and/or starting last. // Sort the properties on priority and/or starting last.
// Then combine the attributes, highest priority last. // Then combine the attributes, highest priority last.
text_prop_follows = FALSE; text_prop_follows = FALSE;
current_text_props = text_props; sort_text_props(wp->w_buffer, text_props,
current_buf = wp->w_buffer; text_prop_idxs, text_props_active);
qsort((void *)text_prop_idxs, (size_t)text_props_active,
sizeof(int), text_prop_compare);
for (pi = 0; pi < text_props_active; ++pi) for (pi = 0; pi < text_props_active; ++pi)
{ {
@ -1704,23 +1755,28 @@ win_line(
&& -text_prop_id && -text_prop_id
<= wp->w_buffer->b_textprop_text.ga_len) <= 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)[ ->b_textprop_text.ga_data)[
-text_prop_id - 1]; -text_prop_id - 1];
// reset the ID in the copy to avoid it being used // reset the ID in the copy to avoid it being used
// again // again
text_props[used_tpi].tp_id = -MAXCOL; tp->tp_id = -MAXCOL;
if (p != NULL) if (p != NULL)
{ {
int right = (text_props[used_tpi].tp_flags int right = (tp->tp_flags
& TP_FLAG_ALIGN_RIGHT); & TP_FLAG_ALIGN_RIGHT);
int below = (text_props[used_tpi].tp_flags int below = (tp->tp_flags
& TP_FLAG_ALIGN_BELOW); & TP_FLAG_ALIGN_BELOW);
int wrap = (text_props[used_tpi].tp_flags int wrap = (tp->tp_flags & TP_FLAG_WRAP);
& 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.p_extra = p;
wlv.c_extra = NUL; wlv.c_extra = NUL;
wlv.c_final = NUL; wlv.c_final = NUL;
@ -1746,72 +1802,30 @@ win_line(
// Keep in sync with where // Keep in sync with where
// textprop_size_after_trunc() is called in // textprop_size_after_trunc() is called in
// win_lbr_chartabsize(). // 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; char_u *prev_p_extra = wlv.p_extra;
int n_used = wlv.n_extra; int start_line;
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 // Take care of padding, right-align and
|| n_used < wlv.n_extra) // 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 // wlv.p_extra was allocated
if (right) vim_free(p_extra_free2);
added -= strsize; p_extra_free2 = wlv.p_extra;
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);
}
} }
// When 'wrap' is off then for "below" we need // When 'wrap' is off then for "below" we need
// to start a new line explictly. // to start a new line explictly.
if (below && wlv.col > win_col_off(wp) if (start_line)
&& !wp->w_p_wrap)
{ {
draw_screen_line(wp, &wlv); 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")); INIT(= N_("E486: Pattern not found: %s"));
EXTERN char e_argument_must_be_positive[] EXTERN char e_argument_must_be_positive[]
INIT(= N_("E487: 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[] EXTERN char e_trailing_characters[]
INIT(= N_("E488: Trailing characters")); INIT(= N_("E488: Trailing characters"));
EXTERN char e_trailing_characters_str[] 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 #ifdef FEAT_PROP_POPUP
EXTERN char e_cannot_specify_both_type_and_types[] EXTERN char e_cannot_specify_both_type_and_types[]
INIT(= N_("E1295: 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 #endif

View File

@ -1,3 +1,4 @@
/* drawline.c */ /* 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); int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int nochange, int number_only);
/* vim: set ft=c : */ /* 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 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 prop_count_below(buf_T *buf, linenr_T lnum);
int count_props(linenr_T lnum, int only_starting, int last_line); 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); 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); 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); 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 typedef struct textprop_S
{ {
colnr_T tp_col; // start column (one based, in bytes) 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_id; // identifier
int tp_type; // property type int tp_type; // property type
int tp_flags; // TP_FLAG_ values 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 |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
|r| +0#0000000#ffffff0@60|A+0#ffffff16#e000002|n|o|t|h|e|r| |e|r@1|o|r @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 |l+0#0000000#ffffff0|i|n|e| |t|w>o| @66
|~+0#4040ff13&| @73 |~+0#4040ff13&| @73
|~| @73 |~| @73
|~| @73 |~| @73
|~| @73
| +0#0000000&@56|2|,|8| @10|A|l@1| | +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| |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 |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 |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 | +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 |e| |m|o|r|e| |t|e|x|t| +0&#ffffff0@48
|c|u|r|s|o|r| >h|e|r|e| @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') call delete('XscriptPropsListMode')
endfunc 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 " vim: shiftwidth=2 sts=2 expandtab

View File

@ -171,6 +171,7 @@ prop_add_one(
char_u *type_name, char_u *type_name,
int id, int id,
char_u *text_arg, char_u *text_arg,
int text_padding_left,
int text_flags, int text_flags,
linenr_T start_lnum, linenr_T start_lnum,
linenr_T end_lnum, linenr_T end_lnum,
@ -264,7 +265,10 @@ prop_add_one(
{ {
length = 1; // text is placed on one character length = 1; // text is placed on one character
if (col == 0) if (col == 0)
{
col = MAXCOL; // after the line col = MAXCOL; // after the line
length += text_padding_left;
}
} }
// Allocate the new line with space for the new property. // 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)); emsg(_(e_invalid_argument));
return; 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) start_col, end_col) == FAIL)
return; return;
} }
@ -428,6 +432,7 @@ prop_add_common(
buf_T *buf = default_buf; buf_T *buf = default_buf;
int id = 0; int id = 0;
char_u *text = NULL; char_u *text = NULL;
int text_padding_left = 0;
int flags = 0; int flags = 0;
if (dict == NULL || !dict_has_key(dict, "type")) 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")) if (dict_has_key(dict, "text_wrap"))
{ {
char_u *p = dict_get_string(dict, "text_wrap", FALSE); char_u *p = dict_get_string(dict, "text_wrap", FALSE);
if (p == NULL) if (p == NULL)
goto theend; goto theend;
if (STRCMP(p, "wrap") == 0) if (STRCMP(p, "wrap") == 0)
@ -529,6 +545,11 @@ prop_add_common(
semsg(_(e_invalid_column_number_nr), (long)start_col); semsg(_(e_invalid_column_number_nr), (long)start_col);
goto theend; 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) if (dict_arg != NULL && get_bufnr_from_arg(dict_arg, &buf) == FAIL)
goto theend; goto theend;
@ -546,7 +567,7 @@ prop_add_common(
// correctly set. // correctly set.
buf->b_has_textprop = TRUE; // this is never reset 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); start_lnum, end_lnum, start_col, end_col);
text = NULL; text = NULL;
@ -655,6 +676,91 @@ count_props(linenr_T lnum, int only_starting, int last_line)
return result; 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". * Find text property "type_id" in the visible lines of window "wp".
* Match "id" when it is > 0. * Match "id" when it is > 0.

View File

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