feat: add support for global statusline

Ref: #9342

Adds the option to have a single global statusline for the current window at the bottom of the screen instead of a statusline at the bottom of every window. Enabled by setting `laststatus = 3`.

Due to the fact that statuslines at the bottom of windows are removed when global statusline is enabled, horizontal separators are used instead to separate horizontal splits. The horizontal separator character is configurable through the`horiz` item in `'fillchars'`. Separator connector characters are also used to connect the horizontal and vertical separators together, which are also configurable through the `horizup`, `horizdown`, `vertleft`, `vertright` and `verthoriz` items in `fillchars`.

The window separators are highlighted using the `WinSeparator` highlight group, which supersedes `VertSplit` and is linked to `VertSplit` by default in order to maintain backwards compatibility.
This commit is contained in:
Famiu Haque 2022-01-30 11:57:41 +06:00
parent 046950f630
commit 5ab1229174
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 vertical separator like "l" or "𝖨" may be used. It may also be
hidden by changing its color to the foreground color: > hidden by changing its color to the foreground color: >
:set fillchars=vert:l :set fillchars=vert:l
:hi VertSplit ctermbg=White :hi WinSeparator ctermbg=White
< Note that this is a workaround, not a proper solution. < Note that this is a workaround, not a proper solution.
If, on the other hand, you'd like to be verbose and explicit and 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 ~ item default Used for ~
stl:c ' ' or '^' statusline of the current window stl:c ' ' or '^' statusline of the current window
stlnc:c ' ' or '=' statusline of the non-current windows 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| 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' fold:c '·' or '-' filling 'foldtext'
foldopen:c '-' mark the beginning of a fold foldopen:c '-' mark the beginning of a fold
foldclose:c '+' show a closed 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 '=' "stlnc" the space will be used when there is highlighting, '^' or '='
otherwise. otherwise.
If 'ambiwidth' is "double" then "vert", "foldsep" and "fold" default to Note that "horiz", "horizup", "horizdown", "vertleft", "vertright" and
single-byte alternatives. "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: > Example: >
:set fillchars=stl:^,stlnc:=,vert:│,fold:·,diff:- :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 ~ item highlight group ~
stl:c StatusLine |hl-StatusLine| stl:c StatusLine |hl-StatusLine|
stlnc:c StatusLineNC |hl-StatusLineNC| 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| fold:c Folded |hl-Folded|
diff:c DiffDelete |hl-DiffDelete| diff:c DiffDelete |hl-DiffDelete|
eob:c EndOfBuffer |hl-EndOfBuffer| 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 0: never
1: only if there are at least two windows 1: only if there are at least two windows
2: always 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 The screen looks nicer with a status line if you have several
windows, but it takes another screen line. |status-line| 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. empty to avoid further errors. Otherwise screen updating would loop.
Note that the only effect of 'ruler' when this option is set (and 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 ~ field meaning ~
- Left justify the item. The default is right justified - Left justify the item. The default is right justified

View File

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

View File

@ -482,6 +482,8 @@ statusline:
0 never 0 never
1 only when there are split windows (the default) 1 only when there are split windows (the default)
2 always 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. 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: For Command-line commands this is done by prepending an "s". For example:

View File

@ -211,6 +211,7 @@ Highlight groups:
|hl-Substitute| |hl-Substitute|
|hl-TermCursor| |hl-TermCursor|
|hl-TermCursorNC| |hl-TermCursorNC|
|hl-WinSeparator| highlights window separators
|hl-Whitespace| highlights 'listchars' whitespace |hl-Whitespace| highlights 'listchars' whitespace
Input/Mappings: Input/Mappings:
@ -227,9 +228,11 @@ Options:
'cpoptions' flags: |cpo-_| 'cpoptions' flags: |cpo-_|
'display' flags: "msgsep" minimizes scrolling when showing messages 'display' flags: "msgsep" minimizes scrolling when showing messages
'guicursor' works in the terminal '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 'foldcolumn' supports up to 9 dynamic/fixed columns
'inccommand' shows interactive results for |:substitute|-like commands 'inccommand' shows interactive results for |:substitute|-like commands
'laststatus' global statusline support
'pumblend' pseudo-transparent popupmenu 'pumblend' pseudo-transparent popupmenu
'scrollback' 'scrollback'
'signcolumn' supports up to 9 dynamic/fixed columns 'signcolumn' supports up to 9 dynamic/fixed columns
@ -348,6 +351,7 @@ Highlight groups:
|hl-ColorColumn|, |hl-CursorColumn| are lower priority than most other |hl-ColorColumn|, |hl-CursorColumn| are lower priority than most other
groups groups
|hl-CursorLine| is low-priority unless foreground color is set |hl-CursorLine| is low-priority unless foreground color is set
*hl-VertSplit* superseded by |hl-WinSeparator|
Macro/|recording| behavior Macro/|recording| behavior
Replay of a macro recorded during :lmap produces the same actions as when it 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' = 0 never a status line
'laststatus' = 1 status line if there is more than one window 'laststatus' = 1 status line if there is more than one window
'laststatus' = 2 always a status line '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. 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 This option can be local to the window, so that you can have a different

View File

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

View File

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

View File

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

View File

@ -1227,7 +1227,13 @@ struct window_S {
struct { struct {
int stl; int stl;
int stlnc; int stlnc;
int horiz;
int horizup;
int horizdown;
int vert; int vert;
int vertleft;
int vertright;
int verthoriz;
int fold; int fold;
int foldopen; ///< when fold is open int foldopen; ///< when fold is open
int foldclosed; ///< when fold is closed int foldclosed; ///< when fold is closed
@ -1273,7 +1279,8 @@ struct window_S {
int w_status_height; // number of status lines (0 or 1) int w_status_height; // number of status lines (0 or 1)
int w_wincol; // Leftmost column of window in screen. int w_wincol; // Leftmost column of window in screen.
int w_width; // Width of window, excluding separation. 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 pos_save_T w_save_cursor; // backup of cursor pos and topline
// inner size of window, which can be overridden by external UI // inner size of window, which can be overridden by external UI

View File

@ -3886,7 +3886,7 @@ static void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
wp = mouse_find_win(&grid, &row, &col); wp = mouse_find_win(&grid, &row, &col);
if (wp != NULL) { 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 // 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. // necessary for a top border since `row` starts at -1 in that case.
if (row < height + wp->w_border_adj[2]) { 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(); validate_cursor();
// May redraw the status line to show the cursor position. // 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; curwin->w_redr_status = true;
} }
@ -3631,7 +3631,7 @@ void compute_cmdrow(void)
} else { } else {
win_T *wp = lastwin_nofloating(); win_T *wp = lastwin_nofloating();
cmdline_row = wp->w_winrow + wp->w_height 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; lines_left = cmdline_row;
} }

View File

@ -72,7 +72,7 @@ static int ses_winsizes(FILE *fd, int restore_size, win_T *tab_firstwin)
n++; n++;
// restore height when not full height // 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, && (fprintf(fd,
"exe '%dresize ' . ((&lines * %" PRId64 "exe '%dresize ' . ((&lines * %" PRId64
" + %" PRId64 ") / %" PRId64 ")\n", " + %" PRId64 ") / %" PRId64 ")\n",

View File

@ -70,7 +70,8 @@ typedef enum {
HLF_R, // return to continue message and yes/no questions HLF_R, // return to continue message and yes/no questions
HLF_S, // status lines HLF_S, // status lines
HLF_SNC, // status lines of not-current windows 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_T, // Titles for output from ":set all", ":autocmd" etc.
HLF_V, // Visual mode HLF_V, // Visual mode
HLF_VNC, // Visual mode, autoselecting and not clipboard owner HLF_VNC, // Visual mode, autoselecting and not clipboard owner
@ -129,10 +130,11 @@ EXTERN const char *hlf_names[] INIT(= {
[HLF_R] = "Question", [HLF_R] = "Question",
[HLF_S] = "StatusLine", [HLF_S] = "StatusLine",
[HLF_SNC] = "StatusLineNC", [HLF_SNC] = "StatusLineNC",
[HLF_C] = "VertSplit", [HLF_C] = "WinSeparator",
[HLF_T] = "Title", [HLF_T] = "Title",
[HLF_V] = "Visual", [HLF_V] = "Visual",
[HLF_VNC] = "VisualNC", [HLF_VNC] = "VisualNC",
[HLF_VSP] = "VertSplit",
[HLF_W] = "WarningMsg", [HLF_W] = "WarningMsg",
[HLF_WM] = "WildMenu", [HLF_WM] = "WildMenu",
[HLF_FL] = "Folded", [HLF_FL] = "Folded",

View File

@ -2910,7 +2910,7 @@ ambw_end:
|| check_opt_strings(curbuf->b_p_bt, p_buftype_values, false) != OK) { || check_opt_strings(curbuf->b_p_bt, p_buftype_values, false) != OK) {
errmsg = e_invarg; errmsg = e_invarg;
} else { } else {
if (curwin->w_status_height) { if (curwin->w_status_height || global_stl_height()) {
curwin->w_redr_status = true; curwin->w_redr_status = true;
redraw_later(curwin, VALID); redraw_later(curwin, VALID);
} }
@ -3553,16 +3553,22 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set)
struct chars_tab *tab; struct chars_tab *tab;
struct chars_tab fcs_tab[] = { struct chars_tab fcs_tab[] = {
{ &wp->w_p_fcs_chars.stl, "stl", ' ' }, { &wp->w_p_fcs_chars.stl, "stl", ' ' },
{ &wp->w_p_fcs_chars.stlnc, "stlnc", ' ' }, { &wp->w_p_fcs_chars.stlnc, "stlnc", ' ' },
{ &wp->w_p_fcs_chars.vert, "vert", 9474 }, // │ { &wp->w_p_fcs_chars.horiz, "horiz", 9472 }, // ─
{ &wp->w_p_fcs_chars.fold, "fold", 183 }, // · { &wp->w_p_fcs_chars.horizup, "horizup", 9524 }, // ┴
{ &wp->w_p_fcs_chars.foldopen, "foldopen", '-' }, { &wp->w_p_fcs_chars.horizdown, "horizdown", 9516 }, // ┬
{ &wp->w_p_fcs_chars.foldclosed, "foldclose", '+' }, { &wp->w_p_fcs_chars.vert, "vert", 9474 }, // │
{ &wp->w_p_fcs_chars.foldsep, "foldsep", 9474 }, // │ { &wp->w_p_fcs_chars.vertleft, "vertleft", 9508 }, // ┤
{ &wp->w_p_fcs_chars.diff, "diff", '-' }, { &wp->w_p_fcs_chars.vertright, "vertright", 9500 }, // ├
{ &wp->w_p_fcs_chars.msgsep, "msgsep", ' ' }, { &wp->w_p_fcs_chars.verthoriz, "verthoriz", 9532 }, // ┼
{ &wp->w_p_fcs_chars.eob, "eob", '~' }, { &wp->w_p_fcs_chars.fold, "fold", 183 }, // ·
{ &wp->w_p_fcs_chars.foldopen, "foldopen", '-' },
{ &wp->w_p_fcs_chars.foldclosed, "foldclose", '+' },
{ &wp->w_p_fcs_chars.foldsep, "foldsep", 9474 }, // │
{ &wp->w_p_fcs_chars.diff, "diff", '-' },
{ &wp->w_p_fcs_chars.msgsep, "msgsep", ' ' },
{ &wp->w_p_fcs_chars.eob, "eob", '~' },
}; };
struct chars_tab lcs_tab[] = { struct chars_tab lcs_tab[] = {
{ &wp->w_p_lcs_chars.eol, "eol", NUL }, { &wp->w_p_lcs_chars.eol, "eol", NUL },
@ -3589,15 +3595,17 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set)
varp = &p_fcs; varp = &p_fcs;
} }
if (*p_ambw == 'd') { if (*p_ambw == 'd') {
// XXX: If ambiwidth=double then "|" and "·" take 2 columns, which is // XXX: If ambiwidth=double then some characters take 2 columns,
// forbidden (TUI limitation?). Set old defaults. // which is forbidden (TUI limitation?). Set old defaults.
fcs_tab[2].def = '|'; fcs_tab[2].def = '-';
fcs_tab[6].def = '|'; fcs_tab[3].def = '-';
fcs_tab[3].def = '-'; fcs_tab[4].def = '-';
} else { fcs_tab[5].def = '|';
fcs_tab[2].def = 9474; // │ fcs_tab[6].def = '|';
fcs_tab[6].def = 9474; // │ fcs_tab[7].def = '|';
fcs_tab[3].def = 183; // · fcs_tab[8].def = '+';
fcs_tab[9].def = '-';
fcs_tab[12].def = '|';
} }
} }
@ -4474,6 +4482,20 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
// 'winminwidth' // 'winminwidth'
win_setminwidth(); win_setminwidth();
} else if (pp == &p_ls) { } 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. last_status(false); // (re)set last window status line.
} else if (pp == &p_stal) { } else if (pp == &p_stal) {
// (re)set tab page line // (re)set tab page line
@ -5645,7 +5667,7 @@ static int put_setbool(FILE *fd, char *cmd, char *name, int value)
void comp_col(void) 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; sc_col = 0;
ru_col = 0; ru_col = 0;

View File

@ -3597,7 +3597,7 @@ static int qf_goto_cwindow(const qf_info_T *qi, bool resize, int sz, bool vertsp
win_setwidth(sz); win_setwidth(sz);
} }
} else if (sz != win->w_height } 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)) { < cmdline_row)) {
win_setheight(sz); win_setheight(sz);
} }

View File

@ -317,7 +317,8 @@ void update_curbuf(int type)
void redraw_buf_status_later(buf_T *buf) void redraw_buf_status_later(buf_T *buf)
{ {
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { 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; wp->w_redr_status = true;
if (must_redraw < VALID) { if (must_redraw < VALID) {
must_redraw = VALID; must_redraw = VALID;
@ -335,6 +336,7 @@ void redraw_buf_status_later(buf_T *buf)
int update_screen(int type) int update_screen(int type)
{ {
static bool did_intro = false; 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. // Don't do anything if the screen structures are (not yet) valid.
// A VimResized autocmd can invoke redrawing in the middle of a resize, // A VimResized autocmd can invoke redrawing in the middle of a resize,
@ -417,10 +419,13 @@ int update_screen(int type)
if (W_ENDROW(wp) > valid) { if (W_ENDROW(wp) > valid) {
wp->w_redr_type = MAX(wp->w_redr_type, NOT_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; 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_set_pos(Rows-p_ch, false);
msg_grid_invalid = false; msg_grid_invalid = false;
@ -442,13 +447,15 @@ int update_screen(int type)
wp->w_redr_type = REDRAW_TOP; wp->w_redr_type = REDRAW_TOP;
} else { } else {
wp->w_redr_type = NOT_VALID; wp->w_redr_type = NOT_VALID;
if (W_ENDROW(wp) + wp->w_status_height if (!is_stl_global && W_ENDROW(wp) + wp->w_status_height <= msg_scrolled) {
<= msg_scrolled) { wp->w_redr_status = true;
wp->w_redr_status = TRUE;
} }
} }
} }
} }
if (is_stl_global && Rows - p_ch - 1 <= msg_scrolled) {
curwin->w_redr_status = true;
}
redraw_cmdline = true; redraw_cmdline = true;
redraw_tabline = true; redraw_tabline = true;
} }
@ -803,8 +810,11 @@ static void win_update(win_T *wp, Providers *providers)
wp->w_lines_valid = 0; 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) { 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; wp->w_redr_type = 0;
return; return;
} }
@ -812,7 +822,8 @@ static void win_update(win_T *wp, Providers *providers)
// Window is zero-width: Only need to draw the separator. // Window is zero-width: Only need to draw the separator.
if (wp->w_grid.Columns == 0) { if (wp->w_grid.Columns == 0) {
// draw the vertical separator right of this window // 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; wp->w_redr_type = 0;
return; return;
} }
@ -1747,7 +1758,9 @@ static void win_update(win_T *wp, Providers *providers)
kvi_destroy(line_providers); kvi_destroy(line_providers);
if (wp->w_redr_type >= REDRAW_TOP) { 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); syn_set_timeout(NULL);
@ -4958,10 +4971,15 @@ void rl_mirror(char_u *str)
*/ */
void status_redraw_all(void) void status_redraw_all(void)
{ {
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (global_stl_height()) {
if (wp->w_status_height) { curwin->w_redr_status = true;
wp->w_redr_status = true; redraw_later(curwin, VALID);
redraw_later(wp, VALID); } else {
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_status_height) {
wp->w_redr_status = true;
redraw_later(wp, VALID);
}
} }
} }
} }
@ -4975,10 +4993,15 @@ void status_redraw_curbuf(void)
/// Marks all status lines of the specified buffer for redraw. /// Marks all status lines of the specified buffer for redraw.
void status_redraw_buf(buf_T *buf) void status_redraw_buf(buf_T *buf)
{ {
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (global_stl_height() != 0 && curwin->w_buffer == buf) {
if (wp->w_status_height != 0 && wp->w_buffer == buf) { curwin->w_redr_status = true;
wp->w_redr_status = true; redraw_later(curwin, VALID);
redraw_later(wp, VALID); } else {
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_status_height != 0 && wp->w_buffer == buf) {
wp->w_redr_status = true;
redraw_later(wp, VALID);
}
} }
} }
} }
@ -5020,10 +5043,8 @@ void win_redraw_last_status(const frame_T *frp)
} }
} }
/* /// Draw the vertical separator right of window "wp"
* Draw the verticap separator right of window "wp" starting with line "row". static void draw_vsep_win(win_T *wp)
*/
static void draw_vsep_win(win_T *wp, int row)
{ {
int hl; int hl;
int c; int c;
@ -5031,15 +5052,97 @@ static void draw_vsep_win(win_T *wp, int row)
if (wp->w_vsep_width) { if (wp->w_vsep_width) {
// draw the vertical separator right of this window // draw the vertical separator right of this window
c = fillchar_vsep(wp, &hl); 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); 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;
/* if (wp->w_hsep_height) {
* Get the length of an item as it will be shown in the status line. // 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) static int status_match_len(expand_T *xp, char_u *s)
{ {
int len = 0; int len = 0;
@ -5240,7 +5343,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. // Create status line if needed by setting 'laststatus' to 2.
// Set 'winminheight' to zero to avoid that the window is // Set 'winminheight' to zero to avoid that the window is
// resized. // resized.
if (lastwin->w_status_height == 0) { if (lastwin->w_status_height == 0 && global_stl_height() == 0) {
save_p_ls = p_ls; save_p_ls = p_ls;
save_p_wmh = p_wmh; save_p_wmh = p_wmh;
p_ls = 2; p_ls = 2;
@ -5276,12 +5379,15 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in
static void win_redr_status(win_T *wp) static void win_redr_status(win_T *wp)
{ {
int row; int row;
int col;
char_u *p; char_u *p;
int len; int len;
int fillchar; int fillchar;
int attr; int attr;
int width;
int this_ru_col; 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) // May get here recursively when 'statusline' (indirectly)
// invokes ":redrawstatus". Simply ignore the call then. // invokes ":redrawstatus". Simply ignore the call then.
@ -5292,9 +5398,9 @@ static void win_redr_status(win_T *wp)
} }
busy = true; busy = true;
wp->w_redr_status = FALSE; wp->w_redr_status = false;
if (wp->w_status_height == 0) { if (wp->w_status_height == 0 && !(is_stl_global && wp == curwin)) {
// no status line, can only be last window // no status line, either global statusline is enabled or the window is a last window
redraw_cmdline = true; redraw_cmdline = true;
} else if (!redrawing()) { } else if (!redrawing()) {
// Don't redraw right now, do it later. Don't update status line when // Don't redraw right now, do it later. Don't update status line when
@ -5305,6 +5411,7 @@ static void win_redr_status(win_T *wp)
redraw_custom_statusline(wp); redraw_custom_statusline(wp);
} else { } else {
fillchar = fillchar_status(&attr, wp); fillchar = fillchar_status(&attr, wp);
width = is_stl_global ? Columns : wp->w_width;
get_trans_bufname(wp->w_buffer); get_trans_bufname(wp->w_buffer);
p = NameBuff; p = NameBuff;
@ -5333,9 +5440,9 @@ static void win_redr_status(win_T *wp)
// len += (int)STRLEN(p + len); // dead assignment // len += (int)STRLEN(p + len); // dead assignment
} }
this_ru_col = ru_col - (Columns - wp->w_width); this_ru_col = ru_col - (Columns - width);
if (this_ru_col < (wp->w_width + 1) / 2) { if (this_ru_col < (width + 1) / 2) {
this_ru_col = (wp->w_width + 1) / 2; this_ru_col = (width + 1) / 2;
} }
if (this_ru_col <= 1) { if (this_ru_col <= 1) {
p = (char_u *)"<"; // No room for file name! p = (char_u *)"<"; // No room for file name!
@ -5360,10 +5467,11 @@ static void win_redr_status(win_T *wp)
} }
} }
row = W_ENDROW(wp); row = is_stl_global ? (Rows - p_ch - 1) : W_ENDROW(wp);
grid_puts(&default_grid, p, row, wp->w_wincol, attr); col = is_stl_global ? 0 : wp->w_wincol;
grid_fill(&default_grid, row, row + 1, len + wp->w_wincol, grid_puts(&default_grid, p, row, col, attr);
this_ru_col + wp->w_wincol, fillchar, fillchar, 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) if (get_keymap_str(wp, (char_u *)"<%s>", NameBuff, MAXPATHL)
&& this_ru_col - len > (int)(STRLEN(NameBuff) + 1)) { && this_ru_col - len > (int)(STRLEN(NameBuff) + 1)) {
@ -5442,6 +5550,76 @@ bool stl_connected(win_T *wp)
return false; 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'. /// Get the value to show for the language mappings, active 'keymap'.
/// ///
@ -5508,6 +5686,7 @@ static void win_redr_custom(win_T *wp, bool draw_ruler)
int use_sandbox = false; int use_sandbox = false;
win_T *ewp; win_T *ewp;
int p_crb_save; int p_crb_save;
bool is_stl_global = global_stl_height() > 0;
ScreenGrid *grid = &default_grid; ScreenGrid *grid = &default_grid;
@ -5529,9 +5708,9 @@ static void win_redr_custom(win_T *wp, bool draw_ruler)
maxwidth = Columns; maxwidth = Columns;
use_sandbox = was_set_insecurely(wp, "tabline", 0); use_sandbox = was_set_insecurely(wp, "tabline", 0);
} else { } else {
row = W_ENDROW(wp); row = is_stl_global ? (Rows - p_ch - 1) : W_ENDROW(wp);
fillchar = fillchar_status(&attr, wp); fillchar = fillchar_status(&attr, wp);
maxwidth = wp->w_width; maxwidth = is_stl_global ? Columns : wp->w_width;
if (draw_ruler) { if (draw_ruler) {
stl = p_ruf; stl = p_ruf;
@ -5549,12 +5728,12 @@ static void win_redr_custom(win_T *wp, bool draw_ruler)
stl = p_ruf; stl = p_ruf;
} }
} }
col = ru_col - (Columns - wp->w_width); col = ru_col - (Columns - maxwidth);
if (col < (wp->w_width + 1) / 2) { if (col < (maxwidth + 1) / 2) {
col = (wp->w_width + 1) / 2; col = (maxwidth + 1) / 2;
} }
maxwidth = wp->w_width - col; maxwidth = maxwidth - col;
if (!wp->w_status_height) { if (!wp->w_status_height && !is_stl_global) {
grid = &msg_grid_adj; grid = &msg_grid_adj;
row = Rows - 1; row = Rows - 1;
maxwidth--; // writing in last column may cause scrolling maxwidth--; // writing in last column may cause scrolling
@ -5572,7 +5751,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); 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) { if (maxwidth <= 0) {
@ -7154,10 +7333,10 @@ int showmode(void)
clear_showcmd(); clear_showcmd();
} }
// If the last window has no status line, the ruler is after the mode // If the last window has no status line and global statusline is disabled,
// message and must be redrawn // the ruler is after the mode message and must be redrawn
win_T *last = lastwin_nofloating(); 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); win_redr_ruler(last, true);
} }
redraw_cmdline = false; redraw_cmdline = false;
@ -7472,16 +7651,22 @@ int fillchar_status(int *attr, win_T *wp)
return '='; return '=';
} }
/* /// Get the character to use in a separator between vertically split windows.
* Get the character to use in a separator between vertically split windows. /// Get its attributes in "*attr".
* Get its attributes in "*attr".
*/
static int fillchar_vsep(win_T *wp, int *attr) static int fillchar_vsep(win_T *wp, int *attr)
{ {
*attr = win_hl_attr(wp, HLF_C); *attr = win_hl_attr(wp, HLF_C);
return wp->w_p_fcs_chars.vert; 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. * Return TRUE if redrawing should currently be done.
*/ */
@ -7507,7 +7692,8 @@ void showruler(bool always)
if (!always && !redrawing()) { if (!always && !redrawing()) {
return; 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); redraw_custom_statusline(curwin);
} else { } else {
win_redr_ruler(curwin, always); win_redr_ruler(curwin, always);
@ -7526,6 +7712,7 @@ void showruler(bool always)
static void win_redr_ruler(win_T *wp, 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; static bool did_show_ext_ruler = false;
// If 'ruler' off or redrawing disabled, don't do anything // If 'ruler' off or redrawing disabled, don't do anything
@ -7543,7 +7730,7 @@ static void win_redr_ruler(win_T *wp, bool always)
// Don't draw the ruler while doing insert-completion, it might overwrite // Don't draw the ruler while doing insert-completion, it might overwrite
// the (long) mode message. // 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) { if (edit_submode != NULL) {
return; return;
} }
@ -7598,6 +7785,12 @@ static void win_redr_ruler(win_T *wp, bool always)
off = wp->w_wincol; off = wp->w_wincol;
width = wp->w_width; width = wp->w_width;
part_of_status = true; 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 { } else {
row = Rows - 1; row = Rows - 1;
fillchar = ' '; fillchar = ' ';
@ -7637,7 +7830,7 @@ static void win_redr_ruler(win_T *wp, bool always)
int i = (int)STRLEN(buffer); int i = (int)STRLEN(buffer);
get_rel_pos(wp, buffer + i + 1, RULER_BUF_LEN - i - 1); get_rel_pos(wp, buffer + i + 1, RULER_BUF_LEN - i - 1);
int o = i + vim_strsize(buffer + 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++; o++;
} }
int this_ru_col = ru_col - (Columns - width); int this_ru_col = ru_col - (Columns - width);

View File

@ -21,6 +21,14 @@
#define NOT_VALID 40 // buffer needs complete redraw #define NOT_VALID 40 // buffer needs complete redraw
#define CLEAR 50 // screen messed up, clear it #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 /// 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 /// 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 /// grid, then this is only used for global screen elements that hasn't been

View File

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

View File

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

View File

@ -60,7 +60,7 @@
#define NOWIN (win_T *)-1 // non-existing window #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() /// flags for win_enter_ext()
typedef enum { typedef enum {
@ -647,6 +647,7 @@ win_T *win_new_float(win_T *wp, FloatConfig fconfig, Error *err)
} }
wp->w_floating = 1; wp->w_floating = 1;
wp->w_status_height = 0; wp->w_status_height = 0;
wp->w_hsep_height = 0;
wp->w_vsep_width = 0; wp->w_vsep_width = 0;
win_config_float(wp, fconfig); win_config_float(wp, fconfig);
@ -956,6 +957,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
int before; int before;
int minheight; int minheight;
int wmh1; int wmh1;
int hsep_height;
bool did_set_fraction = false; bool did_set_fraction = false;
if (flags & WSP_TOP) { if (flags & WSP_TOP) {
@ -1063,6 +1065,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
} }
} }
} else { } else {
hsep_height = global_stl_height() > 0 ? 1 : STATUS_HEIGHT;
layout = FR_COL; layout = FR_COL;
/* /*
@ -1071,7 +1074,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
*/ */
// Current window requires at least 1 space. // Current window requires at least 1 space.
wmh1 = p_wmh == 0 ? 1 : p_wmh; wmh1 = p_wmh == 0 ? 1 : p_wmh;
needed = wmh1 + STATUS_HEIGHT; needed = wmh1 + hsep_height;
if (flags & WSP_ROOM) { if (flags & WSP_ROOM) {
needed += p_wh - wmh1; needed += p_wh - wmh1;
} }
@ -1113,15 +1116,15 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
new_size = oldwin_height / 2; new_size = oldwin_height / 2;
} }
if (new_size > available - minheight - STATUS_HEIGHT) { if (new_size > available - minheight - hsep_height) {
new_size = available - minheight - STATUS_HEIGHT; new_size = available - minheight - hsep_height;
} }
if (new_size < wmh1) { if (new_size < wmh1) {
new_size = wmh1; new_size = wmh1;
} }
// if it doesn't fit in the current window, need win_equal() // 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; do_equal = true;
} }
@ -1134,7 +1137,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
set_fraction(oldwin); set_fraction(oldwin);
did_set_fraction = true; 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);
oldwin_height = oldwin->w_height; oldwin_height = oldwin->w_height;
if (need_status) { if (need_status) {
@ -1151,8 +1154,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
while (frp != NULL) { while (frp != NULL) {
if (frp->fr_win != oldwin && frp->fr_win != NULL if (frp->fr_win != oldwin && frp->fr_win != NULL
&& (frp->fr_win->w_height > new_size && (frp->fr_win->w_height > new_size
|| frp->fr_win->w_height > oldwin_height - new_size || frp->fr_win->w_height > oldwin_height - new_size - hsep_height)) {
- STATUS_HEIGHT)) {
do_equal = true; do_equal = true;
break; break;
} }
@ -1278,13 +1280,15 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
if (flags & (WSP_TOP | WSP_BOT)) { if (flags & (WSP_TOP | WSP_BOT)) {
// set height and row of new window to full height // set height and row of new window to full height
wp->w_winrow = tabline_height(); wp->w_winrow = tabline_height();
win_new_height(wp, curfrp->fr_height - (p_ls > 0)); win_new_height(wp, curfrp->fr_height - (p_ls == 1 || p_ls == 2));
wp->w_status_height = (p_ls > 0); wp->w_status_height = (p_ls == 1 || p_ls == 2);
wp->w_hsep_height = 0;
} else { } else {
// height and row of new window is same as current window // height and row of new window is same as current window
wp->w_winrow = oldwin->w_winrow; wp->w_winrow = oldwin->w_winrow;
win_new_height(wp, oldwin->w_height); win_new_height(wp, oldwin->w_height);
wp->w_status_height = oldwin->w_status_height; wp->w_status_height = oldwin->w_status_height;
wp->w_hsep_height = oldwin->w_hsep_height;
} }
frp->fr_height = curfrp->fr_height; frp->fr_height = curfrp->fr_height;
@ -1317,6 +1321,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
frame_fix_width(oldwin); frame_fix_width(oldwin);
frame_fix_width(wp); frame_fix_width(wp);
} else { } else {
bool is_stl_global = global_stl_height() > 0;
// width and column of new window is same as current window // width and column of new window is same as current window
if (flags & (WSP_TOP | WSP_BOT)) { if (flags & (WSP_TOP | WSP_BOT)) {
wp->w_wincol = 0; wp->w_wincol = 0;
@ -1332,28 +1337,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 // "new_size" of the current window goes to the new window, use
// one row for the status line // one row for the status line
win_new_height(wp, new_size); 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)) { if (flags & (WSP_TOP | WSP_BOT)) {
int new_fr_height = curfrp->fr_height - new_size; 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; 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); frame_new_height(curfrp, new_fr_height, flags & WSP_TOP, false);
} else { } 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 if (before) { // new window above current one
wp->w_winrow = oldwin->w_winrow; wp->w_winrow = oldwin->w_winrow;
wp->w_status_height = STATUS_HEIGHT;
oldwin->w_winrow += wp->w_height + STATUS_HEIGHT; 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 } else { // new window below current one
wp->w_winrow = oldwin->w_winrow + oldwin->w_height + STATUS_HEIGHT; if (is_stl_global) {
wp->w_status_height = oldwin->w_status_height; wp->w_winrow = oldwin->w_winrow + oldwin->w_height + 1;
if (!(flags & WSP_BOT)) { wp->w_status_height = 0;
oldwin->w_status_height = STATUS_HEIGHT; } 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_add_statusline(curfrp);
} }
frame_fix_height(wp); frame_fix_height(wp);
@ -1595,10 +1625,10 @@ int make_windows(int count, bool vertical)
maxcount = (curwin->w_width + curwin->w_vsep_width maxcount = (curwin->w_width + curwin->w_vsep_width
- (p_wiw - p_wmw)) / (p_wmw + 1); - (p_wiw - p_wmw)) / (p_wmw + 1);
} else { } else {
// Each window needs at least 'winminheight' lines and a status line. // Each window needs at least 'winminheight' lines
maxcount = (curwin->w_height // If statusline isn't global, each window also needs a statusline
+ curwin->w_status_height maxcount = (curwin->w_height + curwin->w_hsep_height + curwin->w_status_height
- (p_wh - p_wmh)) / (p_wmh + STATUS_HEIGHT); - (p_wh - p_wmh)) / (p_wmh + (global_stl_height() > 0 ? 1 : STATUS_HEIGHT));
} }
if (maxcount < 2) { if (maxcount < 2) {
@ -1693,7 +1723,7 @@ static void win_exchange(long Prenum)
* if wp != wp2 * if wp != wp2
* 3. remove wp from the list * 3. remove wp from the list
* 4. insert wp after wp2 * 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; wp2 = curwin->w_prev;
frp2 = curwin->w_frame->fr_prev; frp2 = curwin->w_frame->fr_prev;
@ -1719,6 +1749,9 @@ static void win_exchange(long Prenum)
temp = curwin->w_vsep_width; temp = curwin->w_vsep_width;
curwin->w_vsep_width = wp->w_vsep_width; curwin->w_vsep_width = wp->w_vsep_width;
wp->w_vsep_width = temp; 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(curwin);
frame_fix_height(wp); frame_fix_height(wp);
@ -1793,10 +1826,13 @@ static void win_rotate(bool upwards, int count)
frame_insert(frp->fr_parent->fr_child, frp); 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; n = wp2->w_status_height;
wp2->w_status_height = wp1->w_status_height; wp2->w_status_height = wp1->w_status_height;
wp1->w_status_height = n; 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(wp1);
frame_fix_height(wp2); frame_fix_height(wp2);
n = wp2->w_vsep_width; n = wp2->w_vsep_width;
@ -1870,11 +1906,16 @@ void win_move_after(win_T *win1, win_T *win2)
// check if there is something to do // check if there is something to do
if (win2->w_next != win1) { 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) { if (win1 == lastwin) {
height = win1->w_prev->w_status_height; height = win1->w_prev->w_status_height;
win1->w_prev->w_status_height = win1->w_status_height; win1->w_prev->w_status_height = win1->w_status_height;
win1->w_status_height = 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) { if (win1->w_prev->w_vsep_width == 1) {
// Remove the vertical separator from the last-but-one window, // Remove the vertical separator from the last-but-one window,
// add it to the last window. Adjust the frame widths. // add it to the last window. Adjust the frame widths.
@ -1887,6 +1928,11 @@ void win_move_after(win_T *win1, win_T *win2)
height = win1->w_status_height; height = win1->w_status_height;
win1->w_status_height = win2->w_status_height; win1->w_status_height = win2->w_status_height;
win2->w_status_height = 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) { if (win1->w_vsep_width == 1) {
// Remove the vertical separator from win1, add it to the last // Remove the vertical separator from win1, add it to the last
// window, win2. Adjust the frame widths. // window, win2. Adjust the frame widths.
@ -1950,6 +1996,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
int room = 0; int room = 0;
int new_size; int new_size;
int has_next_curwin = 0; int has_next_curwin = 0;
int hsep_height;
bool hnc; bool hnc;
if (topfr->fr_layout == FR_LEAF) { if (topfr->fr_layout == FR_LEAF) {
@ -2095,19 +2142,22 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
totwincount -= wincount; totwincount -= wincount;
} }
} else { // topfr->fr_layout == FR_COL } else { // topfr->fr_layout == FR_COL
hsep_height = global_stl_height() > 0 ? 1 : STATUS_HEIGHT;
topfr->fr_width = width; topfr->fr_width = width;
topfr->fr_height = height; topfr->fr_height = height;
if (dir != 'h') { // equalize frame heights if (dir != 'h') { // equalize frame heights
// Compute maximum number of windows vertically in this frame. // Compute maximum number of windows vertically in this frame.
n = frame_minheight(topfr, NOWIN); 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) { if (row + height == cmdline_row && p_ls == 0) {
extra_sep = STATUS_HEIGHT;
} else if (global_stl_height() > 0) {
extra_sep = 1; extra_sep = 1;
} else { } else {
extra_sep = 0; 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); has_next_curwin = frame_has_win(topfr, next_curwin);
/* /*
@ -2142,7 +2192,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
} else { } else {
// These windows don't use up room. // These windows don't use up room.
totwincount -= (n + (fr->fr_next == NULL totwincount -= (n + (fr->fr_next == NULL
? extra_sep : 0)) / (p_wmh + 1); ? extra_sep : 0)) / (p_wmh + hsep_height);
} }
room -= new_size - n; room -= new_size - n;
if (room < 0) { if (room < 0) {
@ -2188,7 +2238,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". // Compute the maximum number of windows vert. in "fr".
n = frame_minheight(fr, NOWIN); n = frame_minheight(fr, NOWIN);
wincount = (n + (fr->fr_next == NULL ? extra_sep : 0)) wincount = (n + (fr->fr_next == NULL ? extra_sep : 0))
/ (p_wmh + 1); / (p_wmh + hsep_height);
m = frame_minheight(fr, next_curwin); m = frame_minheight(fr, next_curwin);
if (has_next_curwin) { if (has_next_curwin) {
hnc = frame_has_win(fr, next_curwin); hnc = frame_has_win(fr, next_curwin);
@ -3134,7 +3184,7 @@ static tabpage_T *alt_tabpage(void)
/* /*
* Find the left-upper window in frame "frp". * 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) { while (frp->fr_win == NULL) {
frp = frp->fr_child; frp = frp->fr_child;
@ -3161,23 +3211,40 @@ static bool frame_has_win(const frame_T *frp, const win_T *wp)
return false; 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 /// Set a new height for a frame. Recursively sets the height for contained
/// frames and windows. Caller must take care of positions. /// frames and windows. Caller must take care of positions.
/// ///
/// @param topfirst resize topmost contained frame first. /// @param topfirst resize topmost contained frame first.
/// @param wfh obey 'winfixheight' when there is a choice; /// @param wfh obey 'winfixheight' when there is a choice;
/// may cause the height not to be set. /// 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 FUNC_ATTR_NONNULL_ALL
{ {
frame_T *frp; frame_T *frp;
int extra_lines; int extra_lines;
int h; int h;
win_T *wp;
if (topfrp->fr_win != NULL) { if (topfrp->fr_win != NULL) {
// Simple case: just one window. // Simple case: just one window.
win_new_height(topfrp->fr_win, wp = topfrp->fr_win;
height - topfrp->fr_win->w_status_height); 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) { } else if (topfrp->fr_layout == FR_ROW) {
do { do {
// All frames in this row get the same new height. // All frames in this row get the same new height.
@ -3333,8 +3400,8 @@ static void frame_add_statusline(frame_T *frp)
if (frp->fr_layout == FR_LEAF) { if (frp->fr_layout == FR_LEAF) {
wp = frp->fr_win; wp = frp->fr_win;
if (wp->w_status_height == 0) { if (wp->w_status_height == 0) {
if (wp->w_height > 0) { // don't make it negative if (wp->w_height - STATUS_HEIGHT >= 0) { // don't make it negative
--wp->w_height; wp->w_height -= STATUS_HEIGHT;
} }
wp->w_status_height = STATUS_HEIGHT; wp->w_status_height = STATUS_HEIGHT;
} }
@ -3454,10 +3521,8 @@ static void frame_new_width(frame_T *topfrp, int width, bool leftfirst, bool wfw
topfrp->fr_width = width; topfrp->fr_width = width;
} }
/* /// Add the vertical separator to windows at the right side of "frp".
* Add the vertical separator to windows at the right side of "frp". /// Note: Does not check if there is room!
* Note: Does not check if there is room!
*/
static void frame_add_vsep(const frame_T *frp) static void frame_add_vsep(const frame_T *frp)
FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_ARG(1)
{ {
@ -3487,6 +3552,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. * Set frame width from the window it contains.
*/ */
@ -3501,7 +3597,7 @@ static void frame_fix_width(win_T *wp)
static void frame_fix_height(win_T *wp) static void frame_fix_height(win_T *wp)
FUNC_ATTR_NONNULL_ALL 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;
} }
/* /*
@ -3519,10 +3615,11 @@ static int frame_minheight(frame_T *topfrp, win_T *next_curwin)
if (topfrp->fr_win != NULL) { if (topfrp->fr_win != NULL) {
if (topfrp->fr_win == next_curwin) { 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 { } else {
// window: minimal height of the window plus status line // window: minimal height of the window plus separator column or status line
m = p_wmh + topfrp->fr_win->w_status_height; // 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) { if (topfrp->fr_win == curwin && next_curwin == NULL) {
// Current window is minimal one line high. // Current window is minimal one line high.
if (p_wmh == 0) { if (p_wmh == 0) {
@ -3751,7 +3848,7 @@ static int win_alloc_firstwin(win_T *oldwin)
new_frame(curwin); new_frame(curwin);
topframe = curwin->w_frame; topframe = curwin->w_frame;
topframe->fr_width = Columns; topframe->fr_width = Columns;
topframe->fr_height = Rows - p_ch; topframe->fr_height = Rows - p_ch - global_stl_height();
return OK; return OK;
} }
@ -5147,11 +5244,8 @@ void win_size_restore(garray_T *gap)
} }
} }
/* // Update the position for all windows, using the width and height of the frames.
* Update the position for all windows, using the width and height of the // Returns the row just after the last window and global statusline (if there is one).
* frames.
* Returns the row just after the last window.
*/
int win_comp_pos(void) int win_comp_pos(void)
{ {
int row = tabline_height(); int row = tabline_height();
@ -5166,7 +5260,7 @@ int win_comp_pos(void)
} }
} }
return row; return row + global_stl_height();
} }
void win_reconfig_floats(void) void win_reconfig_floats(void)
@ -5200,7 +5294,7 @@ static void frame_comp_pos(frame_T *topfrp, int *row, int *col)
wp->w_redr_status = true; wp->w_redr_status = true;
wp->w_pos_changed = 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; *row += h > topfrp->fr_height ? topfrp->fr_height : h;
*col += wp->w_width + wp->w_vsep_width; *col += wp->w_width + wp->w_vsep_width;
} else { } else {
@ -5249,7 +5343,7 @@ void win_setheight_win(int height, win_T *win)
win_config_float(win, win->w_float_config); win_config_float(win, win->w_float_config);
redraw_later(win, NOT_VALID); redraw_later(win, NOT_VALID);
} else { } 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 // recompute the window positions
int row = win_comp_pos(); int row = win_comp_pos();
@ -5340,8 +5434,8 @@ static void frame_setheight(frame_T *curfrp, int height)
room_cmdline = 0; room_cmdline = 0;
} else { } else {
win_T *wp = lastwin_nofloating(); win_T *wp = lastwin_nofloating();
room_cmdline = Rows - p_ch room_cmdline = Rows - p_ch - global_stl_height()
- (wp->w_winrow + wp->w_height + wp->w_status_height); - (wp->w_winrow + wp->w_height + wp->w_hsep_height + wp->w_status_height);
if (room_cmdline < 0) { if (room_cmdline < 0) {
room_cmdline = 0; room_cmdline = 0;
} }
@ -5703,7 +5797,7 @@ void win_drag_status_line(win_T *dragwin, int offset)
} else { // drag down } else { // drag down
up = false; up = false;
// Only dragging the last status line can reduce p_ch. // 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) { if (curfr->fr_next == NULL) {
room -= 1; room -= 1;
} else { } else {
@ -6344,72 +6438,104 @@ 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); 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'. /// value of 'laststatus'.
/// ///
/// @param morewin pretend there are two or more windows if true. /// @param morewin pretend there are two or more windows if true.
void last_status(bool morewin) void last_status(bool morewin)
{ {
// Don't make a difference between horizontal or vertical split. // Don't make a difference between horizontal or vertical split.
last_status_rec(topframe, (p_ls == 2 last_status_rec(topframe, (p_ls == 2 || (p_ls == 1 && (morewin || !one_window()))),
|| (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)
{
// Find a frame to take a line from.
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));
return;
}
// In a column of frames: go to frame above. If already at
// the top or in a row of frames: go to parent.
if (fp->fr_parent->fr_layout == FR_COL && fp->fr_prev != NULL) {
fp = fp->fr_prev;
} else {
fp = fp->fr_parent;
}
}
n = MIN(fp->fr_height - frame_minheight(fp, NULL), resize_amount);
resize_amount -= n;
if (fp != fr) {
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 - n);
}
}
}
static void last_status_rec(frame_T *fr, bool statusline, bool is_stl_global)
{ {
frame_T *fp; frame_T *fp;
win_T *wp; win_T *wp;
if (fr->fr_layout == FR_LEAF) { if (fr->fr_layout == FR_LEAF) {
wp = fr->fr_win; wp = fr->fr_win;
if (wp->w_status_height != 0 && !statusline) { bool is_last = is_bottom_win(wp);
// remove status line
win_new_height(wp, wp->w_height + 1); 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_status_height = 0;
wp->w_hsep_height = 1;
comp_col(); comp_col();
} else if (wp->w_status_height == 0 && statusline) { } else if (wp->w_status_height == 0 && !is_stl_global) {
// Find a frame to take a line from. // If statusline isn't global and the window doesn't have a statusline, re-add it
fp = fr; wp->w_status_height = STATUS_HEIGHT;
while (fp->fr_height <= frame_minheight(fp, NULL)) { wp->w_hsep_height = 0;
if (fp == topframe) { resize_frame_for_status(fr, STATUS_HEIGHT - 1);
emsg(_(e_noroom));
return;
}
// In a column of frames: go to frame above. If already at
// the top or in a row of frames: go to parent.
if (fp->fr_parent->fr_layout == FR_COL && fp->fr_prev != NULL) {
fp = fp->fr_prev;
} else {
fp = fp->fr_parent;
}
}
wp->w_status_height = 1;
if (fp != fr) {
frame_new_height(fp, fp->fr_height - 1, false, false);
frame_fix_height(wp);
(void)win_comp_pos();
} else {
win_new_height(wp, wp->w_height - 1);
}
comp_col(); comp_col();
redraw_all_later(SOME_VALID);
} }
} else if (fr->fr_layout == FR_ROW) { redraw_all_later(SOME_VALID);
// vertically split windows, set status line for each one } 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) { FOR_ALL_FRAMES(fp, fr->fr_child) {
last_status_rec(fp, statusline); last_status_rec(fp, statusline, is_stl_global);
} }
} else { } else {
// horizontally split window, set status line for last one // For a row frame, recursively call this function for all child frames
for (fp = fr->fr_child; fp->fr_next != NULL; fp = fp->fr_next) { FOR_ALL_FRAMES(fp, fr->fr_child) {
last_status_rec(fp, statusline, is_stl_global);
} }
last_status_rec(fp, statusline);
} }
} }
/* /// Return the number of lines used by the tab page line.
* Return the number of lines used by the tab page line.
*/
int tabline_height(void) int tabline_height(void)
{ {
if (ui_has(kUITabline)) { if (ui_has(kUITabline)) {
@ -6425,10 +6551,14 @@ int tabline_height(void)
return 1; return 1;
} }
/* /// Return the number of lines used by the global statusline
* Return the minimal number of rows that is needed on the screen to display int global_stl_height(void)
* the current number of windows. {
*/ 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) int min_rows(void)
{ {
if (firstwin == NULL) { // not initialized yet if (firstwin == NULL) { // not initialized yet
@ -6442,7 +6572,7 @@ int min_rows(void)
total = n; total = n;
} }
} }
total += tabline_height(); total += tabline_height() + global_stl_height();
total += 1; // count the room for the command line total += 1; // count the room for the command line
return total; return total;
} }

View File

@ -212,10 +212,10 @@ describe('ui/cursor', function()
if m.blinkwait then m.blinkwait = 700 end if m.blinkwait then m.blinkwait = 700 end
end end
if m.hl_id then if m.hl_id then
m.hl_id = 60 m.hl_id = 61
m.attr = {background = Screen.colors.DarkGray} m.attr = {background = Screen.colors.DarkGray}
end end
if m.id_lm then m.id_lm = 61 end if m.id_lm then m.id_lm = 62 end
end end
-- Assert the new expectation. -- 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() it('work with cleared UI highlights', function()
screen:set_default_attr_ids({ 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}, [2] = {{bold = true, foreground = Screen.colors.Blue1},
{{hi_name = "NonText", ui_name = "EndOfBuffer", kind = "ui"}}}, {{hi_name = "NonText", ui_name = "EndOfBuffer", kind = "ui"}}},
[3] = {{bold = true, reverse = true}, [3] = {{bold = true, reverse = true},

View File

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