Merge pull request #17266 from famiu/feat/ui/global-statusline

feat(statusline): add global statusline
This commit is contained in:
bfredl 2022-03-17 20:16:39 +01:00 committed by GitHub
commit 3c7e937a89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 828 additions and 203 deletions

View File

@ -175,7 +175,7 @@ o Enable Arabic settings [short-cut]
vertical separator like "l" or "𝖨" may be used. It may also be
hidden by changing its color to the foreground color: >
:set fillchars=vert:l
:hi VertSplit ctermbg=White
:hi WinSeparator ctermbg=White
< Note that this is a workaround, not a proper solution.
If, on the other hand, you'd like to be verbose and explicit and

View File

@ -2441,7 +2441,14 @@ A jump table for the options with a short description can be found at |Q_op|.
item default Used for ~
stl:c ' ' or '^' statusline of the current window
stlnc:c ' ' or '=' statusline of the non-current windows
horiz:c '─' or '-' horizontal separators |:split|
horizup:c '┴' or '-' upwards facing horizontal separator
horizdown:c '┬' or '-' downwards facing horizontal separator
vert:c '│' or '|' vertical separators |:vsplit|
vertleft:c '┤' or '|' left facing vertical separator
vertright:c '├' or '|' right facing vertical separator
verthoriz:c '┼' or '+' overlapping vertical and horizontal
separator
fold:c '·' or '-' filling 'foldtext'
foldopen:c '-' mark the beginning of a fold
foldclose:c '+' show a closed fold
@ -2454,8 +2461,13 @@ A jump table for the options with a short description can be found at |Q_op|.
"stlnc" the space will be used when there is highlighting, '^' or '='
otherwise.
If 'ambiwidth' is "double" then "vert", "foldsep" and "fold" default to
single-byte alternatives.
Note that "horiz", "horizup", "horizdown", "vertleft", "vertright" and
"verthoriz" are only used when 'laststatus' is 3, since only vertical
window separators are used otherwise.
If 'ambiwidth' is "double" then "horiz", "horizup", "horizdown",
"vert", "vertleft", "vertright", "verthoriz", "foldsep" and "fold"
default to single-byte alternatives.
Example: >
:set fillchars=stl:^,stlnc:=,vert:│,fold:·,diff:-
@ -2469,7 +2481,13 @@ A jump table for the options with a short description can be found at |Q_op|.
item highlight group ~
stl:c StatusLine |hl-StatusLine|
stlnc:c StatusLineNC |hl-StatusLineNC|
vert:c VertSplit |hl-VertSplit|
horiz:c WinSeparator |hl-WinSeparator|
horizup:c WinSeparator |hl-WinSeparator|
horizdown:c WinSeparator |hl-WinSeparator|
vert:c WinSeparator |hl-WinSeparator|
vertleft:c WinSeparator |hl-WinSeparator|
vertright:c WinSeparator |hl-WinSeparator|
verthoriz:c WinSeparator |hl-WinSeparator|
fold:c Folded |hl-Folded|
diff:c DiffDelete |hl-DiffDelete|
eob:c EndOfBuffer |hl-EndOfBuffer|
@ -3652,6 +3670,8 @@ A jump table for the options with a short description can be found at |Q_op|.
0: never
1: only if there are at least two windows
2: always
3: have a global statusline at the bottom instead of one for
each window
The screen looks nicer with a status line if you have several
windows, but it takes another screen line. |status-line|
@ -5929,7 +5949,7 @@ A jump table for the options with a short description can be found at |Q_op|.
empty to avoid further errors. Otherwise screen updating would loop.
Note that the only effect of 'ruler' when this option is set (and
'laststatus' is 2) is controlling the output of |CTRL-G|.
'laststatus' is 2 or 3) is controlling the output of |CTRL-G|.
field meaning ~
- Left justify the item. The default is right justified

View File

@ -5119,8 +5119,8 @@ TermCursor cursor in a focused terminal
TermCursorNC cursor in an unfocused terminal
*hl-ErrorMsg*
ErrorMsg error messages on the command line
*hl-VertSplit*
VertSplit the column separating vertically split windows
*hl-WinSeparator*
WinSeparator separators between window splits
*hl-Folded*
Folded line used for closed folds
*hl-FoldColumn*

View File

@ -482,6 +482,8 @@ statusline:
0 never
1 only when there are split windows (the default)
2 always
3 have a global statusline at the bottom instead of one for each
window
Many commands that edit another file have a variant that splits the window.
For Command-line commands this is done by prepending an "s". For example:

View File

@ -211,6 +211,7 @@ Highlight groups:
|hl-Substitute|
|hl-TermCursor|
|hl-TermCursorNC|
|hl-WinSeparator| highlights window separators
|hl-Whitespace| highlights 'listchars' whitespace
Input/Mappings:
@ -227,9 +228,11 @@ Options:
'cpoptions' flags: |cpo-_|
'display' flags: "msgsep" minimizes scrolling when showing messages
'guicursor' works in the terminal
'fillchars' flags: "msgsep" (see 'display')
'fillchars' flags: "msgsep" (see 'display'), "horiz", "horizup",
"horizdown", "vertleft", "vertright", "verthoriz"
'foldcolumn' supports up to 9 dynamic/fixed columns
'inccommand' shows interactive results for |:substitute|-like commands
'laststatus' global statusline support
'pumblend' pseudo-transparent popupmenu
'scrollback'
'signcolumn' supports up to 9 dynamic/fixed columns
@ -348,6 +351,7 @@ Highlight groups:
|hl-ColorColumn|, |hl-CursorColumn| are lower priority than most other
groups
|hl-CursorLine| is low-priority unless foreground color is set
*hl-VertSplit* superseded by |hl-WinSeparator|
Macro/|recording| behavior
Replay of a macro recorded during :lmap produces the same actions as when it

View File

@ -104,6 +104,8 @@ when the last window also has a status line:
'laststatus' = 0 never a status line
'laststatus' = 1 status line if there is more than one window
'laststatus' = 2 always a status line
'laststatus' = 3 have a global statusline at the bottom instead
of one for each window
You can change the contents of the status line with the 'statusline' option.
This option can be local to the window, so that you can have a different

View File

@ -2329,7 +2329,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
maxwidth = (int)opts->maxwidth.data.integer;
} else {
maxwidth = use_tabline ? Columns : wp->w_width;
maxwidth = (use_tabline || global_stl_height() > 0) ? Columns : wp->w_width;
}
char buf[MAXPATHL];

View File

@ -131,7 +131,7 @@
/// An empty string can be used to turn off a specific border, for instance,
/// [ "", "", "", ">", "", "", "", "<" ]
/// will only make vertical borders but not horizontal ones.
/// By default, `FloatBorder` highlight is used, which links to `VertSplit`
/// By default, `FloatBorder` highlight is used, which links to `WinSeparator`
/// when not defined. It could also be specified by character:
/// [ {"+", "MyCorner"}, {"x", "MyBorder"} ].
/// - noautocmd: If true then no buffer-related autocommand events such as

View File

@ -4947,8 +4947,8 @@ void ex_buffer_all(exarg_T *eap)
wpnext = wp->w_next;
if ((wp->w_buffer->b_nwindows > 1
|| ((cmdmod.split & WSP_VERT)
? wp->w_height + wp->w_status_height < Rows - p_ch
- tabline_height()
? wp->w_height + wp->w_hsep_height + wp->w_status_height < Rows - p_ch
- tabline_height() - global_stl_height()
: wp->w_width != Columns)
|| (had_tab > 0 && wp != firstwin))
&& !ONE_WINDOW

View File

@ -1232,7 +1232,13 @@ struct window_S {
struct {
int stl;
int stlnc;
int horiz;
int horizup;
int horizdown;
int vert;
int vertleft;
int vertright;
int verthoriz;
int fold;
int foldopen; ///< when fold is open
int foldclosed; ///< when fold is closed
@ -1278,7 +1284,8 @@ struct window_S {
int w_status_height; // number of status lines (0 or 1)
int w_wincol; // Leftmost column of window in screen.
int w_width; // Width of window, excluding separation.
int w_vsep_width; // Number of separator columns (0 or 1).
int w_hsep_height; // Number of horizontal separator rows (0 or 1)
int w_vsep_width; // Number of vertical separator columns (0 or 1).
pos_save_T w_save_cursor; // backup of cursor pos and topline
// inner size of window, which can be overridden by external UI

View File

@ -3745,7 +3745,7 @@ static void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
wp = mouse_find_win(&grid, &row, &col);
if (wp != NULL) {
int height = wp->w_height + wp->w_status_height;
int height = wp->w_height + wp->w_hsep_height + wp->w_status_height;
// The height is adjusted by 1 when there is a bottom border. This is not
// necessary for a top border since `row` starts at -1 in that case.
if (row < height + wp->w_border_adj[2]) {

View File

@ -632,7 +632,7 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat
validate_cursor();
// May redraw the status line to show the cursor position.
if (p_ru && curwin->w_status_height > 0) {
if (p_ru && (curwin->w_status_height > 0 || global_stl_height() > 0)) {
curwin->w_redr_status = true;
}
@ -3631,7 +3631,7 @@ void compute_cmdrow(void)
} else {
win_T *wp = lastwin_nofloating();
cmdline_row = wp->w_winrow + wp->w_height
+ wp->w_status_height;
+ wp->w_hsep_height + wp->w_status_height + global_stl_height();
}
lines_left = cmdline_row;
}

View File

@ -72,7 +72,7 @@ static int ses_winsizes(FILE *fd, int restore_size, win_T *tab_firstwin)
n++;
// restore height when not full height
if (wp->w_height + wp->w_status_height < topframe->fr_height
if (wp->w_height + wp->w_hsep_height + wp->w_status_height < topframe->fr_height
&& (fprintf(fd,
"exe '%dresize ' . ((&lines * %" PRId64
" + %" PRId64 ") / %" PRId64 ")\n",

View File

@ -74,7 +74,8 @@ typedef enum {
HLF_R, // return to continue message and yes/no questions
HLF_S, // status lines
HLF_SNC, // status lines of not-current windows
HLF_C, // column to separate vertically split windows
HLF_C, // window split separators
HLF_VSP, // VertSplit
HLF_T, // Titles for output from ":set all", ":autocmd" etc.
HLF_V, // Visual mode
HLF_VNC, // Visual mode, autoselecting and not clipboard owner
@ -133,10 +134,11 @@ EXTERN const char *hlf_names[] INIT(= {
[HLF_R] = "Question",
[HLF_S] = "StatusLine",
[HLF_SNC] = "StatusLineNC",
[HLF_C] = "VertSplit",
[HLF_C] = "WinSeparator",
[HLF_T] = "Title",
[HLF_V] = "Visual",
[HLF_VNC] = "VisualNC",
[HLF_VSP] = "VertSplit",
[HLF_W] = "WarningMsg",
[HLF_WM] = "WildMenu",
[HLF_FL] = "Folded",

View File

@ -2910,7 +2910,7 @@ ambw_end:
|| check_opt_strings(curbuf->b_p_bt, p_buftype_values, false) != OK) {
errmsg = e_invarg;
} else {
if (curwin->w_status_height) {
if (curwin->w_status_height || global_stl_height()) {
curwin->w_redr_status = true;
redraw_later(curwin, VALID);
}
@ -3555,7 +3555,13 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set)
struct chars_tab fcs_tab[] = {
{ &wp->w_p_fcs_chars.stl, "stl", ' ' },
{ &wp->w_p_fcs_chars.stlnc, "stlnc", ' ' },
{ &wp->w_p_fcs_chars.horiz, "horiz", 9472 }, // ─
{ &wp->w_p_fcs_chars.horizup, "horizup", 9524 }, // ┴
{ &wp->w_p_fcs_chars.horizdown, "horizdown", 9516 }, // ┬
{ &wp->w_p_fcs_chars.vert, "vert", 9474 }, // │
{ &wp->w_p_fcs_chars.vertleft, "vertleft", 9508 }, // ┤
{ &wp->w_p_fcs_chars.vertright, "vertright", 9500 }, // ├
{ &wp->w_p_fcs_chars.verthoriz, "verthoriz", 9532 }, // ┼
{ &wp->w_p_fcs_chars.fold, "fold", 183 }, // ·
{ &wp->w_p_fcs_chars.foldopen, "foldopen", '-' },
{ &wp->w_p_fcs_chars.foldclosed, "foldclose", '+' },
@ -3589,15 +3595,17 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set)
varp = &p_fcs;
}
if (*p_ambw == 'd') {
// XXX: If ambiwidth=double then "|" and "·" take 2 columns, which is
// forbidden (TUI limitation?). Set old defaults.
fcs_tab[2].def = '|';
fcs_tab[6].def = '|';
// XXX: If ambiwidth=double then some characters take 2 columns,
// which is forbidden (TUI limitation?). Set old defaults.
fcs_tab[2].def = '-';
fcs_tab[3].def = '-';
} else {
fcs_tab[2].def = 9474; // │
fcs_tab[6].def = 9474; // │
fcs_tab[3].def = 183; // ·
fcs_tab[4].def = '-';
fcs_tab[5].def = '|';
fcs_tab[6].def = '|';
fcs_tab[7].def = '|';
fcs_tab[8].def = '+';
fcs_tab[9].def = '-';
fcs_tab[12].def = '|';
}
}
@ -4475,6 +4483,20 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
// 'winminwidth'
win_setminwidth();
} else if (pp == &p_ls) {
// When switching to global statusline, decrease topframe height
// Also clear the cmdline to remove the ruler if there is one
if (value == 3 && old_value != 3) {
frame_new_height(topframe, topframe->fr_height - STATUS_HEIGHT, false, false);
(void)win_comp_pos();
clear_cmdline = true;
}
// When switching from global statusline, increase height of topframe by STATUS_HEIGHT
// in order to to re-add the space that was previously taken by the global statusline
if (old_value == 3 && value != 3) {
frame_new_height(topframe, topframe->fr_height + STATUS_HEIGHT, false, false);
(void)win_comp_pos();
}
last_status(false); // (re)set last window status line.
} else if (pp == &p_stal) {
// (re)set tab page line
@ -5646,7 +5668,7 @@ static int put_setbool(FILE *fd, char *cmd, char *name, int value)
void comp_col(void)
{
int last_has_status = (p_ls == 2 || (p_ls == 1 && !ONE_WINDOW));
int last_has_status = (p_ls > 1 || (p_ls == 1 && !ONE_WINDOW));
sc_col = 0;
ru_col = 0;

View File

@ -3635,7 +3635,7 @@ static int qf_goto_cwindow(const qf_info_T *qi, bool resize, int sz, bool vertsp
win_setwidth(sz);
}
} else if (sz != win->w_height
&& (win->w_height + win->w_status_height + tabline_height()
&& (win->w_height + win->w_hsep_height + win->w_status_height + tabline_height()
< cmdline_row)) {
win_setheight(sz);
}

View File

@ -286,7 +286,8 @@ void update_curbuf(int type)
void redraw_buf_status_later(buf_T *buf)
{
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_buffer == buf && wp->w_status_height) {
if (wp->w_buffer == buf
&& (wp->w_status_height || (wp == curwin && global_stl_height()))) {
wp->w_redr_status = true;
if (must_redraw < VALID) {
must_redraw = VALID;
@ -316,6 +317,7 @@ void redraw_win_signcol(win_T *wp)
int update_screen(int type)
{
static bool did_intro = false;
bool is_stl_global = global_stl_height() > 0;
// Don't do anything if the screen structures are (not yet) valid.
// A VimResized autocmd can invoke redrawing in the middle of a resize,
@ -398,10 +400,13 @@ int update_screen(int type)
if (W_ENDROW(wp) > valid) {
wp->w_redr_type = MAX(wp->w_redr_type, NOT_VALID);
}
if (W_ENDROW(wp) + wp->w_status_height > valid) {
if (!is_stl_global && W_ENDROW(wp) + wp->w_status_height > valid) {
wp->w_redr_status = true;
}
}
if (is_stl_global && Rows - p_ch - 1 > valid) {
curwin->w_redr_status = true;
}
}
msg_grid_set_pos(Rows-p_ch, false);
msg_grid_invalid = false;
@ -423,13 +428,15 @@ int update_screen(int type)
wp->w_redr_type = REDRAW_TOP;
} else {
wp->w_redr_type = NOT_VALID;
if (W_ENDROW(wp) + wp->w_status_height
<= msg_scrolled) {
wp->w_redr_status = TRUE;
if (!is_stl_global && W_ENDROW(wp) + wp->w_status_height <= msg_scrolled) {
wp->w_redr_status = true;
}
}
}
}
if (is_stl_global && Rows - p_ch - 1 <= msg_scrolled) {
curwin->w_redr_status = true;
}
redraw_cmdline = true;
redraw_tabline = true;
}
@ -740,8 +747,11 @@ static void win_update(win_T *wp, DecorProviders *providers)
wp->w_lines_valid = 0;
}
// Window is zero-height: nothing to draw.
// Window is zero-height: Only need to draw the separator
if (wp->w_grid.Rows == 0) {
// draw the horizontal separator below this window
draw_hsep_win(wp);
draw_sep_connectors_win(wp);
wp->w_redr_type = 0;
return;
}
@ -749,7 +759,8 @@ static void win_update(win_T *wp, DecorProviders *providers)
// Window is zero-width: Only need to draw the separator.
if (wp->w_grid.Columns == 0) {
// draw the vertical separator right of this window
draw_vsep_win(wp, 0);
draw_vsep_win(wp);
draw_sep_connectors_win(wp);
wp->w_redr_type = 0;
return;
}
@ -1664,7 +1675,9 @@ static void win_update(win_T *wp, DecorProviders *providers)
kvi_destroy(line_providers);
if (wp->w_redr_type >= REDRAW_TOP) {
draw_vsep_win(wp, 0);
draw_vsep_win(wp);
draw_hsep_win(wp);
draw_sep_connectors_win(wp);
}
syn_set_timeout(NULL);
@ -4881,6 +4894,10 @@ void rl_mirror(char_u *str)
*/
void status_redraw_all(void)
{
if (global_stl_height()) {
curwin->w_redr_status = true;
redraw_later(curwin, VALID);
} else {
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_status_height) {
wp->w_redr_status = true;
@ -4888,6 +4905,7 @@ void status_redraw_all(void)
}
}
}
}
/// Marks all status lines of the current buffer for redraw.
void status_redraw_curbuf(void)
@ -4898,6 +4916,10 @@ void status_redraw_curbuf(void)
/// Marks all status lines of the specified buffer for redraw.
void status_redraw_buf(buf_T *buf)
{
if (global_stl_height() != 0 && curwin->w_buffer == buf) {
curwin->w_redr_status = true;
redraw_later(curwin, VALID);
} else {
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_status_height != 0 && wp->w_buffer == buf) {
wp->w_redr_status = true;
@ -4905,6 +4927,7 @@ void status_redraw_buf(buf_T *buf)
}
}
}
}
/*
* Redraw all status lines that need to be redrawn.
@ -4943,10 +4966,8 @@ void win_redraw_last_status(const frame_T *frp)
}
}
/*
* Draw the verticap separator right of window "wp" starting with line "row".
*/
static void draw_vsep_win(win_T *wp, int row)
/// Draw the vertical separator right of window "wp"
static void draw_vsep_win(win_T *wp)
{
int hl;
int c;
@ -4954,15 +4975,97 @@ static void draw_vsep_win(win_T *wp, int row)
if (wp->w_vsep_width) {
// draw the vertical separator right of this window
c = fillchar_vsep(wp, &hl);
grid_fill(&default_grid, wp->w_winrow + row, W_ENDROW(wp),
grid_fill(&default_grid, wp->w_winrow, W_ENDROW(wp),
W_ENDCOL(wp), W_ENDCOL(wp) + 1, c, ' ', hl);
}
}
/// Draw the horizontal separator below window "wp"
static void draw_hsep_win(win_T *wp)
{
int hl;
int c;
/*
* Get the length of an item as it will be shown in the status line.
*/
if (wp->w_hsep_height) {
// draw the horizontal separator below this window
c = fillchar_hsep(wp, &hl);
grid_fill(&default_grid, W_ENDROW(wp), W_ENDROW(wp) + 1,
wp->w_wincol, W_ENDCOL(wp), c, c, hl);
}
}
/// Get the separator connector for specified window corner of window "wp"
static int get_corner_sep_connector(win_T *wp, WindowCorner corner)
{
// It's impossible for windows to be connected neither vertically nor horizontally
// So if they're not vertically connected, assume they're horizontally connected
if (vsep_connected(wp, corner)) {
if (hsep_connected(wp, corner)) {
return wp->w_p_fcs_chars.verthoriz;
} else if (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT) {
return wp->w_p_fcs_chars.vertright;
} else {
return wp->w_p_fcs_chars.vertleft;
}
} else if (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT) {
return wp->w_p_fcs_chars.horizdown;
} else {
return wp->w_p_fcs_chars.horizup;
}
}
/// Draw seperator connecting characters on the corners of window "wp"
static void draw_sep_connectors_win(win_T *wp)
{
// Don't draw separator connectors unless global statusline is enabled and the window has
// either a horizontal or vertical separator
if (global_stl_height() == 0 || !(wp->w_hsep_height == 1 || wp->w_vsep_width == 1)) {
return;
}
int hl = win_hl_attr(wp, HLF_C);
// Determine which edges of the screen the window is located on so we can avoid drawing separators
// on corners contained in those edges
bool win_at_top;
bool win_at_bottom = wp->w_hsep_height == 0;
bool win_at_left;
bool win_at_right = wp->w_vsep_width == 0;
frame_T *frp;
for (frp = wp->w_frame; frp->fr_parent != NULL; frp = frp->fr_parent) {
if (frp->fr_parent->fr_layout == FR_COL && frp->fr_prev != NULL) {
break;
}
}
win_at_top = frp->fr_parent == NULL;
for (frp = wp->w_frame; frp->fr_parent != NULL; frp = frp->fr_parent) {
if (frp->fr_parent->fr_layout == FR_ROW && frp->fr_prev != NULL) {
break;
}
}
win_at_left = frp->fr_parent == NULL;
// Draw the appropriate separator connector in every corner where drawing them is necessary
if (!(win_at_top || win_at_left)) {
grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_TOP_LEFT),
wp->w_winrow - 1, wp->w_wincol - 1, hl);
}
if (!(win_at_top || win_at_right)) {
grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_TOP_RIGHT),
wp->w_winrow - 1, W_ENDCOL(wp), hl);
}
if (!(win_at_bottom || win_at_left)) {
grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_BOTTOM_LEFT),
W_ENDROW(wp), wp->w_wincol - 1, hl);
}
if (!(win_at_bottom || win_at_right)) {
grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_BOTTOM_RIGHT),
W_ENDROW(wp), W_ENDCOL(wp), hl);
}
}
/// Get the length of an item as it will be shown in the status line.
static int status_match_len(expand_T *xp, char_u *s)
{
int len = 0;
@ -5163,7 +5266,7 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in
// Create status line if needed by setting 'laststatus' to 2.
// Set 'winminheight' to zero to avoid that the window is
// resized.
if (lastwin->w_status_height == 0) {
if (lastwin->w_status_height == 0 && global_stl_height() == 0) {
save_p_ls = p_ls;
save_p_wmh = p_wmh;
p_ls = 2;
@ -5199,12 +5302,15 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in
static void win_redr_status(win_T *wp)
{
int row;
int col;
char_u *p;
int len;
int fillchar;
int attr;
int width;
int this_ru_col;
static int busy = FALSE;
bool is_stl_global = global_stl_height() > 0;
static int busy = false;
// May get here recursively when 'statusline' (indirectly)
// invokes ":redrawstatus". Simply ignore the call then.
@ -5215,9 +5321,9 @@ static void win_redr_status(win_T *wp)
}
busy = true;
wp->w_redr_status = FALSE;
if (wp->w_status_height == 0) {
// no status line, can only be last window
wp->w_redr_status = false;
if (wp->w_status_height == 0 && !(is_stl_global && wp == curwin)) {
// no status line, either global statusline is enabled or the window is a last window
redraw_cmdline = true;
} else if (!redrawing()) {
// Don't redraw right now, do it later. Don't update status line when
@ -5228,6 +5334,7 @@ static void win_redr_status(win_T *wp)
redraw_custom_statusline(wp);
} else {
fillchar = fillchar_status(&attr, wp);
width = is_stl_global ? Columns : wp->w_width;
get_trans_bufname(wp->w_buffer);
p = NameBuff;
@ -5256,9 +5363,9 @@ static void win_redr_status(win_T *wp)
// len += (int)STRLEN(p + len); // dead assignment
}
this_ru_col = ru_col - (Columns - wp->w_width);
if (this_ru_col < (wp->w_width + 1) / 2) {
this_ru_col = (wp->w_width + 1) / 2;
this_ru_col = ru_col - (Columns - width);
if (this_ru_col < (width + 1) / 2) {
this_ru_col = (width + 1) / 2;
}
if (this_ru_col <= 1) {
p = (char_u *)"<"; // No room for file name!
@ -5283,10 +5390,11 @@ static void win_redr_status(win_T *wp)
}
}
row = W_ENDROW(wp);
grid_puts(&default_grid, p, row, wp->w_wincol, attr);
grid_fill(&default_grid, row, row + 1, len + wp->w_wincol,
this_ru_col + wp->w_wincol, fillchar, fillchar, attr);
row = is_stl_global ? (Rows - p_ch - 1) : W_ENDROW(wp);
col = is_stl_global ? 0 : wp->w_wincol;
grid_puts(&default_grid, p, row, col, attr);
grid_fill(&default_grid, row, row + 1, len + col,
this_ru_col + col, fillchar, fillchar, attr);
if (get_keymap_str(wp, (char_u *)"<%s>", NameBuff, MAXPATHL)
&& this_ru_col - len > (int)(STRLEN(NameBuff) + 1)) {
@ -5365,6 +5473,76 @@ bool stl_connected(win_T *wp)
return false;
}
/// Check if horizontal separator of window "wp" at specified window corner is connected to the
/// horizontal separator of another window
/// Assumes global statusline is enabled
static bool hsep_connected(win_T *wp, WindowCorner corner)
{
bool before = (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT);
int sep_row = (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT)
? wp->w_winrow - 1 : W_ENDROW(wp);
frame_T *fr = wp->w_frame;
while (fr->fr_parent != NULL) {
if (fr->fr_parent->fr_layout == FR_ROW && (before ? fr->fr_prev : fr->fr_next) != NULL) {
fr = before ? fr->fr_prev : fr->fr_next;
break;
}
fr = fr->fr_parent;
}
if (fr->fr_parent == NULL) {
return false;
}
while (fr->fr_layout != FR_LEAF) {
fr = fr->fr_child;
if (fr->fr_parent->fr_layout == FR_ROW && before) {
while (fr->fr_next != NULL) {
fr = fr->fr_next;
}
} else {
while (fr->fr_next != NULL && frame2win(fr)->w_winrow + fr->fr_height < sep_row) {
fr = fr->fr_next;
}
}
}
return (sep_row == fr->fr_win->w_winrow - 1 || sep_row == W_ENDROW(fr->fr_win));
}
/// Check if vertical separator of window "wp" at specified window corner is connected to the
/// vertical separator of another window
static bool vsep_connected(win_T *wp, WindowCorner corner)
{
bool before = (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT);
int sep_col = (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT)
? wp->w_wincol - 1 : W_ENDCOL(wp);
frame_T *fr = wp->w_frame;
while (fr->fr_parent != NULL) {
if (fr->fr_parent->fr_layout == FR_COL && (before ? fr->fr_prev : fr->fr_next) != NULL) {
fr = before ? fr->fr_prev : fr->fr_next;
break;
}
fr = fr->fr_parent;
}
if (fr->fr_parent == NULL) {
return false;
}
while (fr->fr_layout != FR_LEAF) {
fr = fr->fr_child;
if (fr->fr_parent->fr_layout == FR_COL && before) {
while (fr->fr_next != NULL) {
fr = fr->fr_next;
}
} else {
while (fr->fr_next != NULL && frame2win(fr)->w_wincol + fr->fr_width < sep_col) {
fr = fr->fr_next;
}
}
}
return (sep_col == fr->fr_win->w_wincol - 1 || sep_col == W_ENDCOL(fr->fr_win));
}
/// Get the value to show for the language mappings, active 'keymap'.
///
@ -5431,6 +5609,7 @@ static void win_redr_custom(win_T *wp, bool draw_ruler)
int use_sandbox = false;
win_T *ewp;
int p_crb_save;
bool is_stl_global = global_stl_height() > 0;
ScreenGrid *grid = &default_grid;
@ -5452,9 +5631,9 @@ static void win_redr_custom(win_T *wp, bool draw_ruler)
maxwidth = Columns;
use_sandbox = was_set_insecurely(wp, "tabline", 0);
} else {
row = W_ENDROW(wp);
row = is_stl_global ? (Rows - p_ch - 1) : W_ENDROW(wp);
fillchar = fillchar_status(&attr, wp);
maxwidth = wp->w_width;
maxwidth = is_stl_global ? Columns : wp->w_width;
if (draw_ruler) {
stl = p_ruf;
@ -5472,12 +5651,12 @@ static void win_redr_custom(win_T *wp, bool draw_ruler)
stl = p_ruf;
}
}
col = ru_col - (Columns - wp->w_width);
if (col < (wp->w_width + 1) / 2) {
col = (wp->w_width + 1) / 2;
col = ru_col - (Columns - maxwidth);
if (col < (maxwidth + 1) / 2) {
col = (maxwidth + 1) / 2;
}
maxwidth = wp->w_width - col;
if (!wp->w_status_height) {
maxwidth = maxwidth - col;
if (!wp->w_status_height && !is_stl_global) {
grid = &msg_grid_adj;
row = Rows - 1;
maxwidth--; // writing in last column may cause scrolling
@ -5495,7 +5674,7 @@ static void win_redr_custom(win_T *wp, bool draw_ruler)
use_sandbox = was_set_insecurely(wp, "statusline", *wp->w_p_stl == NUL ? 0 : OPT_LOCAL);
}
col += wp->w_wincol;
col += is_stl_global ? 0 : wp->w_wincol;
}
if (maxwidth <= 0) {
@ -7077,10 +7256,10 @@ int showmode(void)
clear_showcmd();
}
// If the last window has no status line, the ruler is after the mode
// message and must be redrawn
// If the last window has no status line and global statusline is disabled,
// the ruler is after the mode message and must be redrawn
win_T *last = lastwin_nofloating();
if (redrawing() && last->w_status_height == 0) {
if (redrawing() && last->w_status_height == 0 && global_stl_height() == 0) {
win_redr_ruler(last, true);
}
redraw_cmdline = false;
@ -7395,16 +7574,22 @@ int fillchar_status(int *attr, win_T *wp)
return '=';
}
/*
* Get the character to use in a separator between vertically split windows.
* Get its attributes in "*attr".
*/
/// Get the character to use in a separator between vertically split windows.
/// Get its attributes in "*attr".
static int fillchar_vsep(win_T *wp, int *attr)
{
*attr = win_hl_attr(wp, HLF_C);
return wp->w_p_fcs_chars.vert;
}
/// Get the character to use in a separator between horizontally split windows.
/// Get its attributes in "*attr".
static int fillchar_hsep(win_T *wp, int *attr)
{
*attr = win_hl_attr(wp, HLF_C);
return wp->w_p_fcs_chars.horiz;
}
/*
* Return TRUE if redrawing should currently be done.
*/
@ -7430,7 +7615,8 @@ void showruler(bool always)
if (!always && !redrawing()) {
return;
}
if ((*p_stl != NUL || *curwin->w_p_stl != NUL) && curwin->w_status_height) {
if ((*p_stl != NUL || *curwin->w_p_stl != NUL)
&& (curwin->w_status_height || global_stl_height())) {
redraw_custom_statusline(curwin);
} else {
win_redr_ruler(curwin, always);
@ -7449,6 +7635,7 @@ void showruler(bool always)
static void win_redr_ruler(win_T *wp, bool always)
{
bool is_stl_global = global_stl_height() > 0;
static bool did_show_ext_ruler = false;
// If 'ruler' off or redrawing disabled, don't do anything
@ -7466,7 +7653,7 @@ static void win_redr_ruler(win_T *wp, bool always)
// Don't draw the ruler while doing insert-completion, it might overwrite
// the (long) mode message.
if (wp == lastwin && lastwin->w_status_height == 0) {
if (wp == lastwin && lastwin->w_status_height == 0 && !is_stl_global) {
if (edit_submode != NULL) {
return;
}
@ -7521,6 +7708,12 @@ static void win_redr_ruler(win_T *wp, bool always)
off = wp->w_wincol;
width = wp->w_width;
part_of_status = true;
} else if (is_stl_global) {
row = Rows - p_ch - 1;
fillchar = fillchar_status(&attr, wp);
off = 0;
width = Columns;
part_of_status = true;
} else {
row = Rows - 1;
fillchar = ' ';
@ -7560,7 +7753,7 @@ static void win_redr_ruler(win_T *wp, bool always)
int i = (int)STRLEN(buffer);
get_rel_pos(wp, buffer + i + 1, RULER_BUF_LEN - i - 1);
int o = i + vim_strsize(buffer + i + 1);
if (wp->w_status_height == 0) { // can't use last char of screen
if (wp->w_status_height == 0 && !is_stl_global) { // can't use last char of screen
o++;
}
int this_ru_col = ru_col - (Columns - width);

View File

@ -19,6 +19,14 @@
#define NOT_VALID 40 // buffer needs complete redraw
#define CLEAR 50 // screen messed up, clear it
/// corner value flags for hsep_connected and vsep_connected
typedef enum {
WC_TOP_LEFT = 0,
WC_TOP_RIGHT,
WC_BOTTOM_LEFT,
WC_BOTTOM_RIGHT
} WindowCorner;
/// By default, all widows are draw on a single rectangular grid, represented by
/// this ScreenGrid instance. In multigrid mode each window will have its own
/// grid, then this is only used for global screen elements that hasn't been

View File

@ -6174,6 +6174,7 @@ static const char *highlight_init_both[] = {
"TermCursor cterm=reverse gui=reverse",
"VertSplit cterm=reverse gui=reverse",
"WildMenu ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
"default link WinSeparator VertSplit",
"default link EndOfBuffer NonText",
"default link LineNrAbove LineNr",
"default link LineNrBelow LineNr",
@ -6184,7 +6185,7 @@ static const char *highlight_init_both[] = {
"default link Whitespace NonText",
"default link MsgSeparator StatusLine",
"default link NormalFloat Pmenu",
"default link FloatBorder VertSplit",
"default link FloatBorder WinSeparator",
"default FloatShadow blend=80 guibg=Black",
"default FloatShadowThrough blend=100 guibg=Black",
"RedrawDebugNormal cterm=reverse gui=reverse",

View File

@ -146,7 +146,7 @@ func Test_highlight_eol_with_cursorline_vertsplit()
" 'abcd |abcd '
" ^^^^ ^^^^^^^^^ no highlight
" ^ 'Search' highlight
" ^ 'VertSplit' highlight
" ^ 'WinSeparator' highlight
let attrs0 = ScreenAttrs(1, 15)[0]
call assert_equal(repeat([attrs0[0]], 4), attrs0[0:3])
call assert_equal(repeat([attrs0[0]], 9), attrs0[6:14])
@ -160,7 +160,7 @@ func Test_highlight_eol_with_cursorline_vertsplit()
" 'abcd |abcd '
" ^^^^ underline
" ^ 'Search' highlight with underline
" ^ 'VertSplit' highlight
" ^ 'WinSeparator' highlight
" ^^^^^^^^^ no highlight
" underline

View File

@ -60,7 +60,7 @@
#define NOWIN (win_T *)-1 // non-existing window
#define ROWS_AVAIL (Rows - p_ch - tabline_height())
#define ROWS_AVAIL (Rows - p_ch - tabline_height() - global_stl_height())
/// flags for win_enter_ext()
typedef enum {
@ -658,6 +658,7 @@ win_T *win_new_float(win_T *wp, FloatConfig fconfig, Error *err)
}
wp->w_floating = 1;
wp->w_status_height = 0;
wp->w_hsep_height = 0;
wp->w_vsep_width = 0;
win_config_float(wp, fconfig);
@ -967,6 +968,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
int before;
int minheight;
int wmh1;
int hsep_height;
bool did_set_fraction = false;
// aucmd_win should always remain floating
@ -1079,6 +1081,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
}
}
} else {
hsep_height = global_stl_height() > 0 ? 1 : STATUS_HEIGHT;
layout = FR_COL;
/*
@ -1087,7 +1090,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
*/
// Current window requires at least 1 space.
wmh1 = p_wmh == 0 ? 1 : p_wmh;
needed = wmh1 + STATUS_HEIGHT;
needed = wmh1 + hsep_height;
if (flags & WSP_ROOM) {
needed += p_wh - wmh1;
}
@ -1129,15 +1132,15 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
new_size = oldwin_height / 2;
}
if (new_size > available - minheight - STATUS_HEIGHT) {
new_size = available - minheight - STATUS_HEIGHT;
if (new_size > available - minheight - hsep_height) {
new_size = available - minheight - hsep_height;
}
if (new_size < wmh1) {
new_size = wmh1;
}
// if it doesn't fit in the current window, need win_equal()
if (oldwin_height - new_size - STATUS_HEIGHT < p_wmh) {
if (oldwin_height - new_size - hsep_height < p_wmh) {
do_equal = true;
}
@ -1150,7 +1153,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
set_fraction(oldwin);
did_set_fraction = true;
win_setheight_win(oldwin->w_height + new_size + STATUS_HEIGHT,
win_setheight_win(oldwin->w_height + new_size + hsep_height,
oldwin);
oldwin_height = oldwin->w_height;
if (need_status) {
@ -1167,8 +1170,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
while (frp != NULL) {
if (frp->fr_win != oldwin && frp->fr_win != NULL
&& (frp->fr_win->w_height > new_size
|| frp->fr_win->w_height > oldwin_height - new_size
- STATUS_HEIGHT)) {
|| frp->fr_win->w_height > oldwin_height - new_size - hsep_height)) {
do_equal = true;
break;
}
@ -1294,13 +1296,15 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
if (flags & (WSP_TOP | WSP_BOT)) {
// set height and row of new window to full height
wp->w_winrow = tabline_height();
win_new_height(wp, curfrp->fr_height - (p_ls > 0));
wp->w_status_height = (p_ls > 0);
win_new_height(wp, curfrp->fr_height - (p_ls == 1 || p_ls == 2));
wp->w_status_height = (p_ls == 1 || p_ls == 2);
wp->w_hsep_height = 0;
} else {
// height and row of new window is same as current window
wp->w_winrow = oldwin->w_winrow;
win_new_height(wp, oldwin->w_height);
wp->w_status_height = oldwin->w_status_height;
wp->w_hsep_height = oldwin->w_hsep_height;
}
frp->fr_height = curfrp->fr_height;
@ -1333,6 +1337,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
frame_fix_width(oldwin);
frame_fix_width(wp);
} else {
bool is_stl_global = global_stl_height() > 0;
// width and column of new window is same as current window
if (flags & (WSP_TOP | WSP_BOT)) {
wp->w_wincol = 0;
@ -1348,28 +1353,53 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
// "new_size" of the current window goes to the new window, use
// one row for the status line
win_new_height(wp, new_size);
if (before) {
wp->w_hsep_height = is_stl_global ? 1 : 0;
} else {
wp->w_hsep_height = oldwin->w_hsep_height;
oldwin->w_hsep_height = is_stl_global ? 1 : 0;
}
if (flags & (WSP_TOP | WSP_BOT)) {
int new_fr_height = curfrp->fr_height - new_size;
if (!((flags & WSP_BOT) && p_ls == 0)) {
if (!((flags & WSP_BOT) && p_ls == 0) && global_stl_height() == 0) {
new_fr_height -= STATUS_HEIGHT;
} else if (global_stl_height() > 0) {
if (flags & WSP_BOT) {
frame_add_hsep(curfrp);
} else {
new_fr_height -= 1;
}
}
frame_new_height(curfrp, new_fr_height, flags & WSP_TOP, false);
} else {
win_new_height(oldwin, oldwin_height - (new_size + STATUS_HEIGHT));
win_new_height(oldwin, oldwin_height - (new_size
+ (global_stl_height() > 0 ? 1 : STATUS_HEIGHT)));
}
if (before) { // new window above current one
wp->w_winrow = oldwin->w_winrow;
if (is_stl_global) {
wp->w_status_height = 0;
oldwin->w_winrow += wp->w_height + 1;
} else {
wp->w_status_height = STATUS_HEIGHT;
oldwin->w_winrow += wp->w_height + STATUS_HEIGHT;
}
} else { // new window below current one
if (is_stl_global) {
wp->w_winrow = oldwin->w_winrow + oldwin->w_height + 1;
wp->w_status_height = 0;
} else {
wp->w_winrow = oldwin->w_winrow + oldwin->w_height + STATUS_HEIGHT;
wp->w_status_height = oldwin->w_status_height;
if (!(flags & WSP_BOT)) {
oldwin->w_status_height = STATUS_HEIGHT;
}
}
if (flags & WSP_BOT) {
}
if ((flags & WSP_BOT) && global_stl_height() == 0) {
frame_add_statusline(curfrp);
}
frame_fix_height(wp);
@ -1609,10 +1639,10 @@ int make_windows(int count, bool vertical)
maxcount = (curwin->w_width + curwin->w_vsep_width
- (p_wiw - p_wmw)) / (p_wmw + 1);
} else {
// Each window needs at least 'winminheight' lines and a status line.
maxcount = (curwin->w_height
+ curwin->w_status_height
- (p_wh - p_wmh)) / (p_wmh + STATUS_HEIGHT);
// Each window needs at least 'winminheight' lines
// If statusline isn't global, each window also needs a statusline
maxcount = (curwin->w_height + curwin->w_hsep_height + curwin->w_status_height
- (p_wh - p_wmh)) / (p_wmh + (global_stl_height() > 0 ? 1 : STATUS_HEIGHT));
}
if (maxcount < 2) {
@ -1707,7 +1737,7 @@ static void win_exchange(long Prenum)
* if wp != wp2
* 3. remove wp from the list
* 4. insert wp after wp2
* 5. exchange the status line height and vsep width.
* 5. exchange the status line height, hsep height and vsep width.
*/
wp2 = curwin->w_prev;
frp2 = curwin->w_frame->fr_prev;
@ -1733,6 +1763,9 @@ static void win_exchange(long Prenum)
temp = curwin->w_vsep_width;
curwin->w_vsep_width = wp->w_vsep_width;
wp->w_vsep_width = temp;
temp = curwin->w_hsep_height;
curwin->w_hsep_height = wp->w_hsep_height;
wp->w_hsep_height = temp;
frame_fix_height(curwin);
frame_fix_height(wp);
@ -1813,10 +1846,13 @@ static void win_rotate(bool upwards, int count)
frame_insert(frp->fr_parent->fr_child, frp);
}
// exchange status height and vsep width of old and new last window
// exchange status height, hsep height and vsep width of old and new last window
n = wp2->w_status_height;
wp2->w_status_height = wp1->w_status_height;
wp1->w_status_height = n;
n = wp2->w_hsep_height;
wp2->w_hsep_height = wp1->w_hsep_height;
wp1->w_hsep_height = n;
frame_fix_height(wp1);
frame_fix_height(wp2);
n = wp2->w_vsep_width;
@ -1893,11 +1929,16 @@ void win_move_after(win_T *win1, win_T *win2)
// check if there is something to do
if (win2->w_next != win1) {
// may need move the status line/vertical separator of the last window
// may need move the status line, horizontal or vertical separator of the last window
if (win1 == lastwin) {
height = win1->w_prev->w_status_height;
win1->w_prev->w_status_height = win1->w_status_height;
win1->w_status_height = height;
height = win1->w_prev->w_hsep_height;
win1->w_prev->w_hsep_height = win1->w_hsep_height;
win1->w_hsep_height = height;
if (win1->w_prev->w_vsep_width == 1) {
// Remove the vertical separator from the last-but-one window,
// add it to the last window. Adjust the frame widths.
@ -1910,6 +1951,11 @@ void win_move_after(win_T *win1, win_T *win2)
height = win1->w_status_height;
win1->w_status_height = win2->w_status_height;
win2->w_status_height = height;
height = win1->w_hsep_height;
win1->w_hsep_height = win2->w_hsep_height;
win2->w_hsep_height = height;
if (win1->w_vsep_width == 1) {
// Remove the vertical separator from win1, add it to the last
// window, win2. Adjust the frame widths.
@ -1973,6 +2019,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
int room = 0;
int new_size;
int has_next_curwin = 0;
int hsep_height;
bool hnc;
if (topfr->fr_layout == FR_LEAF) {
@ -2118,19 +2165,22 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
totwincount -= wincount;
}
} else { // topfr->fr_layout == FR_COL
hsep_height = global_stl_height() > 0 ? 1 : STATUS_HEIGHT;
topfr->fr_width = width;
topfr->fr_height = height;
if (dir != 'h') { // equalize frame heights
// Compute maximum number of windows vertically in this frame.
n = frame_minheight(topfr, NOWIN);
// add one for the bottom window if it doesn't have a statusline
// add one for the bottom window if it doesn't have a statusline or separator
if (row + height == cmdline_row && p_ls == 0) {
extra_sep = STATUS_HEIGHT;
} else if (global_stl_height() > 0) {
extra_sep = 1;
} else {
extra_sep = 0;
}
totwincount = (n + extra_sep) / (p_wmh + 1);
totwincount = (n + extra_sep) / (p_wmh + hsep_height);
has_next_curwin = frame_has_win(topfr, next_curwin);
/*
@ -2165,7 +2215,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
} else {
// These windows don't use up room.
totwincount -= (n + (fr->fr_next == NULL
? extra_sep : 0)) / (p_wmh + 1);
? extra_sep : 0)) / (p_wmh + hsep_height);
}
room -= new_size - n;
if (room < 0) {
@ -2211,7 +2261,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
// Compute the maximum number of windows vert. in "fr".
n = frame_minheight(fr, NOWIN);
wincount = (n + (fr->fr_next == NULL ? extra_sep : 0))
/ (p_wmh + 1);
/ (p_wmh + hsep_height);
m = frame_minheight(fr, next_curwin);
if (has_next_curwin) {
hnc = frame_has_win(fr, next_curwin);
@ -3167,7 +3217,7 @@ static tabpage_T *alt_tabpage(void)
/*
* Find the left-upper window in frame "frp".
*/
static win_T *frame2win(frame_T *frp)
win_T *frame2win(frame_T *frp)
{
while (frp->fr_win == NULL) {
frp = frp->fr_child;
@ -3194,23 +3244,40 @@ static bool frame_has_win(const frame_T *frp, const win_T *wp)
return false;
}
/// Check if current window is at the bottom
/// Returns true if there are no windows below current window
static bool is_bottom_win(win_T *wp)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
frame_T *frp;
for (frp = wp->w_frame; frp->fr_parent != NULL; frp = frp->fr_parent) {
if (frp->fr_parent->fr_layout == FR_COL && frp->fr_next != NULL) {
return false;
}
}
return true;
}
/// Set a new height for a frame. Recursively sets the height for contained
/// frames and windows. Caller must take care of positions.
///
/// @param topfirst resize topmost contained frame first.
/// @param wfh obey 'winfixheight' when there is a choice;
/// may cause the height not to be set.
static void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wfh)
void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wfh)
FUNC_ATTR_NONNULL_ALL
{
frame_T *frp;
int extra_lines;
int h;
win_T *wp;
if (topfrp->fr_win != NULL) {
// Simple case: just one window.
win_new_height(topfrp->fr_win,
height - topfrp->fr_win->w_status_height);
wp = topfrp->fr_win;
if (is_bottom_win(wp)) {
wp->w_hsep_height = 0;
}
win_new_height(wp, height - wp->w_hsep_height - wp->w_status_height);
} else if (topfrp->fr_layout == FR_ROW) {
do {
// All frames in this row get the same new height.
@ -3366,8 +3433,8 @@ static void frame_add_statusline(frame_T *frp)
if (frp->fr_layout == FR_LEAF) {
wp = frp->fr_win;
if (wp->w_status_height == 0) {
if (wp->w_height > 0) { // don't make it negative
--wp->w_height;
if (wp->w_height - STATUS_HEIGHT >= 0) { // don't make it negative
wp->w_height -= STATUS_HEIGHT;
}
wp->w_status_height = STATUS_HEIGHT;
}
@ -3487,10 +3554,8 @@ static void frame_new_width(frame_T *topfrp, int width, bool leftfirst, bool wfw
topfrp->fr_width = width;
}
/*
* Add the vertical separator to windows at the right side of "frp".
* Note: Does not check if there is room!
*/
/// Add the vertical separator to windows at the right side of "frp".
/// Note: Does not check if there is room!
static void frame_add_vsep(const frame_T *frp)
FUNC_ATTR_NONNULL_ARG(1)
{
@ -3520,6 +3585,37 @@ static void frame_add_vsep(const frame_T *frp)
}
}
/// Add the horizontal separator to windows at the bottom of "frp".
/// Note: Does not check if there is room or whether the windows have a statusline!
static void frame_add_hsep(const frame_T *frp)
FUNC_ATTR_NONNULL_ARG(1)
{
win_T *wp;
if (frp->fr_layout == FR_LEAF) {
wp = frp->fr_win;
if (wp->w_hsep_height == 0) {
if (wp->w_height > 0) { // don't make it negative
wp->w_height++;
}
wp->w_hsep_height = 1;
}
} else if (frp->fr_layout == FR_ROW) {
// Handle all the frames in the row.
FOR_ALL_FRAMES(frp, frp->fr_child) {
frame_add_hsep(frp);
}
} else {
assert(frp->fr_layout == FR_COL);
// Only need to handle the last frame in the column.
frp = frp->fr_child;
while (frp->fr_next != NULL) {
frp = frp->fr_next;
}
frame_add_hsep(frp);
}
}
/*
* Set frame width from the window it contains.
*/
@ -3534,7 +3630,7 @@ static void frame_fix_width(win_T *wp)
static void frame_fix_height(win_T *wp)
FUNC_ATTR_NONNULL_ALL
{
wp->w_frame->fr_height = wp->w_height + wp->w_status_height;
wp->w_frame->fr_height = wp->w_height + wp->w_hsep_height + wp->w_status_height;
}
/*
@ -3552,10 +3648,11 @@ static int frame_minheight(frame_T *topfrp, win_T *next_curwin)
if (topfrp->fr_win != NULL) {
if (topfrp->fr_win == next_curwin) {
m = p_wh + topfrp->fr_win->w_status_height;
m = p_wh + topfrp->fr_win->w_hsep_height + topfrp->fr_win->w_status_height;
} else {
// window: minimal height of the window plus status line
m = p_wmh + topfrp->fr_win->w_status_height;
// window: minimal height of the window plus separator column or status line
// depending on whether global statusline is enabled
m = p_wmh + topfrp->fr_win->w_hsep_height + topfrp->fr_win->w_status_height;
if (topfrp->fr_win == curwin && next_curwin == NULL) {
// Current window is minimal one line high.
if (p_wmh == 0) {
@ -3784,7 +3881,7 @@ static int win_alloc_firstwin(win_T *oldwin)
new_frame(curwin);
topframe = curwin->w_frame;
topframe->fr_width = Columns;
topframe->fr_height = Rows - p_ch;
topframe->fr_height = Rows - p_ch - global_stl_height();
return OK;
}
@ -5200,11 +5297,8 @@ void win_size_restore(garray_T *gap)
}
}
/*
* Update the position for all windows, using the width and height of the
* frames.
* Returns the row just after the last window.
*/
// Update the position for all windows, using the width and height of the frames.
// Returns the row just after the last window and global statusline (if there is one).
int win_comp_pos(void)
{
int row = tabline_height();
@ -5219,7 +5313,7 @@ int win_comp_pos(void)
}
}
return row;
return row + global_stl_height();
}
void win_reconfig_floats(void)
@ -5253,7 +5347,7 @@ static void frame_comp_pos(frame_T *topfrp, int *row, int *col)
wp->w_redr_status = true;
wp->w_pos_changed = true;
}
const int h = wp->w_height + wp->w_status_height;
const int h = wp->w_height + wp->w_hsep_height + wp->w_status_height;
*row += h > topfrp->fr_height ? topfrp->fr_height : h;
*col += wp->w_width + wp->w_vsep_width;
} else {
@ -5302,7 +5396,7 @@ void win_setheight_win(int height, win_T *win)
win_config_float(win, win->w_float_config);
redraw_later(win, NOT_VALID);
} else {
frame_setheight(win->w_frame, height + win->w_status_height);
frame_setheight(win->w_frame, height + win->w_hsep_height + win->w_status_height);
// recompute the window positions
int row = win_comp_pos();
@ -5393,8 +5487,8 @@ static void frame_setheight(frame_T *curfrp, int height)
room_cmdline = 0;
} else {
win_T *wp = lastwin_nofloating();
room_cmdline = Rows - p_ch
- (wp->w_winrow + wp->w_height + wp->w_status_height);
room_cmdline = Rows - p_ch - global_stl_height()
- (wp->w_winrow + wp->w_height + wp->w_hsep_height + wp->w_status_height);
if (room_cmdline < 0) {
room_cmdline = 0;
}
@ -5756,7 +5850,7 @@ void win_drag_status_line(win_T *dragwin, int offset)
} else { // drag down
up = false;
// Only dragging the last status line can reduce p_ch.
room = Rows - cmdline_row;
room = Rows - cmdline_row - global_stl_height();
if (curfr->fr_next == NULL) {
room -= 1;
} else {
@ -6397,32 +6491,26 @@ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u
return find_file_name_in_path(ptr, len, options, count, rel_fname);
}
/// Add or remove a status line for the bottom window(s), according to the
/// Add or remove a status line from window(s), according to the
/// value of 'laststatus'.
///
/// @param morewin pretend there are two or more windows if true.
void last_status(bool morewin)
{
// Don't make a difference between horizontal or vertical split.
last_status_rec(topframe, (p_ls == 2
|| (p_ls == 1 && (morewin || !one_window()))));
last_status_rec(topframe, (p_ls == 2 || (p_ls == 1 && (morewin || !one_window()))),
global_stl_height() > 0);
}
static void last_status_rec(frame_T *fr, bool statusline)
// Look for resizable frames and take lines from them to make room for the statusline
static void resize_frame_for_status(frame_T *fr, int resize_amount)
{
frame_T *fp;
win_T *wp;
if (fr->fr_layout == FR_LEAF) {
wp = fr->fr_win;
if (wp->w_status_height != 0 && !statusline) {
// remove status line
win_new_height(wp, wp->w_height + 1);
wp->w_status_height = 0;
comp_col();
} else if (wp->w_status_height == 0 && statusline) {
// Find a frame to take a line from.
fp = fr;
frame_T *fp = fr;
win_T *wp = fr->fr_win;
int n;
while (resize_amount > 0) {
while (fp->fr_height <= frame_minheight(fp, NULL)) {
if (fp == topframe) {
emsg(_(e_noroom));
@ -6436,33 +6524,71 @@ static void last_status_rec(frame_T *fr, bool statusline)
fp = fp->fr_parent;
}
}
wp->w_status_height = 1;
n = MIN(fp->fr_height - frame_minheight(fp, NULL), resize_amount);
resize_amount -= n;
if (fp != fr) {
frame_new_height(fp, fp->fr_height - 1, false, false);
frame_new_height(fp, fp->fr_height - n, false, false);
frame_fix_height(wp);
(void)win_comp_pos();
} else {
win_new_height(wp, wp->w_height - 1);
win_new_height(wp, wp->w_height - n);
}
comp_col();
redraw_all_later(SOME_VALID);
}
} else if (fr->fr_layout == FR_ROW) {
// vertically split windows, set status line for each one
FOR_ALL_FRAMES(fp, fr->fr_child) {
last_status_rec(fp, statusline);
}
} else {
// horizontally split window, set status line for last one
for (fp = fr->fr_child; fp->fr_next != NULL; fp = fp->fr_next) {
}
last_status_rec(fp, statusline);
}
}
/*
* Return the number of lines used by the tab page line.
*/
static void last_status_rec(frame_T *fr, bool statusline, bool is_stl_global)
{
frame_T *fp;
win_T *wp;
if (fr->fr_layout == FR_LEAF) {
wp = fr->fr_win;
bool is_last = is_bottom_win(wp);
if (is_last) {
if (wp->w_status_height != 0 && (!statusline || is_stl_global)) {
// Remove status line
wp->w_status_height = 0;
win_new_height(wp, wp->w_height + STATUS_HEIGHT);
comp_col();
} else if (wp->w_status_height == 0 && !is_stl_global && statusline) {
// Add statusline to window if needed
wp->w_status_height = STATUS_HEIGHT;
resize_frame_for_status(fr, STATUS_HEIGHT);
comp_col();
}
} else if (wp->w_status_height != 0 && is_stl_global) {
// If statusline is global and the window has a statusline, replace it with a horizontal
// separator
if (STATUS_HEIGHT - 1 != 0) {
win_new_height(wp, wp->w_height + STATUS_HEIGHT - 1);
}
wp->w_status_height = 0;
wp->w_hsep_height = 1;
comp_col();
} else if (wp->w_status_height == 0 && !is_stl_global) {
// If statusline isn't global and the window doesn't have a statusline, re-add it
wp->w_status_height = STATUS_HEIGHT;
wp->w_hsep_height = 0;
resize_frame_for_status(fr, STATUS_HEIGHT - 1);
comp_col();
}
redraw_all_later(SOME_VALID);
} else if (fr->fr_layout == FR_COL) {
// For a column frame, recursively call this function for all child frames
FOR_ALL_FRAMES(fp, fr->fr_child) {
last_status_rec(fp, statusline, is_stl_global);
}
} else {
// For a row frame, recursively call this function for all child frames
FOR_ALL_FRAMES(fp, fr->fr_child) {
last_status_rec(fp, statusline, is_stl_global);
}
}
}
/// Return the number of lines used by the tab page line.
int tabline_height(void)
{
if (ui_has(kUITabline)) {
@ -6478,10 +6604,14 @@ int tabline_height(void)
return 1;
}
/*
* Return the minimal number of rows that is needed on the screen to display
* the current number of windows.
*/
/// Return the number of lines used by the global statusline
int global_stl_height(void)
{
return (p_ls == 3) ? STATUS_HEIGHT : 0;
}
/// Return the minimal number of rows that is needed on the screen to display
/// the current number of windows.
int min_rows(void)
{
if (firstwin == NULL) { // not initialized yet
@ -6495,7 +6625,7 @@ int min_rows(void)
total = n;
}
}
total += tabline_height();
total += tabline_height() + global_stl_height();
total += 1; // count the room for the command line
return total;
}

View File

@ -212,10 +212,10 @@ describe('ui/cursor', function()
if m.blinkwait then m.blinkwait = 700 end
end
if m.hl_id then
m.hl_id = 60
m.hl_id = 61
m.attr = {background = Screen.colors.DarkGray}
end
if m.id_lm then m.id_lm = 61 end
if m.id_lm then m.id_lm = 62 end
end
-- Assert the new expectation.

View File

@ -0,0 +1,233 @@
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local clear, command, feed = helpers.clear, helpers.command, helpers.feed
describe('global statusline', function()
local screen
before_each(function()
clear()
screen = Screen.new(60, 16)
screen:attach()
command('set laststatus=3')
command('set ruler')
end)
it('works', function()
screen:expect{grid=[[
^ |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{2:[No Name] 0,0-1 All}|
|
]], attr_ids={
[1] = {bold = true, foreground = Screen.colors.Blue1};
[2] = {bold = true, reverse = true};
}}
feed('i<CR><CR>')
screen:expect{grid=[[
|
|
^ |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{2:[No Name] [+] 3,1 All}|
{3:-- INSERT --} |
]], attr_ids={
[1] = {bold = true, foreground = Screen.colors.Blue};
[2] = {bold = true, reverse = true};
[3] = {bold = true};
}}
end)
it('works with splits', function()
command('vsplit | split | vsplit | vsplit | wincmd l | split | 2wincmd l | split')
screen:expect{grid=[[
{1:} {1:} {1:}^ |
{2:~ }{1:}{2:~ }{1:}{2:~}{1:}{2:~ }|
{2:~ }{1:}{2:~ }{1:}{2:~}{1:}{2:~ }|
{2:~ }{1:}{2:~ }{1:}{2:~}{1:}{2:~ }|
{2:~ }{1:}{2:~}{1:}{2:~ }|
{2:~ }{1:} {1:}{2:~}{1:}{2:~ }|
{2:~ }{1:}{2:~ }{1:}{2:~}{1:}{2:~ }|
{2:~ }{1:}{2:~ }{1:}{2:~}{1:}|
{2:~ }{1:}{2:~ }{1:}{2:~}{1:} |
{1:}{2:~ }|
{1:}{2:~ }|
{2:~ }{1:}{2:~ }|
{2:~ }{1:}{2:~ }|
{2:~ }{1:}{2:~ }|
{3:[No Name] 0,0-1 All}|
|
]], attr_ids={
[1] = {reverse = true};
[2] = {bold = true, foreground = Screen.colors.Blue1};
[3] = {bold = true, reverse = true};
}}
end)
it('works when switching between values of laststatus', function()
command('set laststatus=1')
screen:expect{grid=[[
^ |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
0,0-1 All |
]], attr_ids={
[1] = {foreground = Screen.colors.Blue, bold = true};
}}
command('set laststatus=3')
screen:expect{grid=[[
^ |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{2:[No Name] 0,0-1 All}|
|
]], attr_ids={
[1] = {foreground = Screen.colors.Blue, bold = true};
[2] = {reverse = true, bold = true};
}}
command('vsplit | split | vsplit | vsplit | wincmd l | split | 2wincmd l | split')
command('set laststatus=2')
screen:expect{grid=[[
{1:} {1:} {1:}^ |
{2:~ }{1:}{2:~ }{1:}{2:~}{1:}{2:~ }|
{2:~ }{1:}{2:~ }{1:}{2:~}{1:}{2:~ }|
{2:~ }{1:}{2:~ }{1:}{2:~}{1:}{2:~ }|
{2:~ }{1:< Name] 0,0-1 }{2:~}{1:}{2:~ }|
{2:~ }{1:} {1:}{2:~}{1:}{2:~ }|
{2:~ }{1:}{2:~ }{1:}{2:~}{1:}{2:~ }|
{2:~ }{1:}{2:~ }{1:}{2:~}{1:}{3:<No Name] 0,0-1 All}|
{2:~ }{1:}{2:~ }{1:}{2:~}{1:} |
{1:<No Name] 0,0-1 All < Name] 0,0-1 <}{2:~ }|
{1:}{2:~ }|
{2:~ }{1:}{2:~ }|
{2:~ }{1:}{2:~ }|
{2:~ }{1:}{2:~ }|
{1:[No Name] 0,0-1 All <No Name] 0,0-1 All}|
|
]], attr_ids={
[1] = {reverse = true};
[2] = {foreground = Screen.colors.Blue, bold = true};
[3] = {reverse = true, bold = true};
}}
command('set laststatus=3')
screen:expect{grid=[[
{1:} {1:} {1:}^ |
{2:~ }{1:}{2:~ }{1:}{2:~}{1:}{2:~ }|
{2:~ }{1:}{2:~ }{1:}{2:~}{1:}{2:~ }|
{2:~ }{1:}{2:~ }{1:}{2:~}{1:}{2:~ }|
{2:~ }{1:}{2:~}{1:}{2:~ }|
{2:~ }{1:} {1:}{2:~}{1:}{2:~ }|
{2:~ }{1:}{2:~ }{1:}{2:~}{1:}{2:~ }|
{2:~ }{1:}{2:~ }{1:}{2:~}{1:}|
{2:~ }{1:}{2:~ }{1:}{2:~}{1:} |
{1:}{2:~ }|
{1:}{2:~ }|
{2:~ }{1:}{2:~ }|
{2:~ }{1:}{2:~ }|
{2:~ }{1:}{2:~ }|
{3:[No Name] 0,0-1 All}|
|
]], attr_ids={
[1] = {reverse = true};
[2] = {foreground = Screen.colors.Blue, bold = true};
[3] = {reverse = true, bold = true};
}}
command('set laststatus=0')
screen:expect{grid=[[
{1:} {1:} {1:}^ |
{2:~ }{1:}{2:~ }{1:}{2:~}{1:}{2:~ }|
{2:~ }{1:}{2:~ }{1:}{2:~}{1:}{2:~ }|
{2:~ }{1:}{2:~ }{1:}{2:~}{1:}{2:~ }|
{2:~ }{1:< Name] 0,0-1 }{2:~}{1:}{2:~ }|
{2:~ }{1:} {1:}{2:~}{1:}{2:~ }|
{2:~ }{1:}{2:~ }{1:}{2:~}{1:}{2:~ }|
{2:~ }{1:}{2:~ }{1:}{2:~}{1:}{3:<No Name] 0,0-1 All}|
{2:~ }{1:}{2:~ }{1:}{2:~}{1:} |
{1:<No Name] 0,0-1 All < Name] 0,0-1 <}{2:~ }|
{1:}{2:~ }|
{2:~ }{1:}{2:~ }|
{2:~ }{1:}{2:~ }|
{2:~ }{1:}{2:~ }|
{2:~ }{1:}{2:~ }|
0,0-1 All |
]], attr_ids={
[1] = {reverse = true};
[2] = {foreground = Screen.colors.Blue, bold = true};
[3] = {reverse = true, bold = true};
}}
command('set laststatus=3')
screen:expect{grid=[[
{1:} {1:} {1:}^ |
{2:~ }{1:}{2:~ }{1:}{2:~}{1:}{2:~ }|
{2:~ }{1:}{2:~ }{1:}{2:~}{1:}{2:~ }|
{2:~ }{1:}{2:~ }{1:}{2:~}{1:}{2:~ }|
{2:~ }{1:}{2:~}{1:}{2:~ }|
{2:~ }{1:} {1:}{2:~}{1:}{2:~ }|
{2:~ }{1:}{2:~ }{1:}{2:~}{1:}{2:~ }|
{2:~ }{1:}{2:~ }{1:}{2:~}{1:}|
{2:~ }{1:}{2:~ }{1:}{2:~}{1:} |
{1:}{2:~ }|
{1:}{2:~ }|
{2:~ }{1:}{2:~ }|
{2:~ }{1:}{2:~ }|
{2:~ }{1:}{2:~ }|
{3:[No Name] 0,0-1 All}|
|
]], attr_ids={
[1] = {reverse = true};
[2] = {foreground = Screen.colors.Blue, bold = true};
[3] = {reverse = true, bold = true};
}}
end)
end)

View File

@ -59,7 +59,7 @@ describe('ext_hlstate detailed highlights', function()
it('work with cleared UI highlights', function()
screen:set_default_attr_ids({
[1] = {{}, {{hi_name = "VertSplit", ui_name = "VertSplit", kind = "ui"}}},
[1] = {{}, {{hi_name = "VertSplit", ui_name = "WinSeparator", kind = "ui"}}},
[2] = {{bold = true, foreground = Screen.colors.Blue1},
{{hi_name = "NonText", ui_name = "EndOfBuffer", kind = "ui"}}},
[3] = {{bold = true, reverse = true},

View File

@ -48,6 +48,7 @@ local predefined_hl_defs = {
TermCursor=true,
VertSplit=true,
WildMenu=true,
WinSeparator=true,
EndOfBuffer=true,
QuickFixLine=true,
Substitute=true,