diff options
author | Famiu Haque <famiuhaque@protonmail.com> | 2022-01-30 11:57:41 +0600 |
---|---|---|
committer | Famiu Haque <famiuhaque@protonmail.com> | 2022-03-18 00:21:41 +0600 |
commit | 5ab122917474b3f9e88be4ee88bc6d627980cfe0 (patch) | |
tree | 4e7a3cbfcc0614cbb176d0166dd16948fe5c0ee5 | |
parent | 046950f6309070d98ab5a3deeff9d00e079ccdd4 (diff) | |
download | rneovim-5ab122917474b3f9e88be4ee88bc6d627980cfe0.tar.gz rneovim-5ab122917474b3f9e88be4ee88bc6d627980cfe0.tar.bz2 rneovim-5ab122917474b3f9e88be4ee88bc6d627980cfe0.zip |
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.
-rw-r--r-- | runtime/doc/arabic.txt | 2 | ||||
-rw-r--r-- | runtime/doc/options.txt | 28 | ||||
-rw-r--r-- | runtime/doc/syntax.txt | 4 | ||||
-rw-r--r-- | runtime/doc/usr_08.txt | 2 | ||||
-rw-r--r-- | runtime/doc/vim_diff.txt | 6 | ||||
-rw-r--r-- | runtime/doc/windows.txt | 2 | ||||
-rw-r--r-- | src/nvim/api/vim.c | 2 | ||||
-rw-r--r-- | src/nvim/api/win_config.c | 2 | ||||
-rw-r--r-- | src/nvim/buffer.c | 4 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 9 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 2 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 4 | ||||
-rw-r--r-- | src/nvim/ex_session.c | 2 | ||||
-rw-r--r-- | src/nvim/highlight_defs.h | 6 | ||||
-rw-r--r-- | src/nvim/option.c | 64 | ||||
-rw-r--r-- | src/nvim/quickfix.c | 2 | ||||
-rw-r--r-- | src/nvim/screen.c | 301 | ||||
-rw-r--r-- | src/nvim/screen.h | 8 | ||||
-rw-r--r-- | src/nvim/syntax.c | 3 | ||||
-rw-r--r-- | src/nvim/testdir/test_highlight.vim | 4 | ||||
-rw-r--r-- | src/nvim/window.c | 334 | ||||
-rw-r--r-- | test/functional/ui/cursor_spec.lua | 4 | ||||
-rw-r--r-- | test/functional/ui/global_statusline_spec.lua | 233 | ||||
-rw-r--r-- | test/functional/ui/hlstate_spec.lua | 2 | ||||
-rw-r--r-- | test/unit/viml/expressions/parser_spec.lua | 1 |
25 files changed, 828 insertions, 203 deletions
diff --git a/runtime/doc/arabic.txt b/runtime/doc/arabic.txt index 5d3bf7a761..0df861111c 100644 --- a/runtime/doc/arabic.txt +++ b/runtime/doc/arabic.txt @@ -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 diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 6e2bc228d0..66048c2e5f 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -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 diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index 319a715e40..5446bd3a88 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -5105,8 +5105,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* diff --git a/runtime/doc/usr_08.txt b/runtime/doc/usr_08.txt index 8ccaa73006..1d20913a14 100644 --- a/runtime/doc/usr_08.txt +++ b/runtime/doc/usr_08.txt @@ -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: diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 7892b82137..69b0e0081a 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -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 diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt index 5b91321c40..f8a8a557ef 100644 --- a/runtime/doc/windows.txt +++ b/runtime/doc/windows.txt @@ -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 diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index f4909b0801..2e3d99cdad 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -2319,7 +2319,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]; diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index ceb7f71423..d8ccd67bcd 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -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 diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 38b045b31c..493c011ad6 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -5006,8 +5006,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 diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 7ae5df164f..d0f49ec346 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -1227,7 +1227,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 @@ -1273,7 +1279,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 diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index c6baa105b0..9a74e4e351 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -3886,7 +3886,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]) { diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index ed4475eb1a..fccb57c0d2 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -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; } diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c index ca07174543..3b985bc329 100644 --- a/src/nvim/ex_session.c +++ b/src/nvim/ex_session.c @@ -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", diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h index 50a03e0c02..9f6f800fb4 100644 --- a/src/nvim/highlight_defs.h +++ b/src/nvim/highlight_defs.h @@ -70,7 +70,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 @@ -129,10 +130,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", diff --git a/src/nvim/option.c b/src/nvim/option.c index c8e50d4494..3eb854aded 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -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); } @@ -3553,16 +3553,22 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set) struct chars_tab *tab; struct chars_tab fcs_tab[] = { - { &wp->w_p_fcs_chars.stl, "stl", ' ' }, - { &wp->w_p_fcs_chars.stlnc, "stlnc", ' ' }, - { &wp->w_p_fcs_chars.vert, "vert", 9474 }, // โ - { &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", '~' }, + { &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", '+' }, + { &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[] = { { &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; } 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 = '|'; - fcs_tab[3].def = '-'; - } else { - fcs_tab[2].def = 9474; // โ - fcs_tab[6].def = 9474; // โ - fcs_tab[3].def = 183; // ยท + // 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 = '-'; + 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 = '|'; } } @@ -4474,6 +4482,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 @@ -5645,7 +5667,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; diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 7e29aed51b..124500d0fc 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -3597,7 +3597,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); } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 15fb6901cc..c87763c7a4 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -317,7 +317,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; @@ -335,6 +336,7 @@ void redraw_buf_status_later(buf_T *buf) 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, @@ -417,10 +419,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; @@ -442,13 +447,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; } @@ -803,8 +810,11 @@ static void win_update(win_T *wp, Providers *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; } @@ -812,7 +822,8 @@ static void win_update(win_T *wp, Providers *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; } @@ -1747,7 +1758,9 @@ static void win_update(win_T *wp, Providers *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); @@ -4958,10 +4971,15 @@ void rl_mirror(char_u *str) */ void status_redraw_all(void) { - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_status_height) { - wp->w_redr_status = true; - redraw_later(wp, VALID); + 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; + redraw_later(wp, VALID); + } } } } @@ -4975,10 +4993,15 @@ void status_redraw_curbuf(void) /// Marks all status lines of the specified buffer for redraw. void status_redraw_buf(buf_T *buf) { - 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); + 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; + redraw_later(wp, VALID); + } } } } @@ -5020,10 +5043,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; @@ -5031,15 +5052,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; + + 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 length of an item as it will be shown in the status line. - */ +/// 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; @@ -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. // 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; @@ -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) { 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. @@ -5292,9 +5398,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 @@ -5305,6 +5411,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; @@ -5333,9 +5440,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! @@ -5360,10 +5467,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)) { @@ -5442,6 +5550,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'. /// @@ -5508,6 +5686,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; @@ -5529,9 +5708,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; @@ -5549,12 +5728,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 @@ -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); } - col += wp->w_wincol; + col += is_stl_global ? 0 : wp->w_wincol; } if (maxwidth <= 0) { @@ -7154,10 +7333,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; @@ -7472,16 +7651,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. */ @@ -7507,7 +7692,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); @@ -7526,6 +7712,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 @@ -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 // 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; } @@ -7598,6 +7785,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 = ' '; @@ -7637,7 +7830,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); diff --git a/src/nvim/screen.h b/src/nvim/screen.h index d704a6eb8a..9e149594e1 100644 --- a/src/nvim/screen.h +++ b/src/nvim/screen.h @@ -21,6 +21,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 diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 119f6e811f..ac764e2d99 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -6173,6 +6173,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", @@ -6183,7 +6184,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", diff --git a/src/nvim/testdir/test_highlight.vim b/src/nvim/testdir/test_highlight.vim index 6971ecd357..aa7b3a225b 100644 --- a/src/nvim/testdir/test_highlight.vim +++ b/src/nvim/testdir/test_highlight.vim @@ -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 diff --git a/src/nvim/window.c b/src/nvim/window.c index e09af7a7bb..8cac9f23a1 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -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 { @@ -647,6 +647,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); @@ -956,6 +957,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; if (flags & WSP_TOP) { @@ -1063,6 +1065,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; /* @@ -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. wmh1 = p_wmh == 0 ? 1 : p_wmh; - needed = wmh1 + STATUS_HEIGHT; + needed = wmh1 + hsep_height; if (flags & WSP_ROOM) { 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; } - 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; } @@ -1134,7 +1137,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) { @@ -1151,8 +1154,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; } @@ -1278,13 +1280,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; @@ -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(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; @@ -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 // 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; - 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 - 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 (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); @@ -1595,10 +1625,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) { @@ -1693,7 +1723,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; @@ -1719,6 +1749,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); @@ -1793,10 +1826,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; @@ -1870,11 +1906,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. @@ -1887,6 +1928,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. @@ -1950,6 +1996,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) { @@ -2095,19 +2142,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); /* @@ -2142,7 +2192,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) { @@ -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". 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); @@ -3134,7 +3184,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; @@ -3161,23 +3211,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. @@ -3333,8 +3400,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; } @@ -3454,10 +3521,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) { @@ -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. */ @@ -3501,7 +3597,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; } /* @@ -3519,10 +3615,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) { @@ -3751,7 +3848,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; } @@ -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. - * 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(); @@ -5166,7 +5260,7 @@ int win_comp_pos(void) } } - return row; + return row + global_stl_height(); } 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_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 { @@ -5249,7 +5343,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(); @@ -5340,8 +5434,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; } @@ -5703,7 +5797,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 { @@ -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); } -/// 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) +{ + // 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; 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); + 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 && statusline) { - // Find a frame to take a line from. - fp = fr; - 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; - } - } - 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); - } + } 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_ROW) { - // vertically split windows, set status line for each one + 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); + last_status_rec(fp, statusline, is_stl_global); } } else { - // horizontally split window, set status line for last one - for (fp = fr->fr_child; fp->fr_next != NULL; fp = fp->fr_next) { + // 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); } - 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) { if (ui_has(kUITabline)) { @@ -6425,10 +6551,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 @@ -6442,7 +6572,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; } diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua index 03cd4bfd06..4c51547e2c 100644 --- a/test/functional/ui/cursor_spec.lua +++ b/test/functional/ui/cursor_spec.lua @@ -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. diff --git a/test/functional/ui/global_statusline_spec.lua b/test/functional/ui/global_statusline_spec.lua new file mode 100644 index 0000000000..6b37e5e2f1 --- /dev/null +++ b/test/functional/ui/global_statusline_spec.lua @@ -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) diff --git a/test/functional/ui/hlstate_spec.lua b/test/functional/ui/hlstate_spec.lua index 2a567b28ee..2d36735353 100644 --- a/test/functional/ui/hlstate_spec.lua +++ b/test/functional/ui/hlstate_spec.lua @@ -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}, diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index 8342044b5e..51a703b593 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -48,6 +48,7 @@ local predefined_hl_defs = { TermCursor=true, VertSplit=true, WildMenu=true, + WinSeparator=true, EndOfBuffer=true, QuickFixLine=true, Substitute=true, |