0
0
mirror of https://github.com/vim/vim.git synced 2025-07-26 11:04:33 -04:00

patch 9.0.0438: cannot put virtual text above a line

Problem:    Cannot put virtual text above a line.
Solution:   Add the "above" value for "text_align".
This commit is contained in:
Bram Moolenaar 2022-09-10 20:00:56 +01:00
parent 55e9366e32
commit 04e0ed1ddf
11 changed files with 198 additions and 103 deletions

View File

@ -153,6 +153,7 @@ prop_add({lnum}, {col}, {props})
the text wraps to the next screen
line)
below in the next screen line
above just above the line
When omitted "after" is used. Only one
"right" property can fit in each line, if
there are two ore more these will go in a

View File

@ -1069,40 +1069,6 @@ lbr_chartabsize_adv(chartabsize_T *cts)
return retval;
}
#if defined(FEAT_PROP_POPUP) || defined(PROTO)
/*
* Return the cell size of virtual text after truncation.
*/
int
textprop_size_after_trunc(
win_T *wp,
int below,
int added,
char_u *text,
int *n_used_ptr)
{
int space = below ? wp->w_width : added;
int len = (int)STRLEN(text);
int strsize = 0;
int n_used;
// if the remaining size is to small wrap
// anyway and use the next line
if (space < PROP_TEXT_MIN_CELLS)
space += wp->w_width;
for (n_used = 0; n_used < len; n_used += (*mb_ptr2len)(text + n_used))
{
int clen = ptr2cells(text + n_used);
if (strsize + clen > space)
break;
strsize += clen;
}
*n_used_ptr = n_used;
return strsize;
}
#endif
/*
* Return the screen size of the character indicated by "cts".
* "cts->cts_cur_text_width" is set to the extra size for a text property that
@ -1142,6 +1108,7 @@ win_lbr_chartabsize(
#if defined(FEAT_PROP_POPUP)
cts->cts_cur_text_width = 0;
cts->cts_first_char = 0;
#endif
#if defined(FEAT_LINEBREAK) || defined(FEAT_PROP_POPUP)
@ -1194,9 +1161,12 @@ win_lbr_chartabsize(
if (tp->tp_id < 0
&& ((tp->tp_col - 1 >= col
&& tp->tp_col - 1 < col + charlen)
|| (tp->tp_col == MAXCOL && (s[0] == NUL || s[1] == NUL)
&& cts->cts_with_trailing))
&& -tp->tp_id - 1 < gap->ga_len)
|| (tp->tp_col == MAXCOL
&& ((tp->tp_flags & TP_FLAG_ALIGN_ABOVE)
? col == 0
: (s[0] == NUL || s[1] == NUL)
&& cts->cts_with_trailing)))
&& tp->tp_id - 1 < gap->ga_len)
{
char_u *p = ((char_u **)gap->ga_data)[-tp->tp_id - 1];
@ -1218,6 +1188,8 @@ win_lbr_chartabsize(
else
cells = vim_strsize(p);
cts->cts_cur_text_width += cells;
if (tp->tp_flags & TP_FLAG_ALIGN_ABOVE)
cts->cts_first_char += cells;
cts->cts_start_incl = tp->tp_flags & TP_FLAG_START_INCL;
size += cells;
if (*s == TAB)
@ -1564,6 +1536,11 @@ getvcol(
#endif
break;
}
#ifdef FEAT_PROP_POPUP
if (cursor == &wp->w_virtcol && cts.cts_ptr == cts.cts_line)
// do not count the virtual text above for w_curswant
wp->w_virtcol_first_char = cts.cts_first_char;
#endif
if (posptr != NULL && cts.cts_ptr >= posptr)
// character at pos->col

View File

@ -278,6 +278,38 @@ get_sign_display_info(
#endif
#if defined(FEAT_PROP_POPUP) || defined(PROTO)
/*
* Return the cell size of virtual text after truncation.
*/
static int
textprop_size_after_trunc(
win_T *wp,
int flags, // TP_FLAG_ALIGN_*
int added,
char_u *text,
int *n_used_ptr)
{
int space = (flags & (TP_FLAG_ALIGN_BELOW | TP_FLAG_ALIGN_ABOVE))
? wp->w_width : added;
int len = (int)STRLEN(text);
int strsize = 0;
int n_used;
// if the remaining size is to small wrap anyway and use the next line
if (space < PROP_TEXT_MIN_CELLS)
space += wp->w_width;
for (n_used = 0; n_used < len; n_used += (*mb_ptr2len)(text + n_used))
{
int clen = ptr2cells(text + n_used);
if (strsize + clen > space)
break;
strsize += clen;
}
*n_used_ptr = n_used;
return strsize;
}
/*
* Take care of padding, right-align and truncation of virtual text after a
* line.
@ -297,6 +329,7 @@ text_prop_position(
int *n_attr_skip) // cells to skip attr, NULL if not used
{
int right = (tp->tp_flags & TP_FLAG_ALIGN_RIGHT);
int above = (tp->tp_flags & TP_FLAG_ALIGN_ABOVE);
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
@ -304,36 +337,45 @@ text_prop_position(
int col_with_padding = vcol + (below ? 0 : padding);
int col_off = 0;
int room = wp->w_width - col_with_padding;
int added = room;
int before = room; // spaces before the text
int after = 0; // spaces after the text
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);
int cells = wrap ? strsize : textprop_size_after_trunc(wp,
tp->tp_flags, before, *p_extra, &n_used);
if (wrap || right || below || padding > 0 || n_used < *n_extra)
if (wrap || right || above || below || padding > 0 || n_used < *n_extra)
{
// 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 (above)
{
if (right && (wrap || room < PROP_TEXT_MIN_CELLS))
before = 0;
after = wp->w_width - cells;
}
else
{
// Right-align: fill with before
if (right)
before -= cells;
if (before < 0
|| !(right || below)
|| (below
? (col_with_padding == 0 || !wp->w_p_wrap)
: (n_used < *n_extra)))
{
// right-align on next line instead of wrapping if possible
col_off = win_col_off(wp) + win_col_off2(wp);
added = wp->w_width - col_off - strsize + room;
if (added < 0)
added = 0;
if (right && (wrap || room < PROP_TEXT_MIN_CELLS))
{
// right-align on next line instead of wrapping if possible
col_off = win_col_off(wp) + win_col_off2(wp);
before = wp->w_width - col_off - strsize + room;
if (before < 0)
before = 0;
else
n_used = *n_extra;
}
else
n_used = *n_extra;
before = 0;
}
else
added = 0;
}
// With 'nowrap' add one to show the "extends" character if needed (it
@ -346,15 +388,15 @@ text_prop_position(
// add 1 for NUL, 2 for when '…' is used
if (n_attr != NULL)
l = alloc(n_used + added + padding + 3);
l = alloc(n_used + before + after + padding + 3);
if (n_attr == NULL || l != NULL)
{
int off = 0;
if (n_attr != NULL)
{
vim_memset(l, ' ', added);
off += added;
vim_memset(l, ' ', before);
off += before;
if (padding > 0)
{
vim_memset(l + off, ' ', padding);
@ -365,8 +407,8 @@ text_prop_position(
}
else
{
off = added + padding + n_used;
cells += added + padding;
off = before + after + padding + n_used;
cells += before + after + padding;
}
if (n_attr != NULL)
{
@ -385,10 +427,16 @@ text_prop_position(
// change last character to '>'
*lp = '>';
}
else if (after > 0)
{
vim_memset(l + off, ' ', after);
l[off + after] = NUL;
}
*p_extra = l;
*n_extra = n_used + added + padding;
*n_extra = n_used + before + after + padding;
*n_attr = mb_charlen(*p_extra);
*n_attr_skip = added + padding + col_off;
*n_attr_skip = before + padding + col_off;
}
}
}
@ -1694,11 +1742,14 @@ win_line(
// text prop can show.
while (text_prop_next < text_prop_count
&& (text_props[text_prop_next].tp_col == MAXCOL
? (*ptr == NUL
? ((*ptr == NUL
&& (wp->w_p_wrap
|| wlv.row == startrow
|| (text_props[text_prop_next].tp_flags
& TP_FLAG_ALIGN_BELOW)))
|| (bcol == 0 &&
(text_props[text_prop_next].tp_flags
& TP_FLAG_ALIGN_ABOVE)))
: bcol >= text_props[text_prop_next].tp_col - 1))
{
if (text_props[text_prop_next].tp_col == MAXCOL
@ -1773,6 +1824,8 @@ win_line(
{
int right = (tp->tp_flags
& TP_FLAG_ALIGN_RIGHT);
int above = (tp->tp_flags
& TP_FLAG_ALIGN_ABOVE);
int below = (tp->tp_flags
& TP_FLAG_ALIGN_BELOW);
int wrap = (tp->tp_flags & TP_FLAG_WRAP);
@ -1797,18 +1850,15 @@ win_line(
// don't combine char attr after EOL
text_prop_flags &= ~PT_FLAG_COMBINE;
#ifdef FEAT_LINEBREAK
if (below || right || !wrap)
if (above || below || right || !wrap)
{
// no 'showbreak' before "below" text property
// or after "right" text property
// or after "above" or "right" text property
need_showbreak = FALSE;
dont_use_showbreak = TRUE;
}
#endif
// Keep in sync with where
// textprop_size_after_trunc() is called in
// win_lbr_chartabsize().
if ((right || below || !wrap || padding > 0)
if ((right || above || below || !wrap || padding > 0)
&& wp->w_width > 2)
{
char_u *prev_p_extra = wlv.p_extra;

View File

@ -85,7 +85,7 @@ getviscol2(colnr_T col, colnr_T coladd UNUSED)
}
/*
* Try to advance the Cursor to the specified screen column.
* Try to advance the Cursor to the specified screen column "wantcol".
* If virtual editing: fine tune the cursor position.
* Note that all virtual positions off the end of a line should share
* a curwin->w_cursor.col value (n.b. this is equal to STRLEN(line)),
@ -94,29 +94,30 @@ getviscol2(colnr_T col, colnr_T coladd UNUSED)
* return OK if desired column is reached, FAIL if not
*/
int
coladvance(colnr_T wcol)
coladvance(colnr_T wantcol)
{
int rc = getvpos(&curwin->w_cursor, wcol);
int rc = getvpos(&curwin->w_cursor, wantcol);
if (wcol == MAXCOL || rc == FAIL)
if (wantcol == MAXCOL || rc == FAIL)
curwin->w_valid &= ~VALID_VIRTCOL;
else if (*ml_get_cursor() != TAB)
{
// Virtcol is valid when not on a TAB
curwin->w_valid |= VALID_VIRTCOL;
curwin->w_virtcol = wcol;
curwin->w_virtcol = wantcol;
}
return rc;
}
/*
* Return in "pos" the position of the cursor advanced to screen column "wcol".
* Return in "pos" the position of the cursor advanced to screen column
* "wantcol".
* return OK if desired column is reached, FAIL if not
*/
int
getvpos(pos_T *pos, colnr_T wcol)
getvpos(pos_T *pos, colnr_T wantcol)
{
return coladvance2(pos, FALSE, virtual_active(), wcol);
return coladvance2(pos, FALSE, virtual_active(), wantcol);
}
static int
@ -156,8 +157,8 @@ coladvance2(
}
else
{
int width = curwin->w_width - win_col_off(curwin);
chartabsize_T cts;
int width = curwin->w_width - win_col_off(curwin);
chartabsize_T cts;
if (finetune
&& curwin->w_p_wrap
@ -183,6 +184,9 @@ coladvance2(
init_chartabsize_arg(&cts, curwin, pos->lnum, 0, line, line);
while (cts.cts_vcol <= wcol && *cts.cts_ptr != NUL)
{
#ifdef FEAT_PROP_POPUP
int at_start = cts.cts_ptr == cts.cts_line;
#endif
// Count a tab for what it's worth (if list mode not on)
#ifdef FEAT_LINEBREAK
csize = win_lbr_chartabsize(&cts, &head);
@ -191,6 +195,11 @@ coladvance2(
csize = lbr_chartabsize_adv(&cts);
#endif
cts.cts_vcol += csize;
#ifdef FEAT_PROP_POPUP
if (at_start)
// do not count the columns for virtual text above
cts.cts_vcol -= cts.cts_first_char;
#endif
}
col = cts.cts_vcol;
idx = (int)(cts.cts_ptr - line);
@ -2400,7 +2409,7 @@ update_mouseshape(int shape_idx)
/*
* Change directory to "new_dir". Search 'cdpath' for relative directory
* names, otherwise just mch_chdir().
* names.
*/
int
vim_chdir(char_u *new_dir)

View File

@ -476,7 +476,11 @@ update_curswant(void)
if (curwin->w_set_curswant)
{
validate_virtcol();
curwin->w_curswant = curwin->w_virtcol;
curwin->w_curswant = curwin->w_virtcol
#ifdef FEAT_PROP_POPUP
- curwin->w_virtcol_first_char
#endif
;
curwin->w_set_curswant = FALSE;
}
}
@ -835,6 +839,9 @@ validate_virtcol_win(win_T *wp)
check_cursor_moved(wp);
if (!(wp->w_valid & VALID_VIRTCOL))
{
#ifdef FEAT_PROP_POPUP
wp->w_virtcol_first_char = 0;
#endif
getvvcol(wp, &wp->w_cursor, NULL, &(wp->w_virtcol), NULL);
#ifdef FEAT_SYN_HL
redraw_for_cursorcolumn(wp);
@ -982,6 +989,11 @@ curs_columns(
if (!(curwin->w_valid & VALID_CROW))
curs_rows(curwin);
#ifdef FEAT_PROP_POPUP
// will be set by getvvcol() but not reset
curwin->w_virtcol_first_char = 0;
#endif
/*
* Compute the number of virtual columns.
*/

View File

@ -34,7 +34,6 @@ void init_chartabsize_arg(chartabsize_T *cts, win_T *wp, linenr_T lnum, colnr_T
void clear_chartabsize_arg(chartabsize_T *cts);
int lbr_chartabsize(chartabsize_T *cts);
int lbr_chartabsize_adv(chartabsize_T *cts);
int textprop_size_after_trunc(win_T *wp, int below, int added, char_u *text, int *n_used_ptr);
int win_lbr_chartabsize(chartabsize_T *cts, int *headp);
void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *end);
colnr_T getvcol_nolist(pos_T *posp);

View File

@ -813,12 +813,13 @@ typedef struct textprop_S
#define TP_FLAG_CONT_PREV 0x2 // property was continued from prev line
// without these text is placed after the end of the line
#define TP_FLAG_ALIGN_RIGHT 0x10 // virtual text is right-aligned
#define TP_FLAG_ALIGN_BELOW 0x20 // virtual text on next screen line
#define TP_FLAG_ALIGN_RIGHT 0x010 // virtual text is right-aligned
#define TP_FLAG_ALIGN_ABOVE 0x020 // virtual text above the line
#define TP_FLAG_ALIGN_BELOW 0x040 // virtual text on next screen line
#define TP_FLAG_WRAP 0x40 // virtual text wraps - when missing
#define TP_FLAG_WRAP 0x080 // virtual text wraps - when missing
// text is truncated
#define TP_FLAG_START_INCL 0x80 // "start_incl" copied from proptype
#define TP_FLAG_START_INCL 0x100 // "start_incl" copied from proptype
#define PROP_TEXT_MIN_CELLS 4 // minimun number of cells to use for
// the text, even when truncating
@ -3678,6 +3679,11 @@ struct window_S
// more than one screen line or when
// w_leftcol is non-zero
#ifdef FEAT_PROP_POPUP
colnr_T w_virtcol_first_char; // offset for w_virtcol when there are
// virtual text properties above the
// line
#endif
/*
* w_wrow and w_wcol specify the cursor position in the window.
* This is related to positions in the window, not in the display or
@ -4607,6 +4613,7 @@ typedef struct {
textprop_T *cts_text_props; // text props (allocated)
char cts_has_prop_with_text; // TRUE if if a property inserts text
int cts_cur_text_width; // width of current inserted text
int cts_first_char; // width text props above the line
int cts_with_trailing; // include size of trailing props with
// last character
int cts_start_incl; // prop has true "start_incl" arg

View File

@ -0,0 +1,9 @@
|f+0&#ffff4012|i|r|s|t| |t|h|i|n|g| |a|b|o|v|e| @42
|s+0&#ffd7ff255|e|c|o|n|d| |t|h|i|n|g| |a|b|o|v|e| @41
|o+0&#ffffff0|n|e| |t|w|o| @52
|t|h|r>e@1| |f|o|u|r| @49
|a+0&#ffff4012|n|o|t|h|e|r| |t|h|i|n|g| @46
|f+0&#ffffff0|i|v|e| |s|i|x| @51
|~+0#4040ff13&| @58
|~| @58
| +0#0000000&@41|2|,|4| @10|A|l@1|

View File

@ -2848,6 +2848,26 @@ func Test_props_with_text_below_nowrap()
call delete('XscriptPropsBelowNowrap')
endfunc
func Test_props_with_text_above()
CheckRunVimInTerminal
let lines =<< trim END
call setline(1, ['one two', 'three four', 'five six'])
call prop_type_add('above1', #{highlight: 'Search'})
call prop_type_add('above2', #{highlight: 'DiffChange'})
call prop_add(1, 0, #{type: 'above1', text: 'first thing above', text_align: 'above'})
call prop_add(1, 0, #{type: 'above2', text: 'second thing above', text_align: 'above'})
call prop_add(3, 0, #{type: 'above1', text: 'another thing', text_align: 'above'})
normal gglllj
END
call writefile(lines, 'XscriptPropsWithTextAbove', 'D')
let buf = RunVimInTerminal('-S XscriptPropsWithTextAbove', #{rows: 9, cols: 60})
call VerifyScreenDump(buf, 'Test_prop_with_text_above_1', {})
call StopVimInTerminal(buf)
endfunc
func Test_props_with_text_override()
CheckRunVimInTerminal

View File

@ -497,6 +497,8 @@ prop_add_common(
}
if (STRCMP(p, "right") == 0)
flags |= TP_FLAG_ALIGN_RIGHT;
else if (STRCMP(p, "above") == 0)
flags |= TP_FLAG_ALIGN_ABOVE;
else if (STRCMP(p, "below") == 0)
flags |= TP_FLAG_ALIGN_BELOW;
else if (STRCMP(p, "after") != 0)
@ -673,6 +675,21 @@ count_props(linenr_T lnum, int only_starting, int last_line)
static textprop_T *text_prop_compare_props;
static buf_T *text_prop_compare_buf;
/* Score for sorting on position of the text property: 0: above,
* 1: after (default), 2: right, 3: below (comes last)
*/
static int
text_prop_order(int flags)
{
if (flags & TP_FLAG_ALIGN_ABOVE)
return 0;
if (flags & TP_FLAG_ALIGN_RIGHT)
return 2;
if (flags & TP_FLAG_ALIGN_BELOW)
return 3;
return 1;
}
/*
* Function passed to qsort() to sort text properties.
* Return 1 if "s1" has priority over "s2", -1 if the other way around, zero if
@ -694,21 +711,13 @@ text_prop_compare(const void *s1, const void *s2)
col2 = tp2->tp_col;
if (col1 == MAXCOL && col2 == MAXCOL)
{
int flags1 = 0;
int flags2 = 0;
int order1 = text_prop_order(tp1->tp_flags);
int order2 = text_prop_order(tp2->tp_flags);
// 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;
// both props add text before or after the line, sort on order where it
// is added
if (order1 != order2)
return order1 < order2 ? 1 : -1;
}
// property that inserts text has priority over one that doesn't

View File

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