feat(ui): add 'statuscolumn' option

Problem:    Unable to customize the column next to a window ('gutter').
Solution:   Add 'statuscolumn' option that follows the 'statusline' syntax,
	    allowing to customize the status column. Also supporting the %@
	    click execute function label. Adds new items @C and @s which
	    will print the fold and sign columns. Line numbers and signs
	    can be clicked, highlighted, aligned, transformed, margined etc.
This commit is contained in:
luukvbaal
2023-01-09 18:12:06 +01:00
committed by GitHub
parent 50f03773f4
commit 364b131f42
26 changed files with 681 additions and 43 deletions

View File

@@ -2001,10 +2001,10 @@ v:lc_time The current locale setting for time messages of the runtime
command. See |multi-lang|. command. See |multi-lang|.
*v:lnum* *lnum-variable* *v:lnum* *lnum-variable*
v:lnum Line number for the 'foldexpr' |fold-expr|, 'formatexpr' and v:lnum Line number for the 'foldexpr' |fold-expr|, 'formatexpr',
'indentexpr' expressions, tab page number for 'guitablabel' 'indentexpr' and 'statuscolumn' expressions, tab page number
and 'guitabtooltip'. Only valid while one of these for 'guitablabel' and 'guitabtooltip'. Only valid while one of
expressions is being evaluated. Read-only when in the these expressions is being evaluated. Read-only when in the
|sandbox|. |sandbox|.
*v:lua* *lua-variable* *v:lua* *lua-variable*
@@ -2138,6 +2138,10 @@ v:register The name of the register in effect for the current normal mode
'*' or '+'. '*' or '+'.
Also see |getreg()| and |setreg()| Also see |getreg()| and |setreg()|
*v:relnum* *relnum-variable*
v:relnum Relative line number for the 'statuscolumn' expression.
Read-only.
*v:scrollstart* *scrollstart-variable* *v:scrollstart* *scrollstart-variable*
v:scrollstart String describing the script or function that caused the v:scrollstart String describing the script or function that caused the
screen to scroll up. It's only set when it is empty, thus the screen to scroll up. It's only set when it is empty, thus the
@@ -2293,6 +2297,11 @@ v:version Vim version number: major version times 100 plus minor
*v:vim_did_enter* *vim_did_enter-variable* *v:vim_did_enter* *vim_did_enter-variable*
v:vim_did_enter 0 during startup, 1 just before |VimEnter|. v:vim_did_enter 0 during startup, 1 just before |VimEnter|.
Read-only.
*v:wrap* *wrap-variable*
v:wrap Boolean indicating whether 'statuscolumn' is being evaluated
for the wrapped part of a line.
Read-only. Read-only.
*v:warningmsg* *warningmsg-variable* *v:warningmsg* *warningmsg-variable*

View File

@@ -118,6 +118,11 @@ The following new APIs or features were added.
• |'splitkeep'| option to control the scroll behavior of horizontal splits. • |'splitkeep'| option to control the scroll behavior of horizontal splits.
• |'statuscolumn'| option to customize the area to the side of a window,
normally containing the fold, sign and number columns. This new option follows
the 'statusline' syntax and can be used to transform the line numbers, create
mouse click callbacks for |signs|, introduce a custom margin or separator etc.
• |nvim_select_popupmenu_item()| now supports |cmdline-completion| popup menu. • |nvim_select_popupmenu_item()| now supports |cmdline-completion| popup menu.
• |'diffopt'| now includes a `linematch` option to enable a second-stage diff • |'diffopt'| now includes a `linematch` option to enable a second-stage diff

View File

@@ -6001,6 +6001,52 @@ A jump table for the options with a short description can be found at |Q_op|.
In case of buffer changing commands the cursor is placed at the column In case of buffer changing commands the cursor is placed at the column
where it was the last time the buffer was edited. where it was the last time the buffer was edited.
*'statuscolumn'* *'stc'*
'statuscolumn' 'stc' string (default: empty)
local to window
EXPERIMENTAL
When non-empty, this option determines the content of the area to the
side of a window, normally containing the fold, sign and number columns.
The format of this option is like that of 'statusline'.
Some of the items from the 'statusline' format are different for
'statuscolumn':
%l line number of currently drawn line
%r relative line number of currently drawn line
%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'.
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.
Examples: >vim
" Relative number with bar separator and click handlers:
:set statuscolumn=%@SignCb@%s%=%T%@NumCb@%r│%T
" Right aligned relative cursor line number:
: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)} '
" Human readable line numbers with thousands separator:
:let &stc='%{substitute(v:lnum,"\\d\\zs\\ze\\'
. '%(\\d\\d\\d\\)\\+$",",","g")}'
" Both relative and absolute line numbers with different
" highlighting for odd and even relative numbers:
:let &stc='%#NonText#%{&nu?v:lnum:""}' .
'%=%{&rnu&&(v:lnum%2)?"\ ".v:relnum:""}' .
'%#LineNr#%{&rnu&&!(v:lnum%2)?"\ ".v:relnum:""}'
< WARNING: this expression is evaluated for each screen line so defining
an expensive expression can negatively affect render performance.
*'statusline'* *'stl'* *E540* *E542* *'statusline'* *'stl'* *E540* *E542*
'statusline' 'stl' string (default empty) 'statusline' 'stl' string (default empty)
global or local to window |global-local| global or local to window |global-local|

View File

@@ -883,6 +883,7 @@ Short explanation of each option: *option-list*
'splitkeep' 'spk' determines scroll behavior for split windows 'splitkeep' 'spk' determines scroll behavior for split windows
'splitright' 'spr' new window is put right of the current one 'splitright' 'spr' new window is put right of the current one
'startofline' 'sol' commands move cursor to first non-blank in line 'startofline' 'sol' commands move cursor to first non-blank in line
'statuscolumn' 'stc' custom format for the status column
'statusline' 'stl' custom format for the status line 'statusline' 'stl' custom format for the status line
'suffixes' 'su' suffixes that are ignored with multiple match 'suffixes' 'su' suffixes that are ignored with multiple match
'suffixesadd' 'sua' suffixes added when searching for a file 'suffixesadd' 'sua' suffixes added when searching for a file

View File

@@ -452,6 +452,9 @@ call <SID>Header(gettext("multiple windows"))
call <SID>AddOption("laststatus", gettext("0, 1, 2 or 3; when to use a status line for the last window")) call <SID>AddOption("laststatus", gettext("0, 1, 2 or 3; when to use a status line for the last window"))
call append("$", " \tset ls=" . &ls) call append("$", " \tset ls=" . &ls)
if has("statusline") if has("statusline")
call <SID>AddOption("statuscolumn", gettext("custom format for the status column"))
call append("$", "\t" .. s:local_to_window)
call <SID>OptionG("stc", &stc)
call <SID>AddOption("statusline", gettext("alternate format to be used for a status line")) call <SID>AddOption("statusline", gettext("alternate format to be used for a status line"))
call <SID>OptionG("stl", &stl) call <SID>OptionG("stl", &stl)
endif endif

View File

@@ -2238,6 +2238,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
fillchar, fillchar,
maxwidth, maxwidth,
hltab_ptr, hltab_ptr,
NULL,
NULL); NULL);
PUT(result, "width", INTEGER_OBJ(width)); PUT(result, "width", INTEGER_OBJ(width));

View File

@@ -3236,7 +3236,7 @@ void maketitle(void)
if (*p_titlestring != NUL) { if (*p_titlestring != NUL) {
if (stl_syntax & STL_IN_TITLE) { if (stl_syntax & STL_IN_TITLE) {
build_stl_str_hl(curwin, buf, sizeof(buf), p_titlestring, build_stl_str_hl(curwin, buf, sizeof(buf), p_titlestring,
"titlestring", 0, 0, maxlen, NULL, NULL); "titlestring", 0, 0, maxlen, NULL, NULL, NULL);
title_str = buf; title_str = buf;
} else { } else {
title_str = p_titlestring; title_str = p_titlestring;
@@ -3342,7 +3342,7 @@ void maketitle(void)
if (*p_iconstring != NUL) { if (*p_iconstring != NUL) {
if (stl_syntax & STL_IN_ICON) { if (stl_syntax & STL_IN_ICON) {
build_stl_str_hl(curwin, icon_str, sizeof(buf), p_iconstring, build_stl_str_hl(curwin, icon_str, sizeof(buf), p_iconstring,
"iconstring", 0, 0, 0, NULL, NULL); "iconstring", 0, 0, 0, NULL, NULL, NULL);
} else { } else {
icon_str = p_iconstring; icon_str = p_iconstring;
} }

View File

@@ -204,6 +204,8 @@ typedef struct {
#define w_p_cc w_onebuf_opt.wo_cc // 'colorcolumn' #define w_p_cc w_onebuf_opt.wo_cc // 'colorcolumn'
char *wo_sbr; char *wo_sbr;
#define w_p_sbr w_onebuf_opt.wo_sbr // 'showbreak' #define w_p_sbr w_onebuf_opt.wo_sbr // 'showbreak'
char *wo_stc;
#define w_p_stc w_onebuf_opt.wo_stc // 'statuscolumn'
char *wo_stl; char *wo_stl;
#define w_p_stl w_onebuf_opt.wo_stl // 'statusline' #define w_p_stl w_onebuf_opt.wo_stl // 'statusline'
char *wo_wbr; char *wo_wbr;
@@ -1300,6 +1302,7 @@ struct window_S {
linenr_T w_redraw_bot; // when != 0: last line needing redraw linenr_T w_redraw_bot; // when != 0: last line needing redraw
bool w_redr_status; // if true statusline/winbar must be redrawn bool w_redr_status; // if true statusline/winbar must be redrawn
bool w_redr_border; // if true border must be redrawn bool w_redr_border; // if true border must be redrawn
bool w_redr_statuscol; // if true 'statuscolumn' must be redrawn
// remember what is shown in the ruler for this window (if 'ruler' set) // remember what is shown in the ruler for this window (if 'ruler' set)
pos_T w_ru_cursor; // cursor position shown in ruler pos_T w_ru_cursor; // cursor position shown in ruler
@@ -1404,6 +1407,31 @@ struct window_S {
StlClickDefinition *w_winbar_click_defs; StlClickDefinition *w_winbar_click_defs;
// Size of the w_winbar_click_defs array // Size of the w_winbar_click_defs array
size_t w_winbar_click_defs_size; size_t w_winbar_click_defs_size;
// Status column click definitions
StlClickDefinition *w_statuscol_click_defs;
// Size of the w_statuscol_click_defs array
size_t w_statuscol_click_defs_size;
};
/// Struct to hold info for 'statuscolumn'
typedef struct statuscol statuscol_T;
struct statuscol {
int width; // width of the status column
int cur_attr; // current attributes in text
int num_attr; // attributes used for line number
int fold_attr; // attributes used for fold column
int sign_attr[SIGN_SHOW_MAX]; // attributes used for signs
int truncate; // truncated width
bool draw; // draw statuscolumn or not
char fold_text[10]; // text in fold column (%C)
char *sign_text[SIGN_SHOW_MAX]; // text in sign column (%s)
char text[MAXPATHL]; // text in status column
char *textp; // current position in text
size_t text_len; // length of text
stl_hlrec_t *hlrec; // highlight groups
stl_hlrec_t *hlrecp; // current highlight group
}; };
/// Macros defined in Vim, but not in Neovim /// Macros defined in Vim, but not in Neovim

View File

@@ -21,6 +21,7 @@
#include "nvim/decoration_provider.h" #include "nvim/decoration_provider.h"
#include "nvim/diff.h" #include "nvim/diff.h"
#include "nvim/drawline.h" #include "nvim/drawline.h"
#include "nvim/eval.h"
#include "nvim/extmark_defs.h" #include "nvim/extmark_defs.h"
#include "nvim/fold.h" #include "nvim/fold.h"
#include "nvim/garray.h" #include "nvim/garray.h"
@@ -43,6 +44,7 @@
#include "nvim/sign.h" #include "nvim/sign.h"
#include "nvim/spell.h" #include "nvim/spell.h"
#include "nvim/state.h" #include "nvim/state.h"
#include "nvim/statusline.h"
#include "nvim/strings.h" #include "nvim/strings.h"
#include "nvim/syntax.h" #include "nvim/syntax.h"
#include "nvim/terminal.h" #include "nvim/terminal.h"
@@ -60,6 +62,7 @@ typedef enum {
WL_FOLD, // 'foldcolumn' WL_FOLD, // 'foldcolumn'
WL_SIGN, // column for signs WL_SIGN, // column for signs
WL_NR, // line number WL_NR, // line number
WL_STC, // 'statuscolumn'
WL_BRI, // 'breakindent' WL_BRI, // 'breakindent'
WL_SBR, // 'showbreak' or 'diff' WL_SBR, // 'showbreak' or 'diff'
WL_LINE, // text in the line WL_LINE, // text in the line
@@ -395,6 +398,90 @@ static int get_sign_attrs(buf_T *buf, linenr_T lnum, SignTextAttrs *sattrs, int
return num_signs; return num_signs;
} }
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, SignTextAttrs *sattrs,
foldinfo_T foldinfo, char_u *extra, statuscol_T *stcp)
{
long relnum;
bool wrapped = row != startrow + filler_lines;
bool use_cul = use_cursor_line_sign(wp, lnum);
// Set num, fold and sign text and attrs, empty when wrapped
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);
if (compute_foldcolumn(wp, 0)) {
size_t n = fill_foldcolumn((char_u *)stcp->fold_text, wp, foldinfo, lnum);
stcp->fold_text[n] = NUL;
stcp->fold_attr = win_hl_attr(wp, use_cul ? HLF_CLF : HLF_FC);
}
}
int i = 0;
for (; i < wp->w_scwidth; i++) {
SignTextAttrs *sattr = wrapped ? NULL : sign_get_attr(i, sattrs, wp->w_scwidth);
stcp->sign_text[i] = sattr && sattr->text ? sattr->text : " ";
stcp->sign_attr[i] = use_cul && cul_attr ? cul_attr : sattr ? sattr->hl_attr_id : 0;
}
stcp->sign_text[i] = NULL;
int width = build_statuscol_str(wp, row == startrow, wrapped, 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'
wp->w_nrwidth = MIN(MAX_NUMBERWIDTH, wp->w_nrwidth + stcp->truncate);
wp->w_nrwidth_width = wp->w_nrwidth;
} else { // 'statuscolumn' reset due to error
wp->w_nrwidth_line_count = 0;
wp->w_nrwidth = (wp->w_p_nu || wp->w_p_rnu) * number_width(wp);
}
wp->w_redr_statuscol = true;
return;
}
// Reset text/highlight pointer and current attr for new line
stcp->textp = stcp->text;
stcp->hlrecp = stcp->hlrec;
stcp->cur_attr = stcp->num_attr;
stcp->text_len = strlen(stcp->text);
int fill = stcp->width - width;
if (fill > 0) {
// Fill up with ' '
memset(&stcp->text[stcp->text_len], ' ', (size_t)fill);
stcp->text_len += (size_t)fill;
stcp->text[stcp->text_len] = NUL;
}
}
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)
{
*c_extrap = NUL;
*c_finalp = NUL;
do {
*draw_state = WL_STC;
*char_attr = stcp->cur_attr;
*pp_extra = stcp->textp;
*n_extrap = stcp->hlrecp->start ? (int)(stcp->hlrecp->start - stcp->textp)
: (int)strlen(*pp_extra);
// Prepare for next highlight section if not yet at the end
if (stcp->textp + *n_extrap < stcp->text + stcp->text_len) {
int hl = stcp->hlrecp->userhl;
stcp->textp = stcp->hlrecp->start;
stcp->cur_attr = hl < 0 ? syn_id2attr(-stcp->hlrecp->userhl)
: hl > 0 ? hl : stcp->num_attr;
stcp->hlrecp++;
*draw_state = WL_STC - 1;
}
// Skip over empty highlight sections
} while (*n_extrap == 0 && stcp->textp < stcp->text + stcp->text_len);
}
/// Return true if CursorLineNr highlight is to be used for the number column. /// Return true if CursorLineNr highlight is to be used for the number column.
/// ///
/// - 'cursorline' must be set /// - 'cursorline' must be set
@@ -1098,6 +1185,13 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
extra_check = true; extra_check = true;
} }
statuscol_T statuscol = { 0 };
if (*wp->w_p_stc != NUL) {
// Draw the 'statuscolumn' if option is set.
statuscol.draw = true;
statuscol.width = win_col_off(wp);
}
int sign_idx = 0; int sign_idx = 0;
// Repeat for the whole displayed line. // Repeat for the whole displayed line.
for (;;) { for (;;) {
@@ -1125,9 +1219,13 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
} }
} }
// Skip fold, sign and number states if 'statuscolumn' is set.
if (draw_state == WL_FOLD - 1 && n_extra == 0 && statuscol.draw) {
draw_state = WL_STC - 1;
}
if (draw_state == WL_FOLD - 1 && n_extra == 0) { if (draw_state == WL_FOLD - 1 && n_extra == 0) {
int fdc = compute_foldcolumn(wp, 0); int fdc = compute_foldcolumn(wp, 0);
draw_state = WL_FOLD; draw_state = WL_FOLD;
if (fdc > 0) { if (fdc > 0) {
// Draw the 'foldcolumn'. Allocate a buffer, "extra" may // Draw the 'foldcolumn'. Allocate a buffer, "extra" may
@@ -1217,7 +1315,23 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
} }
} }
if (draw_state == WL_NR && n_extra == 0) { if (draw_state == WL_STC - 1 && n_extra == 0) {
draw_state = WL_STC;
// Draw the 'statuscolumn' if option is set.
if (statuscol.draw) {
if (statuscol.text_len == 0) {
get_statuscol_str(wp, lnum, row, startrow, filler_lines, cul_attr,
sign_num_attr, sattrs, foldinfo, extra, &statuscol);
if (wp->w_redr_statuscol) {
return 0;
}
}
get_statuscol_display_info(&draw_state, &char_attr, &n_extra, &c_extra,
&c_final, extra, &p_extra, &statuscol);
}
}
if (draw_state == WL_STC && n_extra == 0) {
win_col_offset = off; win_col_offset = off;
} }
@@ -1349,7 +1463,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
&& wp == curwin && wp == curwin
&& lnum == wp->w_cursor.lnum && lnum == wp->w_cursor.lnum
&& vcol >= (long)wp->w_virtcol) && vcol >= (long)wp->w_virtcol)
|| (number_only && draw_state > WL_NR)) || (number_only && draw_state > WL_STC))
&& filler_todo <= 0) { && filler_todo <= 0) {
draw_virt_text(wp, buf, win_col_offset, &col, grid->cols, row); draw_virt_text(wp, buf, win_col_offset, &col, grid->cols, row);
grid_put_linebuf(grid, row, 0, col, -grid->cols, wp->w_p_rl, wp, bg_attr, false); grid_put_linebuf(grid, row, 0, col, -grid->cols, wp->w_p_rl, wp, bg_attr, false);
@@ -2214,7 +2328,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
&& wp->w_p_list && wp->w_p_list
&& (wp->w_p_wrap ? (wp->w_skipcol > 0 && row == 0) : wp->w_leftcol > 0) && (wp->w_p_wrap ? (wp->w_skipcol > 0 && row == 0) : wp->w_leftcol > 0)
&& filler_todo <= 0 && filler_todo <= 0
&& draw_state > WL_NR && draw_state > WL_STC
&& c != NUL) { && c != NUL) {
c = wp->w_p_lcs_chars.prec; c = wp->w_p_lcs_chars.prec;
lcs_prec_todo = NUL; lcs_prec_todo = NUL;
@@ -2508,7 +2622,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
col++; col++;
// UTF-8: Put a 0 in the second screen char. // UTF-8: Put a 0 in the second screen char.
linebuf_char[off][0] = 0; linebuf_char[off][0] = 0;
if (draw_state > WL_NR && filler_todo <= 0) { if (draw_state > WL_STC && filler_todo <= 0) {
vcol++; vcol++;
} }
// When "tocol" is halfway through a character, set it to the end of // When "tocol" is halfway through a character, set it to the end of
@@ -2591,7 +2705,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
// Only advance the "vcol" when after the 'number' or 'relativenumber' // Only advance the "vcol" when after the 'number' or 'relativenumber'
// column. // column.
if (draw_state > WL_NR if (draw_state > WL_STC
&& filler_todo <= 0) { && filler_todo <= 0) {
vcol++; vcol++;
} }
@@ -2601,7 +2715,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
} }
// restore attributes after "predeces" in 'listchars' // restore attributes after "predeces" in 'listchars'
if (draw_state > WL_NR && n_attr3 > 0 && --n_attr3 == 0) { if (draw_state > WL_STC && n_attr3 > 0 && --n_attr3 == 0) {
char_attr = saved_attr3; char_attr = saved_attr3;
} }
@@ -2697,6 +2811,16 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
if (filler_todo <= 0) { if (filler_todo <= 0) {
need_showbreak = true; need_showbreak = true;
} }
if (statuscol.draw) {
if (row == startrow + 1 || row == startrow + filler_lines) {
// Re-evaluate 'statuscolumn' for the first wrapped row and non filler line
statuscol.text_len = 0;
} else { // Otherwise just reset the text/hlrec pointers
statuscol.textp = statuscol.text;
statuscol.hlrecp = statuscol.hlrec;
} // Fall back to default columns if the 'n' flag isn't in 'cpo'
statuscol.draw = vim_strchr(p_cpo, CPO_NUMCOL) == NULL;
}
filler_todo--; filler_todo--;
// When the filler lines are actually below the last line of the // When the filler lines are actually below the last line of the
// file, don't draw the line itself, break here. // file, don't draw the line itself, break here.

View File

@@ -522,7 +522,7 @@ int update_screen(void)
// TODO(bfredl): special casing curwin here is SÅ JÄVLA BULL. // TODO(bfredl): special casing curwin here is SÅ JÄVLA BULL.
// Either this should be done for all windows or not at all. // Either this should be done for all windows or not at all.
if (curwin->w_redr_type < UPD_NOT_VALID if (curwin->w_redr_type < UPD_NOT_VALID
&& curwin->w_nrwidth != ((curwin->w_p_nu || curwin->w_p_rnu) && curwin->w_nrwidth != ((curwin->w_p_nu || curwin->w_p_rnu || *curwin->w_p_stc)
? number_width(curwin) : 0)) { ? number_width(curwin) : 0)) {
curwin->w_redr_type = UPD_NOT_VALID; curwin->w_redr_type = UPD_NOT_VALID;
} }
@@ -1032,7 +1032,7 @@ static void win_update(win_T *wp, DecorProviders *providers)
// Force redraw when width of 'number' or 'relativenumber' column // Force redraw when width of 'number' or 'relativenumber' column
// changes. // changes.
int nrwidth = (wp->w_p_nu || wp->w_p_rnu) ? number_width(wp) : 0; int nrwidth = (wp->w_p_nu || wp->w_p_rnu || *wp->w_p_stc) ? number_width(wp) : 0;
if (wp->w_nrwidth != nrwidth) { if (wp->w_nrwidth != nrwidth) {
type = UPD_NOT_VALID; type = UPD_NOT_VALID;
wp->w_nrwidth = nrwidth; wp->w_nrwidth = nrwidth;
@@ -1823,6 +1823,18 @@ static void win_update(win_T *wp, DecorProviders *providers)
did_update = DID_NONE; did_update = DID_NONE;
} }
// 'statuscolumn' width has changed or errored, start from the top.
if (wp->w_redr_statuscol) {
wp->w_redr_statuscol = false;
idx = 0;
row = 0;
lnum = wp->w_topline;
wp->w_lines_valid = 0;
wp->w_valid &= ~VALID_WCOL;
decor_providers_invoke_win(wp, providers, &line_providers, &provider_err);
continue;
}
if (lnum > buf->b_ml.ml_line_count) { if (lnum > buf->b_ml.ml_line_count) {
eof = true; eof = true;
break; break;

View File

@@ -267,6 +267,8 @@ static struct vimvar {
VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO), VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO),
VV(VV__NULL_BLOB, "_null_blob", VAR_BLOB, VV_RO), VV(VV__NULL_BLOB, "_null_blob", VAR_BLOB, VV_RO),
VV(VV_LUA, "lua", VAR_PARTIAL, 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),
}; };
#undef VV #undef VV

View File

@@ -165,6 +165,8 @@ typedef enum {
VV__NULL_DICT, // Dictionary with NULL value. For test purposes only. VV__NULL_DICT, // Dictionary with NULL value. For test purposes only.
VV__NULL_BLOB, // Blob with NULL value. For test purposes only. VV__NULL_BLOB, // Blob with NULL value. For test purposes only.
VV_LUA, VV_LUA,
VV_RELNUM,
VV_WRAP,
} VimVarIndex; } VimVarIndex;
/// All recognized msgpack types /// All recognized msgpack types

View File

@@ -291,6 +291,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
pos_T start_visual; pos_T start_visual;
bool moved; // Has cursor moved? bool moved; // Has cursor moved?
bool in_winbar; // mouse in window bar bool in_winbar; // mouse in window bar
bool in_statuscol; // mouse in status column
bool in_status_line; // mouse in status line bool in_status_line; // mouse in status line
static bool in_tab_line = false; // mouse clicked in tab line static bool in_tab_line = false; // mouse clicked in tab line
bool in_sep_line; // mouse in vertical separator line bool in_sep_line; // mouse in vertical separator line
@@ -645,10 +646,11 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
moved = (jump_flags & CURSOR_MOVED); moved = (jump_flags & CURSOR_MOVED);
in_winbar = (jump_flags & MOUSE_WINBAR); in_winbar = (jump_flags & MOUSE_WINBAR);
in_statuscol = (jump_flags & MOUSE_STATUSCOL);
in_status_line = (jump_flags & IN_STATUS_LINE); in_status_line = (jump_flags & IN_STATUS_LINE);
in_sep_line = (jump_flags & IN_SEP_LINE); in_sep_line = (jump_flags & IN_SEP_LINE);
if ((in_winbar || in_status_line) && is_click) { if ((in_winbar || in_status_line || in_statuscol) && is_click) {
// Handle click event on window bar or status lin // Handle click event on window bar or status lin
int click_grid = mouse_grid; int click_grid = mouse_grid;
int click_row = mouse_row; int click_row = mouse_row;
@@ -659,7 +661,8 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
} }
StlClickDefinition *click_defs = in_status_line ? wp->w_status_click_defs StlClickDefinition *click_defs = in_status_line ? wp->w_status_click_defs
: wp->w_winbar_click_defs; : in_winbar ? wp->w_winbar_click_defs
: wp->w_statuscol_click_defs;
if (in_status_line && global_stl_height() > 0) { if (in_status_line && global_stl_height() > 0) {
// global statusline is displayed for the current window, // global statusline is displayed for the current window,
@@ -682,8 +685,8 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
} }
return false; return false;
} else if (in_winbar) { } else if (in_winbar || in_statuscol) {
// A drag or release event in the window bar has no side effects. // A drag or release event in the window bar and status column has no side effects.
return false; return false;
} }
@@ -1027,6 +1030,7 @@ int jump_to_mouse(int flags, bool *inclusive, int which_button)
static bool on_status_line = false; static bool on_status_line = false;
static bool on_sep_line = false; static bool on_sep_line = false;
static bool on_winbar = false; static bool on_winbar = false;
static bool on_statuscol = false;
static int prev_row = -1; static int prev_row = -1;
static int prev_col = -1; static int prev_col = -1;
static int did_drag = false; // drag was noticed static int did_drag = false; // drag was noticed
@@ -1069,6 +1073,9 @@ retnomove:
if (on_winbar) { if (on_winbar) {
return IN_OTHER_WIN | MOUSE_WINBAR; return IN_OTHER_WIN | MOUSE_WINBAR;
} }
if (on_statuscol) {
return IN_OTHER_WIN | MOUSE_STATUSCOL;
}
if (flags & MOUSE_MAY_STOP_VIS) { if (flags & MOUSE_MAY_STOP_VIS) {
end_visual_mode(); end_visual_mode();
redraw_curbuf_later(UPD_INVERTED); // delete the inversion redraw_curbuf_later(UPD_INVERTED); // delete the inversion
@@ -1103,6 +1110,10 @@ retnomove:
? wp->w_winbar_height != 0 ? wp->w_winbar_height != 0
: false; : false;
on_statuscol = grid == (col < win_col_off(wp))
? *wp->w_p_stc != NUL
: false;
on_sep_line = grid == DEFAULT_GRID_HANDLE && col >= wp->w_width on_sep_line = grid == DEFAULT_GRID_HANDLE && col >= wp->w_width
? col - wp->w_width + 1 == 1 ? col - wp->w_width + 1 == 1
: false; : false;
@@ -1130,6 +1141,10 @@ retnomove:
return IN_OTHER_WIN | MOUSE_WINBAR; return IN_OTHER_WIN | MOUSE_WINBAR;
} }
if (on_statuscol) {
return IN_OTHER_WIN | MOUSE_STATUSCOL;
}
fdc = win_fdccol_count(wp); fdc = win_fdccol_count(wp);
dragwin = NULL; dragwin = NULL;
@@ -1230,6 +1245,9 @@ retnomove:
} else if (on_winbar && which_button == MOUSE_RIGHT) { } else if (on_winbar && which_button == MOUSE_RIGHT) {
// After a click on the window bar don't start Visual mode. // After a click on the window bar don't start Visual mode.
return IN_OTHER_WIN | MOUSE_WINBAR; return IN_OTHER_WIN | MOUSE_WINBAR;
} else if (on_statuscol && which_button == MOUSE_RIGHT) {
// After a click on the status column don't start Visual mode.
return IN_OTHER_WIN | MOUSE_STATUSCOL;
} else { } else {
// keep_window_focus must be true // keep_window_focus must be true
// before moving the cursor for a left click, stop Visual mode // before moving the cursor for a left click, stop Visual mode

View File

@@ -9,17 +9,18 @@
#include "nvim/window.h" #include "nvim/window.h"
/// jump_to_mouse() returns one of first five these values, possibly with /// jump_to_mouse() returns one of first five these values, possibly with
/// some of the other four added. /// some of the other five added.
enum { enum {
IN_UNKNOWN = 0, IN_UNKNOWN = 0,
IN_BUFFER = 1, IN_BUFFER = 1,
IN_STATUS_LINE = 2, ///< on status or command line IN_STATUS_LINE = 2, ///< on status or command line
IN_SEP_LINE = 4, ///< on vertical separator line IN_SEP_LINE = 4, ///< on vertical separator line
IN_OTHER_WIN = 8, ///< in other window but can't go there IN_OTHER_WIN = 8, ///< in other window but can't go there
CURSOR_MOVED = 0x100, CURSOR_MOVED = 0x100,
MOUSE_FOLD_CLOSE = 0x200, ///< clicked on '-' in fold column MOUSE_FOLD_CLOSE = 0x200, ///< clicked on '-' in fold column
MOUSE_FOLD_OPEN = 0x400, ///< clicked on '+' in fold column MOUSE_FOLD_OPEN = 0x400, ///< clicked on '+' in fold column
MOUSE_WINBAR = 0x800, ///< in window toolbar MOUSE_WINBAR = 0x800, ///< in window toolbar
MOUSE_STATUSCOL = 0x1000, ///< in 'statuscolumn'
}; };
/// flags for jump_to_mouse() /// flags for jump_to_mouse()

View File

@@ -649,7 +649,8 @@ void validate_cursor_col(void)
// fold column and sign column (these don't move when scrolling horizontally). // fold column and sign column (these don't move when scrolling horizontally).
int win_col_off(win_T *wp) int win_col_off(win_T *wp)
{ {
return ((wp->w_p_nu || wp->w_p_rnu) ? number_width(wp) + 1 : 0) return ((wp->w_p_nu || wp->w_p_rnu || (*wp->w_p_stc != NUL)) ?
(number_width(wp) + (*wp->w_p_stc == NUL)) : 0)
+ (cmdwin_type == 0 || wp != curwin ? 0 : 1) + (cmdwin_type == 0 || wp != curwin ? 0 : 1)
+ win_fdccol_count(wp) + win_fdccol_count(wp)
+ (win_signcol_count(wp) * win_signcol_width(wp)); + (win_signcol_count(wp) * win_signcol_width(wp));

View File

@@ -2094,6 +2094,9 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
if (curwin->w_p_spell) { if (curwin->w_p_spell) {
errmsg = did_set_spelllang(curwin); errmsg = did_set_spelllang(curwin);
} }
} else if (((int *)varp == &curwin->w_p_nu || (int *)varp == &curwin->w_p_rnu)
&& *curwin->w_p_stc != NUL) { // '(relative)number' + 'statuscolumn'
curwin->w_nrwidth_line_count = 0;
} }
if ((int *)varp == &curwin->w_p_arab) { if ((int *)varp == &curwin->w_p_arab) {
@@ -2316,7 +2319,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
} else if (pp == &curwin->w_p_nuw || pp == &curwin->w_allbuf_opt.wo_nuw) { } else if (pp == &curwin->w_p_nuw || pp == &curwin->w_allbuf_opt.wo_nuw) {
if (value < 1) { if (value < 1) {
errmsg = e_positive; errmsg = e_positive;
} else if (value > 20) { } else if (value > MAX_NUMBERWIDTH) {
errmsg = e_invarg; errmsg = e_invarg;
} }
} else if (pp == &curbuf->b_p_iminsert || pp == &p_iminsert) { } else if (pp == &curbuf->b_p_iminsert || pp == &p_iminsert) {
@@ -3630,6 +3633,9 @@ void unset_global_local_option(char *name, void *from)
clear_string_option(&((win_T *)from)->w_p_ve); clear_string_option(&((win_T *)from)->w_p_ve);
((win_T *)from)->w_ve_flags = 0; ((win_T *)from)->w_ve_flags = 0;
break; break;
case PV_STC:
clear_string_option(&((win_T *)from)->w_p_stc);
break;
} }
} }
@@ -4014,6 +4020,8 @@ static char_u *get_varp(vimoption_T *p)
return (char_u *)&(curwin->w_p_winhl); return (char_u *)&(curwin->w_p_winhl);
case PV_WINBL: case PV_WINBL:
return (char_u *)&(curwin->w_p_winbl); return (char_u *)&(curwin->w_p_winbl);
case PV_STC:
return (char_u *)&(curwin->w_p_stc);
default: default:
iemsg(_("E356: get_varp ERROR")); iemsg(_("E356: get_varp ERROR"));
} }
@@ -4102,6 +4110,7 @@ void copy_winopt(winopt_T *from, winopt_T *to)
to->wo_scl = copy_option_val(from->wo_scl); to->wo_scl = copy_option_val(from->wo_scl);
to->wo_winhl = copy_option_val(from->wo_winhl); to->wo_winhl = copy_option_val(from->wo_winhl);
to->wo_winbl = from->wo_winbl; to->wo_winbl = from->wo_winbl;
to->wo_stc = copy_option_val(from->wo_stc);
// Copy the script context so that we know were the value was last set. // Copy the script context so that we know were the value was last set.
memmove(to->wo_script_ctx, from->wo_script_ctx, sizeof(to->wo_script_ctx)); memmove(to->wo_script_ctx, from->wo_script_ctx, sizeof(to->wo_script_ctx));
@@ -4139,6 +4148,7 @@ static void check_winopt(winopt_T *wop)
check_string_option(&wop->wo_fcs); check_string_option(&wop->wo_fcs);
check_string_option(&wop->wo_ve); check_string_option(&wop->wo_ve);
check_string_option(&wop->wo_wbr); check_string_option(&wop->wo_wbr);
check_string_option(&wop->wo_stc);
} }
/// Free the allocated memory inside a winopt_T. /// Free the allocated memory inside a winopt_T.
@@ -4165,6 +4175,7 @@ void clear_winopt(winopt_T *wop)
clear_string_option(&wop->wo_fcs); clear_string_option(&wop->wo_fcs);
clear_string_option(&wop->wo_ve); clear_string_option(&wop->wo_ve);
clear_string_option(&wop->wo_wbr); clear_string_option(&wop->wo_wbr);
clear_string_option(&wop->wo_stc);
} }
void didset_window_options(win_T *wp, bool valid_cursor) void didset_window_options(win_T *wp, bool valid_cursor)

View File

@@ -19,6 +19,8 @@ typedef enum {
#define BCO_ALWAYS 2 // always copy the options #define BCO_ALWAYS 2 // always copy the options
#define BCO_NOHELP 4 // don't touch the help related options #define BCO_NOHELP 4 // don't touch the help related options
#define MAX_NUMBERWIDTH 20 // used for 'numberwidth' and 'statuscolumn'
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "option.h.generated.h" # include "option.h.generated.h"
#endif #endif

View File

@@ -337,6 +337,8 @@ enum {
STL_ARGLISTSTAT = 'a', ///< Argument list status as (x of y). STL_ARGLISTSTAT = 'a', ///< Argument list status as (x of y).
STL_PAGENUM = 'N', ///< Page number (when printing). STL_PAGENUM = 'N', ///< Page number (when printing).
STL_SHOWCMD = 'S', ///< 'showcmd' buffer STL_SHOWCMD = 'S', ///< 'showcmd' buffer
STL_FOLDCOL = 'C', ///< Fold column for 'statuscolumn'
STL_SIGNCOL = 's', ///< Sign column for 'statuscolumn'
STL_VIM_EXPR = '{', ///< Start of expression to substitute. STL_VIM_EXPR = '{', ///< Start of expression to substitute.
STL_SEPARATE = '=', ///< Separation between alignment sections. STL_SEPARATE = '=', ///< Separation between alignment sections.
STL_TRUNCMARK = '<', ///< Truncation mark if line is too long. STL_TRUNCMARK = '<', ///< Truncation mark if line is too long.
@@ -354,8 +356,9 @@ enum {
STL_HELPFLAG, STL_HELPFLAG_ALT, STL_FILETYPE, STL_FILETYPE_ALT, \ STL_HELPFLAG, STL_HELPFLAG_ALT, STL_FILETYPE, STL_FILETYPE_ALT, \
STL_PREVIEWFLAG, STL_PREVIEWFLAG_ALT, STL_MODIFIED, STL_MODIFIED_ALT, \ STL_PREVIEWFLAG, STL_PREVIEWFLAG_ALT, STL_MODIFIED, STL_MODIFIED_ALT, \
STL_QUICKFIX, STL_PERCENTAGE, STL_ALTPERCENT, STL_ARGLISTSTAT, STL_PAGENUM, \ STL_QUICKFIX, STL_PERCENTAGE, STL_ALTPERCENT, STL_ARGLISTSTAT, STL_PAGENUM, \
STL_SHOWCMD, STL_VIM_EXPR, STL_SEPARATE, STL_TRUNCMARK, STL_USER_HL, \ STL_SHOWCMD, STL_FOLDCOL, STL_SIGNCOL, STL_VIM_EXPR, STL_SEPARATE, \
STL_HIGHLIGHT, STL_TABPAGENR, STL_TABCLOSENR, STL_CLICK_FUNC, \ STL_TRUNCMARK, STL_USER_HL, STL_HIGHLIGHT, STL_TABPAGENR, STL_TABCLOSENR, \
STL_CLICK_FUNC, STL_TABPAGENR, STL_TABCLOSENR, STL_CLICK_FUNC, \
0, \ 0, \
}) })
@@ -955,6 +958,7 @@ enum {
WV_CULOPT, WV_CULOPT,
WV_CC, WV_CC,
WV_SBR, WV_SBR,
WV_STC,
WV_STL, WV_STL,
WV_WFH, WV_WFH,
WV_WFW, WV_WFW,

View File

@@ -2297,6 +2297,15 @@ return {
varname='p_sol', varname='p_sol',
defaults={if_true=false} defaults={if_true=false}
}, },
{
full_name='statuscolumn', abbreviation='stc',
short_desc=N_("custom format for the status column"),
type='string', scope={'window'},
redraw={'current_window'},
secure=true,
alloced=true,
defaults={if_true=""}
},
{ {
full_name='statusline', abbreviation='stl', full_name='statusline', abbreviation='stl',
short_desc=N_("custom format for the status line"), short_desc=N_("custom format for the status line"),

View File

@@ -1177,12 +1177,14 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
redraw_titles(); redraw_titles();
} }
} else if (gvarp == &p_stl || gvarp == &p_wbr || varp == &p_tal } else if (gvarp == &p_stl || gvarp == &p_wbr || varp == &p_tal
|| varp == &p_ruf) { || varp == &p_ruf || varp == &curwin->w_p_stc) {
// 'statusline', 'winbar', 'tabline' or 'rulerformat' // 'statusline', 'winbar', 'tabline', 'rulerformat' or 'statuscolumn'
int wid; int wid;
if (varp == &p_ruf) { // reset ru_wid first if (varp == &p_ruf) { // reset ru_wid first
ru_wid = 0; ru_wid = 0;
} else if (varp == &curwin->w_p_stc) {
curwin->w_nrwidth_line_count = 0;
} }
s = *varp; s = *varp;
if (varp == &p_ruf && *s == '%') { if (varp == &p_ruf && *s == '%') {
@@ -1197,7 +1199,8 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
errmsg = check_stl_option(p_ruf); errmsg = check_stl_option(p_ruf);
} }
} else if (varp == &p_ruf || s[0] != '%' || s[1] != '!') { } else if (varp == &p_ruf || s[0] != '%' || s[1] != '!') {
// check 'statusline', 'winbar' or 'tabline' only if it doesn't start with "%!" // check 'statusline', 'winbar', 'tabline' or 'statuscolumn'
// only if it doesn't start with "%!"
errmsg = check_stl_option(s); errmsg = check_stl_option(s);
} }
if (varp == &p_ruf && errmsg == NULL) { if (varp == &p_ruf && errmsg == NULL) {

View File

@@ -780,6 +780,16 @@ int number_width(win_T *wp)
} }
wp->w_nrwidth_line_count = lnum; wp->w_nrwidth_line_count = lnum;
// make best estimate for 'statuscolumn'
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 = 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;
}
n = 0; n = 0;
do { do {
lnum /= 10; lnum /= 10;

View File

@@ -367,8 +367,9 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
// Make a copy, because the statusline may include a function call that // Make a copy, because the statusline may include a function call that
// might change the option value and free the memory. // might change the option value and free the memory.
stl = xstrdup(stl); stl = xstrdup(stl);
width = build_stl_str_hl(ewp, buf, sizeof(buf), stl, opt_name, width = build_stl_str_hl(ewp, buf, sizeof(buf), stl, opt_name, opt_scope,
opt_scope, fillchar, maxwidth, &hltab, &tabtab); fillchar, maxwidth, &hltab, &tabtab, NULL);
xfree(stl); xfree(stl);
ewp->w_p_crb = p_crb_save; ewp->w_p_crb = p_crb_save;
@@ -867,6 +868,32 @@ void draw_tabline(void)
redraw_tabline = false; redraw_tabline = false;
} }
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)
{
if (setnum) {
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);
int width = build_stl_str_hl(wp, buf, MAXPATHL, stc, "statuscolumn", OPT_LOCAL,
fillchar, maxwidth, hlrec, &clickrec, stcp);
xfree(stc);
// Allocate and fill click def array if width has changed
if (wp->w_status_click_defs_size != (size_t)width) {
stl_clear_click_defs(wp->w_statuscol_click_defs, wp->w_statuscol_click_defs_size);
wp->w_statuscol_click_defs = stl_alloc_click_defs(wp->w_statuscol_click_defs, width,
&wp->w_statuscol_click_defs_size);
stl_fill_click_defs(wp->w_statuscol_click_defs, clickrec, buf, width, false);
}
return width;
}
/// Build a string from the status line items in "fmt". /// Build a string from the status line items in "fmt".
/// Return length of string in screen cells. /// Return length of string in screen cells.
/// ///
@@ -890,11 +917,13 @@ void draw_tabline(void)
/// @param fillchar Character to use when filling empty space in the statusline /// @param fillchar Character to use when filling empty space in the statusline
/// @param maxwidth The maximum width to make the statusline /// @param maxwidth The maximum width to make the statusline
/// @param hltab HL attributes (can be NULL) /// @param hltab HL attributes (can be NULL)
/// @param tabtab Tab clicks definition (can be NULL). /// @param tabtab Tab clicks definition (can be NULL)
/// @param stcp Status column attributes (can be NULL)
/// ///
/// @return The final width of the statusline /// @return The final width of the statusline
int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_name, int opt_scope, int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_name, int opt_scope,
int fillchar, int maxwidth, stl_hlrec_t **hltab, StlClickRecord **tabtab) int fillchar, int maxwidth, stl_hlrec_t **hltab, StlClickRecord **tabtab,
statuscol_T *stcp)
{ {
static size_t stl_items_len = 20; // Initial value, grows as needed. static size_t stl_items_len = 20; // Initial value, grows as needed.
static stl_item_t *stl_items = NULL; static stl_item_t *stl_items = NULL;
@@ -1466,8 +1495,9 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n
} }
case STL_LINE: case STL_LINE:
num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) // Overload %l with v:lnum for 'statuscolumn'
? 0L : (long)(wp->w_cursor.lnum); num = strcmp(opt_name, "statuscolumn") == 0 ? get_vim_var_nr(VV_LNUM)
: (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) ? 0L : (long)(wp->w_cursor.lnum);
break; break;
case STL_NUMLINES: case STL_NUMLINES:
@@ -1565,9 +1595,14 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n
case STL_ROFLAG: case STL_ROFLAG:
case STL_ROFLAG_ALT: case STL_ROFLAG_ALT:
itemisflag = true; // Overload %r with v:relnum for 'statuscolumn'
if (wp->w_buffer->b_p_ro) { if (strcmp(opt_name, "statuscolumn") == 0) {
str = (opt == STL_ROFLAG_ALT) ? ",RO" : _("[RO]"); num = get_vim_var_nr(VV_RELNUM);
} else {
itemisflag = true;
if (wp->w_buffer->b_p_ro) {
str = (opt == STL_ROFLAG_ALT) ? ",RO" : _("[RO]");
}
} }
break; break;
@@ -1579,6 +1614,33 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n
} }
break; break;
case STL_FOLDCOL: // 'C' for 'statuscolumn'
case STL_SIGNCOL: { // 's' for 'statuscolumn'
if (stcp == NULL) {
break;
}
bool fold = opt == STL_FOLDCOL;
*buf_tmp = NUL;
for (int i = 0; i <= 9; i++) {
char *p = fold ? stcp->fold_text : stcp->sign_text[i];
if ((!p || !*p) && *buf_tmp == NUL) {
break;
}
stl_items[curitem].type = Highlight;
stl_items[curitem].start = out_p + strlen(buf_tmp);
stl_items[curitem].minwid = !p || (fold && i) ? 0 : fold ? stcp->fold_attr
: stcp->sign_attr[i];
curitem++;
if (!p || (fold && i)) {
str = buf_tmp;
break;
}
STRCAT(buf_tmp, p);
}
break;
}
case STL_FILETYPE: case STL_FILETYPE:
// Copy the filetype if it is not null and the formatted string will fit // Copy the filetype if it is not null and the formatted string will fit
// in the temporary buffer // in the temporary buffer
@@ -1850,6 +1912,10 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n
// What follows is post-processing to handle alignment and highlighting. // What follows is post-processing to handle alignment and highlighting.
int width = vim_strsize(out); int width = vim_strsize(out);
// Return truncated width for 'statuscolumn'
if (stcp != NULL && width > maxwidth) {
stcp->truncate = width - maxwidth;
}
if (maxwidth > 0 && width > maxwidth) { if (maxwidth > 0 && width > maxwidth) {
// Result is too long, must truncate somewhere. // Result is too long, must truncate somewhere.
int item_idx = 0; int item_idx = 0;

View File

@@ -5094,6 +5094,9 @@ static void win_free(win_T *wp, tabpage_T *tp)
stl_clear_click_defs(wp->w_winbar_click_defs, wp->w_winbar_click_defs_size); stl_clear_click_defs(wp->w_winbar_click_defs, wp->w_winbar_click_defs_size);
xfree(wp->w_winbar_click_defs); xfree(wp->w_winbar_click_defs);
stl_clear_click_defs(wp->w_statuscol_click_defs, wp->w_statuscol_click_defs_size);
xfree(wp->w_statuscol_click_defs);
// Remove the window from the b_wininfo lists, it may happen that the // Remove the window from the b_wininfo lists, it may happen that the
// freed memory is re-used for another window. // freed memory is re-used for another window.
FOR_ALL_BUFFERS(buf) { FOR_ALL_BUFFERS(buf) {

View File

@@ -29,6 +29,7 @@ describe('ffi.cdef', function()
typedef struct window_S win_T; typedef struct window_S win_T;
typedef struct {} stl_hlrec_t; typedef struct {} stl_hlrec_t;
typedef struct {} StlClickRecord; typedef struct {} StlClickRecord;
typedef struct {} statuscol_T;
typedef struct {} Error; typedef struct {} Error;
win_T *find_window_by_handle(int Window, Error *err); win_T *find_window_by_handle(int Window, Error *err);
@@ -43,7 +44,8 @@ describe('ffi.cdef', function()
int fillchar, int fillchar,
int maxwidth, int maxwidth,
stl_hlrec_t **hltab, stl_hlrec_t **hltab,
StlClickRecord **tabtab StlClickRecord **tabtab,
statuscol_T *scp
); );
]] ]]
@@ -57,6 +59,7 @@ describe('ffi.cdef', function()
0, 0,
0, 0,
nil, nil,
nil,
nil nil
) )
]=]) ]=])

View File

@@ -0,0 +1,273 @@
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local clear = helpers.clear
local command = helpers.command
local eq = helpers.eq
local eval = helpers.eval
local meths = helpers.meths
local pcall_err = helpers.pcall_err
describe('statuscolumn', function()
local screen
before_each(function()
clear('--cmd', 'set number nuw=1 | call setline(1, repeat(["aaaaa"], 16)) | norm GM')
screen = Screen.new()
screen:attach()
end)
it('fails with invalid \'statuscolumn\'', function()
command('set stc=%{v:relnum?v:relnum:(v:lnum==5?invalid:v:lnum)}\\ ')
screen:expect([[
4 aaaaa |
3 aaaaa |
2 aaaaa |
1 aaaaa |
8 ^aaaaa |
1 aaaaa |
2 aaaaa |
3 aaaaa |
4 aaaaa |
5 aaaaa |
6 aaaaa |
7 aaaaa |
8 aaaaa |
|
]])
command('norm 5G')
eq('Vim(redraw):E121: Undefined variable: invalid', pcall_err(command, 'redraw!'))
eq('', eval('&statuscolumn'))
end)
it('widens with irregular \'statuscolumn\' width', function()
command('set stc=%{v:relnum?v:relnum:(v:lnum==5?\'bbbbb\':v:lnum)}')
command('norm 5G | redraw!')
screen:expect([[
1 aaaaa |
bbbbba^eaaa |
1 aaaaa |
2 aaaaa |
3 aaaaa |
4 aaaaa |
5 aaaaa |
6 aaaaa |
7 aaaaa |
8 aaaaa |
9 aaaaa |
10 aaaaa |
11 aaaaa |
|
]])
end)
it('works with \'statuscolumn\'', function()
command([[set stc=%{&nu?v:lnum:''}%=%{&rnu?'\ '.v:relnum:''}│]])
screen:expect([[
4 │aaaaa |
5 │aaaaa |
6 │aaaaa |
7 │aaaaa |
8 │^aaaaa |
9 │aaaaa |
10│aaaaa |
11│aaaaa |
12│aaaaa |
13│aaaaa |
14│aaaaa |
15│aaaaa |
16│aaaaa |
|
]])
command('set relativenumber')
screen:expect([[
4 4│aaaaa |
5 3│aaaaa |
6 2│aaaaa |
7 1│aaaaa |
8 0│^aaaaa |
9 1│aaaaa |
10 2│aaaaa |
11 3│aaaaa |
12 4│aaaaa |
13 5│aaaaa |
14 6│aaaaa |
15 7│aaaaa |
16 8│aaaaa |
|
]])
command('norm 12GH')
screen:expect([[
4 0│^aaaaa |
5 1│aaaaa |
6 2│aaaaa |
7 3│aaaaa |
8 4│aaaaa |
9 5│aaaaa |
10 6│aaaaa |
11 7│aaaaa |
12 8│aaaaa |
13 9│aaaaa |
14 10│aaaaa |
15 11│aaaaa |
16 12│aaaaa |
|
]])
end)
it('works with highlighted \'statuscolumn\'', function()
command([[set stc=%#NonText#%{&nu?v:lnum:''}]] ..
[[%=%{&rnu&&(v:lnum%2)?'\ '.v:relnum:''}]] ..
[[%#LineNr#%{&rnu&&!(v:lnum%2)?'\ '.v:relnum:''}│]])
screen:set_default_attr_ids({
[0] = {bold = true, foreground = Screen.colors.Blue},
[1] = {foreground = Screen.colors.Brown},
})
screen:expect([[
{0:4 }{1:│}aaaaa |
{0:5 }{1:│}aaaaa |
{0:6 }{1:│}aaaaa |
{0:7 }{1:│}aaaaa |
{0:8 }{1:│}^aaaaa |
{0:9 }{1:│}aaaaa |
{0:10}{1:│}aaaaa |
{0:11}{1:│}aaaaa |
{0:12}{1:│}aaaaa |
{0:13}{1:│}aaaaa |
{0:14}{1:│}aaaaa |
{0:15}{1:│}aaaaa |
{0:16}{1:│}aaaaa |
|
]])
command('set relativenumber')
screen:expect([[
{0:4 }{1: 4│}aaaaa |
{0:5 3}{1:│}aaaaa |
{0:6 }{1: 2│}aaaaa |
{0:7 1}{1:│}aaaaa |
{0:8 }{1: 0│}^aaaaa |
{0:9 1}{1:│}aaaaa |
{0:10}{1: 2│}aaaaa |
{0:11 3}{1:│}aaaaa |
{0:12}{1: 4│}aaaaa |
{0:13 5}{1:│}aaaaa |
{0:14}{1: 6│}aaaaa |
{0:15 7}{1:│}aaaaa |
{0:16}{1: 8│}aaaaa |
|
]])
command('set nonumber')
screen:expect([[
{1:4│}aaaaa |
{0:3}{1:│}aaaaa |
{1:2│}aaaaa |
{0:1}{1:│}aaaaa |
{1:0│}^aaaaa |
{0:1}{1:│}aaaaa |
{1:2│}aaaaa |
{0:3}{1:│}aaaaa |
{1:4│}aaaaa |
{0:5}{1:│}aaaaa |
{1:6│}aaaaa |
{0:7}{1:│}aaaaa |
{1:8│}aaaaa |
|
]])
end)
it('works with wrapped lines, signs and folds', function()
command("set stc=%C%s%=%{v:wrap?'':v:lnum}│\\ ")
command("call setline(1,repeat([repeat('aaaaa',10)],16))")
screen:set_default_attr_ids({
[0] = {bold = true, foreground = Screen.colors.Blue},
[1] = {foreground = Screen.colors.Brown},
[2] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.WebGrey},
[3] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGrey},
})
screen:expect([[
{1: 4│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{1: │ }a |
{1: 5│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{1: │ }a |
{1: 6│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{1: │ }a |
{1: 7│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{1: │ }a |
{1: 8│ }^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{1: │ }a |
{1: 9│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{1: │ }a |
{1:10│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa{0:@@@}|
|
]])
command('set signcolumn=auto:2 foldcolumn=auto')
command('sign define piet1 text=>> texthl=LineNr')
command('sign define piet2 text=>! texthl=NonText')
command('sign place 1 line=4 name=piet1 buffer=1')
command('sign place 2 line=5 name=piet2 buffer=1')
command('sign place 3 line=6 name=piet1 buffer=1')
command('sign place 4 line=6 name=piet2 buffer=1')
screen:expect([[
{1:>> 4│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{1: │ }aaaaa |
{0:>!}{1: 5│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{1: │ }aaaaa |
{1:>>}{0:>!}{1: 6│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{1: │ }aaaaa |
{1: 7│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{1: │ }aaaaa |
{1: 8│ }^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{1: │ }aaaaa |
{1: 9│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{1: │ }aaaaa |
{1: 10│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa{0:@@@}|
|
]])
command('norm zf$')
-- Check that alignment works properly with signs after %=
command("set stc=%C%=%{v:wrap?'':v:lnum}│%s\\ ")
screen:expect([[
{2: }{1: 4│>> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{2: }{1: │ }aaaaaa |
{2: }{1: 5│}{0:>!}{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{2: }{1: │ }aaaaaa |
{2: }{1: 6│>>}{0:>!}{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{2: }{1: │ }aaaaaa |
{2: }{1: 7│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{2: }{1: │ }aaaaaa |
{2:+}{1: 8│ }{3:^+-- 1 line: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}|
{2: }{1: 9│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{2: }{1: │ }aaaaaa |
{2: }{1:10│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{2: }{1: │ }aaaaaa |
|
]])
end)
it('works with \'statuscolumn\' clicks', function()
command('set mousemodel=extend')
command([[
function! MyClickFunc(minwid, clicks, button, mods)
let g:testvar = printf("%d %d %s %d", a:minwid, a:clicks, a:button, getmousepos().line)
if a:mods !=# ' '
let g:testvar ..= '(' .. a:mods .. ')'
endif
endfunction
set stc=%0@MyClickFunc@%=%l%T
]])
meths.input_mouse('left', 'press', '', 0, 0, 0)
eq('0 1 l 4', eval("g:testvar"))
meths.input_mouse('left', 'press', '', 0, 0, 0)
eq('0 2 l 4', eval("g:testvar"))
meths.input_mouse('left', 'press', '', 0, 0, 0)
eq('0 3 l 4', eval("g:testvar"))
meths.input_mouse('left', 'press', '', 0, 0, 0)
eq('0 4 l 4', eval("g:testvar"))
meths.input_mouse('right', 'press', '', 0, 3, 0)
eq('0 1 r 7', eval("g:testvar"))
meths.input_mouse('right', 'press', '', 0, 3, 0)
eq('0 2 r 7', eval("g:testvar"))
meths.input_mouse('right', 'press', '', 0, 3, 0)
eq('0 3 r 7', eval("g:testvar"))
meths.input_mouse('right', 'press', '', 0, 3, 0)
eq('0 4 r 7', eval("g:testvar"))
end)
end)

View File

@@ -238,6 +238,7 @@ describe('buffer functions', function()
fillchar, fillchar,
maximum_cell_count, maximum_cell_count,
NULL, NULL,
NULL,
NULL) NULL)
end end