fix(column)!: ensure 'statuscolumn' works with virtual and wrapped lines

Problem:    The `'statuscolumn'` was not re-evaluated for wrapped lines,
            when preceded by virtual/filler lines. There was also no way
            to distinguish virtual and wrapped lines in the status column.
Solution:   Make sure to rebuild the statuscolumn, and replace variable
            `v:wrap` with `v:virtnum`. `v:virtnum` is negative when drawing
            virtual lines, zero when drawing the actual buffer line, and
            positive when drawing the wrapped part of a buffer line.
This commit is contained in:
Luuk van Baal 2023-01-12 10:40:53 +01:00
parent ef89f9fd46
commit 85111ca0f4
8 changed files with 80 additions and 44 deletions

View File

@ -2295,13 +2295,15 @@ v:version Vim version number: major version times 100 plus minor
:if has("nvim-0.2.1")
<
*v:vim_did_enter* *vim_did_enter-variable*
v:vim_did_enter 0 during startup, 1 just before |VimEnter|.
*v:virtnum* *virtnum-variable*
v:virtnum Virtual line number for the 'statuscolumn' expression.
Negative when drawing the status column for virtual lines, zero
when drawing an actual buffer line, and positive when drawing
the wrapped part of a buffer line.
Read-only.
*v:wrap* *wrap-variable*
v:wrap Boolean indicating whether 'statuscolumn' is being evaluated
for the wrapped part of a line.
*v:vim_did_enter* *vim_did_enter-variable*
v:vim_did_enter 0 during startup, 1 just before |VimEnter|.
Read-only.
*v:warningmsg* *warningmsg-variable*

View File

@ -6017,12 +6017,15 @@ A jump table for the options with a short description can be found at |Q_op|.
%s sign column for currently drawn line
%C fold column for currently drawn line
To draw the sign and fold columns, they must be included in
'statuscolumn'.
NOTE: To draw the sign and fold columns, their items must be included in
'statuscolumn'. Even when they are not included, the status column width
will adapt to the 'signcolumn' and 'foldcolumn' width.
The |v:lnum| variable holds the line number to be drawn.
The |v:relnum| variable holds the relative line number to be drawn.
The |v:wrap| variable holds true for the wrapped part of a line.
The |v:lnum| variable holds the line number to be drawn.
The |v:relnum| variable holds the relative line number to be drawn.
The |v:virtnum| variable is negative when drawing virtual lines, zero
when drawing the actual buffer line, and positive when
drawing the wrapped part of a buffer line.
Examples: >vim
" Relative number with bar separator and click handlers:
@ -6032,7 +6035,7 @@ A jump table for the options with a short description can be found at |Q_op|.
:let &stc='%=%{v:relnum?v:relnum:v:lnum} '
" Line numbers in hexadecimal for non wrapped part of lines:
:let &stc='%=%{v:wrap?"":printf("%x",v:lnum)} '
:let &stc='%=%{v:virtnum>0?"":printf("%x",v:lnum)} '
" Human readable line numbers with thousands separator:
:let &stc='%{substitute(v:lnum,"\\d\\zs\\ze\\'

View File

@ -399,40 +399,45 @@ static int get_sign_attrs(buf_T *buf, linenr_T lnum, SignTextAttrs *sattrs, int
/// Prepare and build the 'statuscolumn' string for line "lnum" in window "wp".
/// Fill "stcp" with the built status column string and attributes.
/// This can be called three times per win_line(), once for virt_lines, once for
/// the start of the buffer line "lnum" and once for the wrapped lines.
///
/// @param[out] stcp Status column attributes
static void get_statuscol_str(win_T *wp, linenr_T lnum, int row, int startrow, int filler_lines,
int cul_attr, int sign_num_attr, int sign_cul_attr, char_u *extra,
foldinfo_T foldinfo, SignTextAttrs *sattrs, statuscol_T *stcp)
int cul_attr, int sign_num_attr, int sign_cul_attr, statuscol_T *stcp,
foldinfo_T foldinfo, SignTextAttrs *sattrs)
{
long relnum = 0;
bool wrapped = row != startrow + filler_lines;
long relnum = -1;
bool use_cul = use_cursor_line_sign(wp, lnum);
int virtnum = row - startrow - filler_lines;
// Set num, fold and sign text and attrs, empty when wrapped
set_vim_var_nr(VV_VIRTNUM, virtnum);
// When called the first time for line "lnum" set num_attr
if (row == startrow) {
relnum = labs(get_cursor_rel_lnum(wp, lnum));
stcp->num_attr = sign_num_attr ? sign_num_attr
: get_line_number_attr(wp, lnum, row, startrow, filler_lines);
}
// When called for the first non-filler row of line "lnum" set num v:vars and fold column
if (virtnum == 0) {
relnum = labs(get_cursor_rel_lnum(wp, lnum));
if (compute_foldcolumn(wp, 0)) {
size_t n = fill_foldcolumn(stcp->fold_text, wp, foldinfo, lnum);
stcp->fold_text[n] = NUL;
stcp->fold_attr = win_hl_attr(wp, use_cul ? HLF_CLF : HLF_FC);
}
}
// Make sure to clear->set->clear sign column for filler->first->wrapped lines
int i = 0;
for (; i < wp->w_scwidth; i++) {
SignTextAttrs *sattr = wrapped ? NULL : sign_get_attr(i, sattrs, wp->w_scwidth);
SignTextAttrs *sattr = virtnum ? NULL : sign_get_attr(i, sattrs, wp->w_scwidth);
stcp->sign_text[i] = sattr && sattr->text ? sattr->text : " ";
stcp->sign_attr[i] = sattr ? (use_cul && sign_cul_attr ? sign_cul_attr : sattr->hl_attr_id)
: win_hl_attr(wp, use_cul ? HLF_CLS : HLF_SC);
}
stcp->sign_text[i] = NULL;
int width = build_statuscol_str(wp, row == startrow, wrapped, lnum, relnum,
stcp->width, ' ', stcp->text, &stcp->hlrec, stcp);
int width = build_statuscol_str(wp, lnum, relnum, stcp->width,
' ', stcp->text, &stcp->hlrec, stcp);
// Force a redraw in case of error or when truncated
if (*wp->w_p_stc == NUL || (stcp->truncate > 0 && wp->w_nrwidth < MAX_NUMBERWIDTH)) {
if (stcp->truncate) { // Avoid truncating 'statuscolumn'
@ -465,9 +470,8 @@ static void get_statuscol_str(win_T *wp, linenr_T lnum, int row, int startrow, i
///
/// @param stcp Status column attributes
/// @param[out] draw_state Current draw state in win_line()
static void get_statuscol_display_info(LineDrawState *draw_state, int *char_attr, int *n_extrap,
int *c_extrap, int *c_finalp, char_u *extra, char **pp_extra,
statuscol_T *stcp)
static void get_statuscol_display_info(statuscol_T *stcp, LineDrawState *draw_state, int *char_attr,
int *n_extrap, int *c_extrap, int *c_finalp, char **pp_extra)
{
*c_extrap = NUL;
*c_finalp = NUL;
@ -1326,14 +1330,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
// Draw the 'statuscolumn' if option is set.
if (statuscol.draw) {
if (statuscol.textp == NULL) {
get_statuscol_str(wp, lnum, row, startrow, filler_lines, cul_attr, sign_num_attr,
sign_cul_attr, extra, foldinfo, sattrs, &statuscol);
get_statuscol_str(wp, lnum, row, startrow, filler_lines, cul_attr,
sign_num_attr, sign_cul_attr, &statuscol, foldinfo, sattrs);
if (wp->w_redr_statuscol) {
return 0;
break;
}
}
get_statuscol_display_info(&draw_state, &char_attr, &n_extra, &c_extra,
&c_final, extra, &p_extra, &statuscol);
get_statuscol_display_info(&statuscol, &draw_state, &char_attr,
&n_extra, &c_extra, &c_final, &p_extra);
}
}
@ -2818,7 +2822,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
need_showbreak = true;
}
if (statuscol.draw) {
if (row == startrow + 1 || row == startrow + filler_lines) {
if (row == startrow + filler_lines + 1 || row == startrow + filler_lines) {
// Re-evaluate 'statuscolumn' for the first wrapped row and non filler line
statuscol.textp = NULL;
} else { // Otherwise just reset the text/hlrec pointers

View File

@ -268,7 +268,7 @@ static struct vimvar {
VV(VV__NULL_BLOB, "_null_blob", VAR_BLOB, VV_RO),
VV(VV_LUA, "lua", VAR_PARTIAL, VV_RO),
VV(VV_RELNUM, "relnum", VAR_NUMBER, VV_RO),
VV(VV_WRAP, "wrap", VAR_BOOL, VV_RO),
VV(VV_VIRTNUM, "virtnum", VAR_NUMBER, VV_RO),
};
#undef VV

View File

@ -166,7 +166,7 @@ typedef enum {
VV__NULL_BLOB, // Blob with NULL value. For test purposes only.
VV_LUA,
VV_RELNUM,
VV_WRAP,
VV_VIRTNUM,
} VimVarIndex;
/// All recognized msgpack types

View File

@ -784,7 +784,7 @@ int number_width(win_T *wp)
if (*wp->w_p_stc != NUL) {
char buf[MAXPATHL];
wp->w_nrwidth_width = 0;
n = build_statuscol_str(wp, true, false, lnum, 0, 0, NUL, buf, NULL, NULL);
n = build_statuscol_str(wp, lnum, 0, 0, NUL, buf, NULL, NULL);
n = MAX(n, (wp->w_p_nu || wp->w_p_rnu) * (int)wp->w_p_nuw);
wp->w_nrwidth_width = MIN(n, MAX_NUMBERWIDTH);
return wp->w_nrwidth_width;

View File

@ -876,14 +876,13 @@ void draw_tabline(void)
/// @param hlrec HL attributes (can be NULL)
/// @param stcp Status column attributes (can be NULL)
/// @return The width of the built status column string for line "lnum"
int build_statuscol_str(win_T *wp, bool setnum, bool wrap, linenr_T lnum, long relnum, int maxwidth,
int fillchar, char *buf, stl_hlrec_t **hlrec, statuscol_T *stcp)
int build_statuscol_str(win_T *wp, linenr_T lnum, long relnum, int maxwidth, int fillchar,
char *buf, stl_hlrec_t **hlrec, statuscol_T *stcp)
{
if (setnum) {
if (relnum >= 0) {
set_vim_var_nr(VV_LNUM, lnum);
set_vim_var_nr(VV_RELNUM, relnum);
}
set_vim_var_bool(VV_WRAP, wrap);
StlClickRecord *clickrec;
char *stc = xstrdup(wp->w_p_stc);
@ -1506,13 +1505,12 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n
case STL_LINE:
// Overload %l with v:lnum for 'statuscolumn'
if (opt_name != NULL && strcmp(opt_name, "statuscolumn") == 0) {
if (wp->w_p_nu) {
if (wp->w_p_nu && !get_vim_var_nr(VV_VIRTNUM)) {
num = get_vim_var_nr(VV_LNUM);
}
} else {
num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) ? 0L : (long)(wp->w_cursor.lnum);
}
break;
case STL_NUMLINES:
@ -1612,7 +1610,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n
case STL_ROFLAG_ALT:
// Overload %r with v:relnum for 'statuscolumn'
if (opt_name != NULL && strcmp(opt_name, "statuscolumn") == 0) {
if (wp->w_p_rnu) {
if (wp->w_p_rnu && !get_vim_var_nr(VV_VIRTNUM)) {
num = get_vim_var_nr(VV_RELNUM);
}
} else {

View File

@ -4,6 +4,7 @@ local clear = helpers.clear
local command = helpers.command
local eq = helpers.eq
local eval = helpers.eval
local exec = helpers.exec_lua
local meths = helpers.meths
local pcall_err = helpers.pcall_err
@ -183,7 +184,7 @@ describe('statuscolumn', function()
end)
it('works with wrapped lines, signs and folds', function()
command("set stc=%C%s%=%{v:wrap?'':v:lnum}│\\ ")
command("set stc=%C%s%=%{v:virtnum?'':v:lnum}│\\ ")
command("call setline(1,repeat([repeat('aaaaa',10)],16))")
screen:set_default_attr_ids({
[0] = {bold = true, foreground = Screen.colors.Blue},
@ -234,7 +235,7 @@ describe('statuscolumn', function()
]])
command('norm zf$')
-- Check that alignment works properly with signs after %=
command("set stc=%C%=%{v:wrap?'':v:lnum}│%s\\ ")
command("set stc=%C%=%{v:virtnum?'':v:lnum}│%s\\ ")
screen:expect([[
{2: }{1: 4>>}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{2: }{1: }{2: }{1: }aaaaaa |
@ -304,7 +305,7 @@ describe('statuscolumn', function()
{2: }{1: 2}{2: }{1: }aaaaaa |
|
]])
command("set stc=%C%=\\ %{v:wrap?'':v:relnum}│%s\\ ")
command([[set stc=%C%=\ %{v:virtnum?'':v:relnum}│%s\ ]])
screen:expect([[
{2: }{1: 4>>}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{2: }{1: }{2: }{1: }aaaaaa |
@ -346,6 +347,34 @@ describe('statuscolumn', function()
{2: }{1: }{2: }{1: }aaaaaaaaaaaaaaaaaaaa |
|
]])
-- Status column is re-evaluated for virt_lines, buffer line, and wrapped line
exec([[
local ns = vim.api.nvim_create_namespace("ns")
vim.api.nvim_buf_set_extmark(0, ns, 4, 0, {
virt_lines = {{{"virt_line", ""}}, {{"virt_line", ""}}}
})
vim.api.nvim_buf_set_extmark(0, ns, 5, 0, {
virt_lines_above = true, virt_lines = {{{"virt_line above", ""}}, {{"virt_line above", ""}}}
})
]])
command('set foldcolumn=0 signcolumn=no')
command([[set stc=%{v:virtnum<0?'virtual':(!v:virtnum?'buffer':'wrapped')}%=%{'\ '.v:virtnum.'\ '.v:lnum}]])
screen:expect([[
{1:buffer 0 4}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{1:wrapped 1 4}aaaaaaaa |
{1:buffer 0 5}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{1:wrapped 1 5}aaaaaaaa |
{1:virtual-4 5}virt_line |
{1:virtual-4 5}virt_line |
{1:virtual-4 5}virt_line above |
{1:virtual-4 5}virt_line above |
{1:buffer 0 6}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{1:wrapped 1 6}aaaaaaaa |
{1:buffer 0 7}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{1:wrapped 1 7}aaaaaaaa |
{4:buffer 0 8}{5:^+-- 1 line: aaaaaaaaaaaaaaaaaaaaaaaaaaaaa}|
|
]])
end)
it('works with \'statuscolumn\' clicks', function()