aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFamiu Haque <famiuhaque@protonmail.com>2022-05-13 20:47:11 +0600
committerFamiu Haque <famiuhaque@protonmail.com>2022-05-18 09:27:08 +0600
commite1bdb2a258cbe6c5cb981acc6bac82cd9e7706fb (patch)
treed892e10e8e922e71ce822e7c6a5c41fad1dd73d1
parentb9b5577d6d8e07d1e39020c8fc05f817f2e81c66 (diff)
downloadrneovim-e1bdb2a258cbe6c5cb981acc6bac82cd9e7706fb.tar.gz
rneovim-e1bdb2a258cbe6c5cb981acc6bac82cd9e7706fb.tar.bz2
rneovim-e1bdb2a258cbe6c5cb981acc6bac82cd9e7706fb.zip
feat(ui): add `'winbar'`
Adds support for a bar at the top of each window, enabled through the `'winbar'` option. Co-authored-by: Björn Linse <bjorn.linse@gmail.com>
-rw-r--r--runtime/doc/api.txt3
-rw-r--r--runtime/doc/index.txt5
-rw-r--r--runtime/doc/options.txt15
-rw-r--r--runtime/doc/syntax.txt4
-rw-r--r--runtime/doc/various.txt8
-rw-r--r--src/nvim/api/keysets.lua1
-rw-r--r--src/nvim/api/private/helpers.c8
-rw-r--r--src/nvim/api/vim.c35
-rw-r--r--src/nvim/buffer_defs.h13
-rw-r--r--src/nvim/eval.c2
-rw-r--r--src/nvim/eval/funcs.c4
-rw-r--r--src/nvim/ex_docmd.c5
-rw-r--r--src/nvim/highlight_defs.h6
-rw-r--r--src/nvim/highlight_group.c2
-rw-r--r--src/nvim/mouse.c18
-rw-r--r--src/nvim/move.c4
-rw-r--r--src/nvim/normal.c8
-rw-r--r--src/nvim/option.c36
-rw-r--r--src/nvim/option_defs.h2
-rw-r--r--src/nvim/options.lua10
-rw-r--r--src/nvim/screen.c170
-rw-r--r--src/nvim/testdir/test_cursor_func.vim5
-rw-r--r--src/nvim/window.c146
-rw-r--r--test/functional/api/vim_spec.lua13
-rw-r--r--test/functional/ui/cursor_spec.lua4
-rw-r--r--test/functional/ui/winbar_spec.lua180
26 files changed, 538 insertions, 169 deletions
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index 812208ca31..820eaad99a 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -844,8 +844,11 @@ nvim_eval_statusline({str}, {*opts}) *nvim_eval_statusline()*
Treated as single-width even if it isn't.
• highlights: (boolean) Return highlight
information.
+ • use_winbar: (boolean) Evaluate winbar instead of
+ statusline.
• use_tabline: (boolean) Evaluate tabline instead
of statusline. When |TRUE|, {winid} is ignored.
+ Mutually exclusive with {use_winbar}.
Return: ~
Dictionary containing statusline information, with these
diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt
index 5c36eaf8e5..58d49b5843 100644
--- a/runtime/doc/index.txt
+++ b/runtime/doc/index.txt
@@ -1501,8 +1501,9 @@ tag command action ~
|:recover| :rec[over] recover a file from a swap file
|:redo| :red[o] redo one undone change
|:redir| :redi[r] redirect messages to a file or register
-|:redraw| :redr[aw] force a redraw of the display
-|:redrawstatus| :redraws[tatus] force a redraw of the status line(s)
+|:redraw| :redr[aw] force a redraw of the display
+|:redrawstatus| :redraws[tatus] force a redraw of the status line(s) and
+ window bar(s)
|:redrawtabline| :redrawt[abline] force a redraw of the tabline
|:registers| :reg[isters] display the contents of registers
|:resize| :res[ize] change current window height
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 4d6f589714..4022bc4334 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -2452,6 +2452,7 @@ 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
+ wbr:c ' ' window bar
horiz:c '─' or '-' horizontal separators |:split|
horizup:c '┴' or '-' upwards facing horizontal separator
horizdown:c '┬' or '-' downwards facing horizontal separator
@@ -2492,6 +2493,7 @@ 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|
+ wbr:c WinBar |hl-WinBar| or |hl-WinBarNC|
horiz:c WinSeparator |hl-WinSeparator|
horizup:c WinSeparator |hl-WinSeparator|
horizdown:c WinSeparator |hl-WinSeparator|
@@ -7045,6 +7047,19 @@ A jump table for the options with a short description can be found at |Q_op|.
key is never used for the menu.
This option is not used for <F10>; on Win32.
+ *'winbar'* *'wbr'*
+'winbar' 'wbr' string (default empty)
+ global or local to window |global-local|
+ When non-empty, this option enables the window bar and determines its
+ contents. The window bar is a bar that's shown at the top of every
+ window with it enabled. The value of 'winbar' is evaluated like with
+ 'statusline'.
+
+ When changing something that is used in 'winbar' that does not trigger
+ it to be updated, use |:redrawstatus|.
+
+ This option cannot be set in a modeline when 'modelineexpr' is off.
+
*'winblend'* *'winbl'*
'winblend' 'winbl' number (default 0)
local to window
diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt
index 0aceb30ce0..c3ad704bb7 100644
--- a/runtime/doc/syntax.txt
+++ b/runtime/doc/syntax.txt
@@ -5244,6 +5244,10 @@ Whitespace "nbsp", "space", "tab", "multispace", "lead" and "trail"
in 'listchars'.
*hl-WildMenu*
WildMenu Current match in 'wildmenu' completion.
+ *hl-WinBar*
+WinBar Window bar of current window.
+ *hl-WinBarNC*
+WinBarNC Window bar of not-current windows.
*hl-User1* *hl-User1..9* *hl-User9*
The 'statusline' syntax allows the use of 9 different highlights in the
diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt
index 75ee0fdfdf..0a25904104 100644
--- a/runtime/doc/various.txt
+++ b/runtime/doc/various.txt
@@ -30,10 +30,10 @@ CTRL-L Clears and redraws the screen. The redraw may happen
function (or a mapping if 'lazyredraw' set).
*:redraws* *:redrawstatus*
-:redraws[tatus][!] Redraws the status line of the current window, or all
- status lines if "!" is included.
- Useful if 'statusline' includes an item that doesn't
- cause automatic updating.
+:redraws[tatus][!] Redraws the status line and window bar of the current
+ window, or all status lines and window bars if "!" is
+ included. Useful if 'statusline' or 'winbar' includes
+ an item that doesn't cause automatic updating.
*:redrawt* *:redrawtabline*
:redrawt[abline] Redraw the tabline. Useful to update the tabline when
diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua
index 78f8ed3cca..27228204e0 100644
--- a/src/nvim/api/keysets.lua
+++ b/src/nvim/api/keysets.lua
@@ -80,6 +80,7 @@ return {
"maxwidth";
"fillchar";
"highlights";
+ "use_winbar";
"use_tabline";
};
option = {
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index dcede27bcb..7bd68f277b 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -1418,14 +1418,14 @@ bool set_mark(buf_T *buf, String name, Integer line, Integer col, Error *err)
}
/// Get default statusline highlight for window
-const char *get_default_stl_hl(win_T *wp)
+const char *get_default_stl_hl(win_T *wp, bool use_winbar)
{
if (wp == NULL) {
return "TabLineFill";
- } else if (wp == curwin) {
- return "StatusLine";
+ } else if (use_winbar) {
+ return (wp == curwin) ? "WinBar" : "WinBarNC";
} else {
- return "StatusLineNC";
+ return (wp == curwin) ? "StatusLine" : "StatusLineNC";
}
}
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index a257dd6478..3c378e73e1 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -2274,8 +2274,9 @@ Array nvim_get_mark(String name, Dictionary opts, Error *err)
/// - fillchar: (string) Character to fill blank spaces in the statusline (see
/// 'fillchars'). Treated as single-width even if it isn't.
/// - highlights: (boolean) Return highlight information.
+/// - use_winbar: (boolean) Evaluate winbar instead of statusline.
/// - use_tabline: (boolean) Evaluate tabline instead of statusline. When |TRUE|, {winid}
-/// is ignored.
+/// is ignored. Mutually exclusive with {use_winbar}.
///
/// @param[out] err Error details, if any.
/// @return Dictionary containing statusline information, with these keys:
@@ -2294,6 +2295,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
int maxwidth;
int fillchar = 0;
Window window = 0;
+ bool use_winbar = false;
bool use_tabline = false;
bool highlights = false;
@@ -2313,7 +2315,6 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
window = (Window)opts->winid.data.integer;
}
-
if (HAS_KEY(opts->fillchar)) {
if (opts->fillchar.type != kObjectTypeString || opts->fillchar.data.string.size == 0
|| ((size_t)utf_ptr2len(opts->fillchar.data.string.data)
@@ -2323,7 +2324,6 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
}
fillchar = utf_ptr2char(opts->fillchar.data.string.data);
}
-
if (HAS_KEY(opts->highlights)) {
highlights = api_object_to_bool(opts->highlights, "highlights", false, err);
@@ -2331,7 +2331,13 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
return result;
}
}
+ if (HAS_KEY(opts->use_winbar)) {
+ use_winbar = api_object_to_bool(opts->use_winbar, "use_winbar", false, err);
+ if (ERROR_SET(err)) {
+ return result;
+ }
+ }
if (HAS_KEY(opts->use_tabline)) {
use_tabline = api_object_to_bool(opts->use_tabline, "use_tabline", false, err);
@@ -2339,6 +2345,10 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
return result;
}
}
+ if (use_winbar && use_tabline) {
+ api_set_error(err, kErrorTypeValidation, "use_winbar and use_tabline are mutually exclusive");
+ return result;
+ }
win_T *wp, *ewp;
@@ -2348,7 +2358,6 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
fillchar = ' ';
} else {
wp = find_window_by_handle(window, err);
-
if (wp == NULL) {
api_set_error(err, kErrorTypeException, "unknown winid %d", window);
return result;
@@ -2356,8 +2365,12 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
ewp = wp;
if (fillchar == 0) {
- int attr;
- fillchar = fillchar_status(&attr, wp);
+ if (use_winbar) {
+ fillchar = wp->w_p_fcs_chars.wbr;
+ } else {
+ int attr;
+ fillchar = fillchar_status(&attr, wp);
+ }
}
}
@@ -2369,7 +2382,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
maxwidth = (int)opts->maxwidth.data.integer;
} else {
- maxwidth = (use_tabline || global_stl_height() > 0) ? Columns : wp->w_width;
+ maxwidth = (use_tabline || (!use_winbar && global_stl_height() > 0)) ? Columns : wp->w_width;
}
char buf[MAXPATHL];
@@ -2404,7 +2417,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
// add the default highlight at the beginning of the highlight list
if (hltab->start == NULL || ((char *)hltab->start - buf) != 0) {
Dictionary hl_info = ARRAY_DICT_INIT;
- grpname = get_default_stl_hl(wp);
+ grpname = get_default_stl_hl(wp, use_winbar);
PUT(hl_info, "start", INTEGER_OBJ(0));
PUT(hl_info, "group", CSTR_TO_OBJ(grpname));
@@ -2418,22 +2431,18 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
PUT(hl_info, "start", INTEGER_OBJ((char *)sp->start - buf));
if (sp->userhl == 0) {
- grpname = get_default_stl_hl(wp);
+ grpname = get_default_stl_hl(wp, use_winbar);
} else if (sp->userhl < 0) {
grpname = (char *)syn_id2name(-sp->userhl);
} else {
snprintf(user_group, sizeof(user_group), "User%d", sp->userhl);
grpname = user_group;
}
-
PUT(hl_info, "group", CSTR_TO_OBJ(grpname));
-
ADD(hl_values, DICTIONARY_OBJ(hl_info));
}
-
PUT(result, "highlights", ARRAY_OBJ(hl_values));
}
-
PUT(result, "str", CSTR_TO_OBJ((char *)buf));
return result;
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 2adeeadbca..9efa540210 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -233,6 +233,8 @@ typedef struct {
#define w_p_sbr w_onebuf_opt.wo_sbr // 'showbreak'
char_u *wo_stl;
#define w_p_stl w_onebuf_opt.wo_stl // 'statusline'
+ char *wo_wbr;
+#define w_p_wbr w_onebuf_opt.wo_wbr // 'winbar'
int wo_scb;
#define w_p_scb w_onebuf_opt.wo_scb // 'scrollbind'
int wo_diff_saved; // options were saved for starting diff mode
@@ -1235,6 +1237,7 @@ struct window_S {
struct {
int stl;
int stlnc;
+ int wbr;
int horiz;
int horizup;
int horizdown;
@@ -1282,14 +1285,20 @@ struct window_S {
//
int w_winrow; // first row of window in screen
int w_height; // number of rows in window, excluding
- // status/command/winbar line(s)
+ // status/command line(s)
int w_status_height; // number of status lines (0 or 1)
+ int w_winbar_height; // number of window bars (0 or 1)
int w_wincol; // Leftmost column of window in screen.
int w_width; // Width of window, excluding separation.
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
+ int w_winrow_off; ///< offset from winrow to the inner window area
+ int w_wincol_off; ///< offset from wincol to the inner window area
+ ///< this includes float border but excludes special columns
+ ///< implemented in win_line() (i.e. signs, folds, numbers)
+
// inner size of window, which can be overridden by external UI
int w_height_inner;
int w_width_inner;
@@ -1379,6 +1388,7 @@ struct window_S {
linenr_T w_redraw_top; // when != 0: first line needing redraw
linenr_T w_redraw_bot; // when != 0: last line needing redraw
bool w_redr_status; // if true status line must be redrawn
+ bool w_redr_winbar; // if true window bar must be redrawn
bool w_redr_border; // if true border must be redrawn
// remember what is shown in the ruler for this window (if 'ruler' set)
@@ -1408,6 +1418,7 @@ struct window_S {
// A few options have local flags for P_INSECURE.
uint32_t w_p_stl_flags; // flags for 'statusline'
+ uint32_t w_p_wbr_flags; // flags for 'winbar'
uint32_t w_p_fde_flags; // flags for 'foldexpr'
uint32_t w_p_fdt_flags; // flags for 'foldtext'
int *w_p_cc_cols; // array of columns to highlight or NULL
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 9c2069663d..f9c6b6fbb2 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -6405,7 +6405,7 @@ dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr)
tv_dict_add_nr(dict, S_LEN("winrow"), wp->w_winrow + 1);
tv_dict_add_nr(dict, S_LEN("topline"), wp->w_topline);
tv_dict_add_nr(dict, S_LEN("botline"), wp->w_botline - 1);
- tv_dict_add_nr(dict, S_LEN("winbar"), 0);
+ tv_dict_add_nr(dict, S_LEN("winbar"), wp->w_winbar_height);
tv_dict_add_nr(dict, S_LEN("width"), wp->w_width);
tv_dict_add_nr(dict, S_LEN("bufnr"), wp->w_buffer->b_fnum);
tv_dict_add_nr(dict, S_LEN("wincol"), wp->w_wincol + 1);
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 68c2e37f22..ad133b5b5f 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -3611,8 +3611,8 @@ static void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// necessary for a top border since `row` starts at -1 in that case.
if (row < height + wp->w_border_adj[2]) {
winid = wp->handle;
- winrow = row + 1 + wp->w_border_adj[0]; // Adjust by 1 for top border
- wincol = col + 1 + wp->w_border_adj[3]; // Adjust by 1 for left border
+ winrow = row + 1 + wp->w_winrow_off; // Adjust by 1 for top border
+ wincol = col + 1 + wp->w_wincol_off; // Adjust by 1 for left border
if (row >= 0 && row < wp->w_height && col >= 0 && col < wp->w_width) {
(void)mouse_comp_pos(wp, &row, &col, &lnum);
col = vcol2col(wp, lnum, col);
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index e845073c12..0510faa57d 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -8831,7 +8831,7 @@ static void ex_redraw(exarg_T *eap)
ui_flush();
}
-/// ":redrawstatus": force redraw of status line(s)
+/// ":redrawstatus": force redraw of status line(s) and window bar(s)
static void ex_redrawstatus(exarg_T *eap)
{
if (State & MODE_CMDPREVIEW) {
@@ -8847,8 +8847,7 @@ static void ex_redrawstatus(exarg_T *eap)
} else {
status_redraw_curbuf();
}
- update_screen(VIsual_active ? INVERTED :
- 0);
+ update_screen(VIsual_active ? INVERTED : 0);
RedrawingDisabled = r;
p_lz = p;
ui_flush();
diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h
index 0515842b61..d293865efd 100644
--- a/src/nvim/highlight_defs.h
+++ b/src/nvim/highlight_defs.h
@@ -111,7 +111,9 @@ typedef enum {
HLF_NFLOAT, // Floating window
HLF_MSG, // Message area
HLF_BORDER, // Floating window border
- HLF_COUNT, // MUST be the last one
+ HLF_WBR, // Window bars
+ HLF_WBRNC, // Window bars of not-current windows
+ HLF_COUNT, // MUST be the last one
} hlf_T;
EXTERN const char *hlf_names[] INIT(= {
@@ -172,6 +174,8 @@ EXTERN const char *hlf_names[] INIT(= {
[HLF_NFLOAT] = "NormalFloat",
[HLF_MSG] = "MsgArea",
[HLF_BORDER] = "FloatBorder",
+ [HLF_WBR] = "WinBar",
+ [HLF_WBRNC] = "WinBarNC",
});
diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c
index 240a96cb4b..0047cb02d6 100644
--- a/src/nvim/highlight_group.c
+++ b/src/nvim/highlight_group.c
@@ -103,9 +103,11 @@ static const char *highlight_init_both[] = {
"TabLineFill cterm=reverse gui=reverse",
"TabLineSel cterm=bold gui=bold",
"TermCursor cterm=reverse gui=reverse",
+ "WinBar cterm=bold gui=bold",
"WildMenu ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
"default link VertSplit Normal",
"default link WinSeparator VertSplit",
+ "default link WinBarNC WinBar",
"default link EndOfBuffer NonText",
"default link LineNrAbove LineNr",
"default link LineNrBelow LineNr",
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index 8038ba2cad..3a1cc6b557 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -68,12 +68,12 @@ bool is_mouse_key(int c)
/// mouse was previously on a status line, then the status line may be dragged.
///
/// If flags has MOUSE_MAY_VIS, then VIsual mode will be started before the
-/// cursor is moved unless the cursor was on a status line.
+/// cursor is moved unless the cursor was on a status line or window bar.
/// This function returns one of IN_UNKNOWN, IN_BUFFER, IN_STATUS_LINE or
/// IN_SEP_LINE depending on where the cursor was clicked.
///
/// If flags has MOUSE_MAY_STOP_VIS, then Visual mode will be stopped, unless
-/// the mouse is on the status line of the same window.
+/// the mouse is on the status line or window bar of the same window.
///
/// If flags has MOUSE_DID_MOVE, nothing is done if the mouse didn't move since
/// the last call.
@@ -87,6 +87,7 @@ int jump_to_mouse(int flags, bool *inclusive, int which_button)
{
static int on_status_line = 0; // #lines below bottom of window
static int on_sep_line = 0; // on separator right of window
+ static bool on_winbar = false;
static int prev_row = -1;
static int prev_col = -1;
static win_T *dragwin = NULL; // window being dragged
@@ -126,6 +127,9 @@ retnomove:
if (on_sep_line) {
return IN_SEP_LINE;
}
+ if (on_winbar) {
+ return IN_OTHER_WIN | MOUSE_WINBAR;
+ }
if (flags & MOUSE_MAY_STOP_VIS) {
end_visual_mode();
redraw_curbuf_later(INVERTED); // delete the inversion
@@ -155,9 +159,11 @@ retnomove:
fdc = win_fdccol_count(wp);
dragwin = NULL;
- if (row == -1) {
- return IN_OTHER_WIN;
+ if (row == -1 + wp->w_winbar_height) {
+ on_winbar = !!wp->w_winbar_height;
+ return IN_OTHER_WIN | (on_winbar ? MOUSE_WINBAR : 0);
}
+ on_winbar = false;
// winpos and height may change in win_enter()!
if (grid == DEFAULT_GRID_HANDLE && row >= wp->w_height) {
@@ -253,6 +259,9 @@ retnomove:
did_drag |= count;
}
return IN_SEP_LINE; // Cursor didn't move
+ } else if (on_winbar) {
+ // After a click on the window bar don't start Visual mode.
+ return IN_OTHER_WIN | MOUSE_WINBAR;
} else {
// keep_window_focus must be true
// before moving the cursor for a left click, stop Visual mode
@@ -497,6 +506,7 @@ win_T *mouse_find_win(int *gridp, int *rowp, int *colp)
// exist.
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp == fp->fr_win) {
+ *rowp -= wp->w_winrow_off - wp->w_winbar_height;
return wp;
}
}
diff --git a/src/nvim/move.c b/src/nvim/move.c
index 11feb497ea..97ce92f6a9 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -1016,7 +1016,7 @@ void textpos2screenpos(win_T *wp, pos_T *pos, int *rowp, int *scolp, int *ccolp,
col -= wp->w_leftcol;
if (col >= 0 && col < wp->w_width) {
- coloff = col - scol + (local ? 0 : wp->w_wincol + wp->w_border_adj[3]) + 1;
+ coloff = col - scol + (local ? 0 : wp->w_wincol + wp->w_wincol_off) + 1;
} else {
scol = ccol = ecol = 0;
// character is left or right of the window
@@ -1027,7 +1027,7 @@ void textpos2screenpos(win_T *wp, pos_T *pos, int *rowp, int *scolp, int *ccolp,
}
}
}
- *rowp = (local ? 0 : wp->w_winrow + wp->w_border_adj[0]) + row + rowoff;
+ *rowp = (local ? 0 : wp->w_winrow + wp->w_winrow_off) + row + rowoff;
*scolp = scol + coloff;
*ccolp = ccol + coloff;
*ecolp = ecol + coloff;
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 2826b7dad1..1692970a97 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -1280,7 +1280,8 @@ static void normal_redraw(NormalState *s)
validate_cursor();
if (VIsual_active) {
- update_curbuf(INVERTED); // update inverted part
+ redraw_curbuf_later(INVERTED); // update inverted part
+ update_screen(INVERTED);
} else if (must_redraw) {
update_screen(0);
} else if (redraw_cmdline || clear_cmdline) {
@@ -1850,6 +1851,11 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
oap == NULL ? NULL : &(oap->inclusive),
which_button);
+ // A click in the window bar has no side effects.
+ if (jump_flags & MOUSE_WINBAR) {
+ return false;
+ }
+
moved = (jump_flags & CURSOR_MOVED);
in_status_line = (jump_flags & IN_STATUS_LINE);
in_sep_line = (jump_flags & IN_SEP_LINE);
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 8267bcf9da..2ee6c175bb 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -2149,6 +2149,8 @@ static uint32_t *insecure_flag(win_T *const wp, int opt_idx, int opt_flags)
switch ((int)options[opt_idx].indir) {
case PV_STL:
return &wp->w_p_stl_flags;
+ case PV_WBR:
+ return &wp->w_p_wbr_flags;
case PV_FDE:
return &wp->w_p_fde_flags;
case PV_FDT:
@@ -2930,8 +2932,8 @@ ambw_end:
curbuf->b_help = (curbuf->b_p_bt[0] == 'h');
redraw_titles();
}
- } else if (gvarp == &p_stl || varp == &p_tal || varp == &p_ruf) {
- // 'statusline', 'tabline' or 'rulerformat'
+ } else if (gvarp == &p_stl || gvarp == (char_u **)&p_wbr || varp == &p_tal || varp == &p_ruf) {
+ // 'statusline', 'winbar', 'tabline' or 'rulerformat'
int wid;
if (varp == &p_ruf) { // reset ru_wid first
@@ -2950,12 +2952,16 @@ ambw_end:
errmsg = check_stl_option(p_ruf);
}
} else if (varp == &p_ruf || s[0] != '%' || s[1] != '!') {
- // check 'statusline' or 'tabline' only if it doesn't start with "%!"
+ // check 'statusline', 'winbar' or 'tabline' only if it doesn't start with "%!"
errmsg = check_stl_option(s);
}
if (varp == &p_ruf && errmsg == NULL) {
comp_col();
}
+ // add / remove window bars for 'winbar'
+ if (gvarp == (char_u **)&p_wbr) {
+ set_winbar();
+ }
} else if (gvarp == &p_cpt) {
// check if it is a valid value for 'complete' -- Acevedo
for (s = *varp; *s;) {
@@ -3570,6 +3576,7 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set)
struct chars_tab fcs_tab[] = {
{ &wp->w_p_fcs_chars.stl, "stl", ' ' },
{ &wp->w_p_fcs_chars.stlnc, "stlnc", ' ' },
+ { &wp->w_p_fcs_chars.wbr, "wbr", ' ' },
{ &wp->w_p_fcs_chars.horiz, "horiz", 9472 }, // ─
{ &wp->w_p_fcs_chars.horizup, "horizup", 9524 }, // ┴
{ &wp->w_p_fcs_chars.horizdown, "horizdown", 9516 }, // ┬
@@ -3612,15 +3619,15 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set)
if (*p_ambw == 'd') {
// 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[5].def = '-';
fcs_tab[6].def = '|';
fcs_tab[7].def = '|';
- fcs_tab[8].def = '+';
- fcs_tab[9].def = '-';
- fcs_tab[12].def = '|';
+ fcs_tab[8].def = '|';
+ fcs_tab[9].def = '+';
+ fcs_tab[10].def = '-';
+ fcs_tab[13].def = '|';
}
}
@@ -4766,7 +4773,7 @@ static void check_redraw(uint32_t flags)
bool doclear = (flags & P_RCLR) == P_RCLR;
bool all = ((flags & P_RALL) == P_RALL || doclear);
- if ((flags & P_RSTAT) || all) { // mark all status lines dirty
+ if ((flags & P_RSTAT) || all) { // mark all status lines and window bars dirty
status_redraw_all();
}
@@ -5814,6 +5821,9 @@ void unset_global_local_option(char *name, void *from)
case PV_STL:
clear_string_option(&((win_T *)from)->w_p_stl);
break;
+ case PV_WBR:
+ clear_string_option((char_u **)&((win_T *)from)->w_p_wbr);
+ break;
case PV_UL:
buf->b_p_ul = NO_LOCAL_UNDOLEVEL;
break;
@@ -5891,6 +5901,8 @@ static char_u *get_varp_scope(vimoption_T *p, int opt_flags)
return (char_u *)&(curwin->w_p_sbr);
case PV_STL:
return (char_u *)&(curwin->w_p_stl);
+ case PV_WBR:
+ return (char_u *)&(curwin->w_p_wbr);
case PV_UL:
return (char_u *)&(curbuf->b_p_ul);
case PV_LW:
@@ -5984,6 +5996,9 @@ static char_u *get_varp(vimoption_T *p)
case PV_STL:
return *curwin->w_p_stl != NUL
? (char_u *)&(curwin->w_p_stl) : p->var;
+ case PV_WBR:
+ return *curwin->w_p_wbr != NUL
+ ? (char_u *)&(curwin->w_p_wbr) : p->var;
case PV_UL:
return curbuf->b_p_ul != NO_LOCAL_UNDOLEVEL
? (char_u *)&(curbuf->b_p_ul) : p->var;
@@ -6252,6 +6267,7 @@ void copy_winopt(winopt_T *from, winopt_T *to)
to->wo_rlc = vim_strsave(from->wo_rlc);
to->wo_sbr = vim_strsave(from->wo_sbr);
to->wo_stl = vim_strsave(from->wo_stl);
+ to->wo_wbr = xstrdup(from->wo_wbr);
to->wo_wrap = from->wo_wrap;
to->wo_wrap_save = from->wo_wrap_save;
to->wo_lbr = from->wo_lbr;
@@ -6327,6 +6343,7 @@ static void check_winopt(winopt_T *wop)
check_string_option(&wop->wo_fcs);
check_string_option(&wop->wo_lcs);
check_string_option(&wop->wo_ve);
+ check_string_option((char_u **)&wop->wo_wbr);
}
/// Free the allocated memory inside a winopt_T.
@@ -6352,6 +6369,7 @@ void clear_winopt(winopt_T *wop)
clear_string_option(&wop->wo_fcs);
clear_string_option(&wop->wo_lcs);
clear_string_option(&wop->wo_ve);
+ clear_string_option((char_u **)&wop->wo_wbr);
}
void didset_window_options(win_T *wp)
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index e358a29622..f5c7ae07bb 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -618,6 +618,7 @@ EXTERN int p_stmp; // 'shelltemp'
EXTERN int p_ssl; // 'shellslash'
#endif
EXTERN char_u *p_stl; // 'statusline'
+EXTERN char *p_wbr; // 'winbar'
EXTERN int p_sr; // 'shiftround'
EXTERN char_u *p_shm; // 'shortmess'
EXTERN char_u *p_sbr; // 'showbreak'
@@ -897,6 +898,7 @@ enum {
WV_FCS,
WV_LCS,
WV_WINBL,
+ WV_WBR,
WV_COUNT, // must be the last one
};
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 313cace4b2..eb92d47fc8 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -2832,6 +2832,16 @@ return {
defaults={if_true="menu"}
},
{
+ full_name='winbar', abbreviation='wbr',
+ short_desc=N_("custom format for the window bar"),
+ type='string', scope={'global', 'window'},
+ alloced=true,
+ modelineexpr=true,
+ redraw={'statuslines'},
+ varname='p_wbr',
+ defaults={if_true=""}
+ },
+ {
full_name='winblend', abbreviation='winbl',
short_desc=N_("Controls transparency level for floating windows"),
type='number', scope={'window'},
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 686516f17b..b2490fb9c5 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -276,25 +276,25 @@ void redrawWinline(win_T *wp, linenr_T lnum)
}
}
-/*
- * update all windows that are editing the current buffer
- */
-void update_curbuf(int type)
-{
- redraw_curbuf_later(type);
- update_screen(type);
-}
-
/// called when the status bars for the buffer 'buf' need to be updated
void redraw_buf_status_later(buf_T *buf)
{
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer == buf
- && (wp->w_status_height || (wp == curwin && global_stl_height()))) {
+ if (wp->w_buffer != buf) {
+ continue;
+ }
+ bool redraw = false;
+
+ if (wp->w_status_height || (wp == curwin && global_stl_height())) {
wp->w_redr_status = true;
- if (must_redraw < VALID) {
- must_redraw = VALID;
- }
+ redraw = true;
+ }
+ if (wp->w_winbar_height) {
+ wp->w_redr_winbar = true;
+ redraw = true;
+ }
+ if (redraw && must_redraw < VALID) {
+ must_redraw = VALID;
}
}
}
@@ -400,6 +400,9 @@ int update_screen(int type)
if (wp->w_floating) {
continue;
}
+ if (wp->w_winrow + wp->w_winbar_height > valid) {
+ wp->w_redr_winbar = true;
+ }
if (W_ENDROW(wp) > valid) {
wp->w_redr_type = MAX(wp->w_redr_type, NOT_VALID);
}
@@ -431,6 +434,9 @@ int update_screen(int type)
wp->w_redr_type = REDRAW_TOP;
} else {
wp->w_redr_type = NOT_VALID;
+ if (wp->w_winrow + wp->w_winbar_height <= msg_scrolled) {
+ wp->w_redr_winbar = true;
+ }
if (!is_stl_global && W_ENDROW(wp) + wp->w_status_height <= msg_scrolled) {
wp->w_redr_status = true;
}
@@ -585,10 +591,13 @@ int update_screen(int type)
win_update(wp, &providers);
}
- // redraw status line after the window to minimize cursor movement
+ // redraw status line and window bar after the window to minimize cursor movement
if (wp->w_redr_status) {
win_redr_status(wp);
}
+ if (wp->w_redr_winbar) {
+ win_redr_winbar(wp);
+ }
}
end_search_hl();
@@ -743,6 +752,7 @@ static void win_update(win_T *wp, DecorProviders *providers)
if (type >= NOT_VALID) {
wp->w_redr_status = true;
+ wp->w_redr_winbar = true;
wp->w_lines_valid = 0;
}
@@ -4528,42 +4538,55 @@ void rl_mirror(char_u *str)
}
}
-/*
- * mark all status lines for redraw; used after first :cd
- */
+/// Mark all status lines and window bars for redraw; used after first :cd
void status_redraw_all(void)
{
- if (global_stl_height()) {
- curwin->w_redr_status = true;
- redraw_later(curwin, VALID);
- } else {
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_status_height) {
- wp->w_redr_status = true;
- redraw_later(wp, VALID);
- }
+ bool is_stl_global = global_stl_height() != 0;
+
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ bool redraw = false;
+
+ if ((!is_stl_global && wp->w_status_height) || (is_stl_global && wp == curwin)) {
+ wp->w_redr_status = true;
+ redraw = true;
+ }
+ if (wp->w_winbar_height) {
+ wp->w_redr_winbar = true;
+ redraw = true;
+ }
+ if (redraw) {
+ redraw_later(wp, VALID);
}
}
}
-/// Marks all status lines of the current buffer for redraw.
+/// Marks all status lines and window bars of the current buffer for redraw.
void status_redraw_curbuf(void)
{
status_redraw_buf(curbuf);
}
-/// Marks all status lines of the specified buffer for redraw.
+/// Marks all status lines and window bars of the given buffer for redraw.
void status_redraw_buf(buf_T *buf)
{
- if (global_stl_height() != 0 && curwin->w_buffer == buf) {
- curwin->w_redr_status = true;
- redraw_later(curwin, VALID);
- } else {
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_status_height != 0 && wp->w_buffer == buf) {
- wp->w_redr_status = true;
- redraw_later(wp, VALID);
- }
+ bool is_stl_global = global_stl_height() != 0;
+
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_buffer != buf) {
+ continue;
+ }
+ bool redraw = false;
+
+ if ((!is_stl_global && wp->w_status_height) || (is_stl_global && wp == curwin)) {
+ wp->w_redr_status = true;
+ redraw = true;
+ }
+ if (wp->w_winbar_height) {
+ wp->w_redr_winbar = true;
+ redraw = true;
+ }
+ if (redraw) {
+ redraw_later(wp, VALID);
}
}
}
@@ -4577,6 +4600,9 @@ void redraw_statuslines(void)
if (wp->w_redr_status) {
win_redr_status(wp);
}
+ if (wp->w_redr_winbar) {
+ win_redr_winbar(wp);
+ }
}
if (redraw_tabline) {
draw_tabline();
@@ -5075,7 +5101,7 @@ static void redraw_custom_statusline(win_T *wp)
entered = true;
did_emsg = false;
- win_redr_custom(wp, false);
+ win_redr_custom(wp, false, false);
if (did_emsg) {
// When there is an error disable the statusline, otherwise the
// display is messed up with errors and a redraw triggers the problem
@@ -5088,6 +5114,41 @@ static void redraw_custom_statusline(win_T *wp)
entered = false;
}
+static void win_redr_winbar(win_T *wp)
+{
+ static bool entered = false;
+
+ // Return when called recursively. This can happen when the winbar contains an expression
+ // that triggers a redraw.
+ if (entered) {
+ return;
+ }
+ entered = true;
+
+ wp->w_redr_winbar = false;
+ if (wp->w_winbar_height == 0) {
+ // No window bar, do nothing.
+ } else if (!redrawing()) {
+ // Don't redraw right now, do it later.
+ wp->w_redr_winbar = true;
+ } else if (*p_wbr != NUL || *wp->w_p_wbr != NUL) {
+ int saved_did_emsg = did_emsg;
+
+ did_emsg = false;
+ win_redr_custom(wp, true, false);
+ if (did_emsg) {
+ // When there is an error disable the winbar, otherwise the
+ // display is messed up with errors and a redraw triggers the problem
+ // again and again.
+ set_string_option_direct("winbar", -1, (char_u *)"",
+ OPT_FREE | (*wp->w_p_stl != NUL
+ ? OPT_LOCAL : OPT_GLOBAL), SID_ERROR);
+ }
+ did_emsg |= saved_did_emsg;
+ }
+ entered = false;
+}
+
/// Only call if (wp->w_vsep_width != 0).
///
/// @return true if the status line of window "wp" is connected to the status
@@ -5224,11 +5285,9 @@ bool get_keymap_str(win_T *wp, char_u *fmt, char_u *buf, int len)
return buf[0] != NUL;
}
-/*
- * Redraw the status line or ruler of window "wp".
- * When "wp" is NULL redraw the tab pages line from 'tabline'.
- */
-static void win_redr_custom(win_T *wp, bool draw_ruler)
+/// Redraw the status line, window bar or ruler of window "wp".
+/// When "wp" is NULL redraw the tab pages line from 'tabline'.
+static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
{
static bool entered = false;
int attr;
@@ -5269,6 +5328,14 @@ static void win_redr_custom(win_T *wp, bool draw_ruler)
attr = HL_ATTR(HLF_TPF);
maxwidth = Columns;
use_sandbox = was_set_insecurely(wp, "tabline", 0);
+ } else if (draw_winbar) {
+ stl = (char_u *)((*wp->w_p_wbr != NUL) ? wp->w_p_wbr : p_wbr);
+ row = wp->w_winrow;
+ col = wp->w_wincol;
+ fillchar = wp->w_p_fcs_chars.wbr;
+ attr = (wp == curwin) ? HL_ATTR(HLF_WBR) : HL_ATTR(HLF_WBRNC);
+ maxwidth = wp->w_width_inner;
+ use_sandbox = was_set_insecurely(wp, "winbar", 0);
} else {
row = is_stl_global ? (Rows - p_ch - 1) : W_ENDROW(wp);
fillchar = fillchar_status(&attr, wp);
@@ -5553,14 +5620,17 @@ void win_grid_alloc(win_T *wp)
grid->Rows = rows;
grid->Columns = cols;
+ wp->w_winrow_off = wp->w_border_adj[0] + wp->w_winbar_height;
+ wp->w_wincol_off = wp->w_border_adj[3];
+
if (want_allocation) {
grid->target = grid_allocated;
- grid->row_offset = wp->w_border_adj[0];
- grid->col_offset = wp->w_border_adj[3];
+ grid->row_offset = wp->w_winrow_off;
+ grid->col_offset = wp->w_wincol_off;
} else {
grid->target = &default_grid;
- grid->row_offset = wp->w_winrow;
- grid->col_offset = wp->w_wincol;
+ grid->row_offset = wp->w_winrow + wp->w_winrow_off;
+ grid->col_offset = wp->w_wincol + wp->w_wincol_off;
}
// send grid resize event if:
@@ -6220,7 +6290,7 @@ void draw_tabline(void)
// Check for an error. If there is one we would loop in redrawing the
// screen. Avoid that by making 'tabline' empty.
did_emsg = false;
- win_redr_custom(NULL, false);
+ win_redr_custom(NULL, false, false);
if (did_emsg) {
set_string_option_direct("tabline", -1,
(char_u *)"", OPT_FREE, SID_ERROR);
@@ -6516,7 +6586,7 @@ static void win_redr_ruler(win_T *wp, bool always)
int save_called_emsg = called_emsg;
called_emsg = false;
- win_redr_custom(wp, true);
+ win_redr_custom(wp, false, true);
if (called_emsg) {
set_string_option_direct("rulerformat", -1, (char_u *)"",
OPT_FREE, SID_ERROR);
diff --git a/src/nvim/testdir/test_cursor_func.vim b/src/nvim/testdir/test_cursor_func.vim
index 9ba82e3b70..6c6a6290cb 100644
--- a/src/nvim/testdir/test_cursor_func.vim
+++ b/src/nvim/testdir/test_cursor_func.vim
@@ -102,10 +102,11 @@ func Test_screenpos()
bwipe!
call assert_equal({'col': 1, 'row': 1, 'endcol': 1, 'curscol': 1}, screenpos(win_getid(), 1, 1))
- " Needs WinBar
" nmenu WinBar.TEST :
- " call assert_equal({'col': 1, 'row': 2, 'endcol': 1, 'curscol': 1}, screenpos(win_getid(), 1, 1))
+ setlocal winbar=TEST
+ call assert_equal({'col': 1, 'row': 2, 'endcol': 1, 'curscol': 1}, screenpos(win_getid(), 1, 1))
" nunmenu WinBar.TEST
+ setlocal winbar&
endfunc
func Test_screenpos_number()
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 2c27a5d6a5..51208e7178 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -695,6 +695,7 @@ win_T *win_new_float(win_T *wp, bool last, FloatConfig fconfig, Error *err)
}
wp->w_floating = 1;
wp->w_status_height = 0;
+ wp->w_winbar_height = 0;
wp->w_hsep_height = 0;
wp->w_vsep_width = 0;
@@ -780,10 +781,8 @@ void win_config_float(win_T *wp, FloatConfig fconfig)
}
if (!ui_has(kUIMultigrid)) {
- wp->w_height = MIN(wp->w_height,
- Rows - 1 - (wp->w_border_adj[0] + wp->w_border_adj[2]));
- wp->w_width = MIN(wp->w_width,
- Columns - (wp->w_border_adj[1] + wp->w_border_adj[3]));
+ wp->w_height = MIN(wp->w_height, Rows - 1 - win_extra_height(wp));
+ wp->w_width = MIN(wp->w_width, Columns - win_extra_width(wp));
}
win_set_inner_size(wp);
@@ -1138,15 +1137,12 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
} else {
layout = FR_COL;
- /*
- * Check if we are able to split the current window and compute its
- * height.
- */
- // Current window requires at least 1 space.
- wmh1 = p_wmh == 0 ? 1 : p_wmh;
+ // Check if we are able to split the current window and compute its height.
+ // Current window requires at least 1 space plus space for the window bar.
+ wmh1 = MAX(p_wmh, 1) + oldwin->w_winbar_height;
needed = wmh1 + STATUS_HEIGHT;
if (flags & WSP_ROOM) {
- needed += p_wh - wmh1;
+ needed += p_wh - wmh1 + oldwin->w_winbar_height;
}
if (flags & (WSP_BOT | WSP_TOP)) {
minheight = frame_minheight(topframe, NOWIN) + need_status;
@@ -1155,8 +1151,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
} else if (p_ea) {
minheight = frame_minheight(oldwin->w_frame, NOWIN) + need_status;
prevfrp = oldwin->w_frame;
- for (frp = oldwin->w_frame->fr_parent; frp != NULL;
- frp = frp->fr_parent) {
+ for (frp = oldwin->w_frame->fr_parent; frp != NULL; frp = frp->fr_parent) {
if (frp->fr_layout == FR_COL) {
FOR_ALL_FRAMES(frp2, frp->fr_child) {
if (frp2 != prevfrp) {
@@ -1339,6 +1334,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
set_fraction(oldwin);
}
wp->w_fraction = oldwin->w_fraction;
+ wp->w_winbar_height = oldwin->w_winbar_height;
if (flags & WSP_VERT) {
wp->w_p_scr = curwin->w_p_scr;
@@ -1416,9 +1412,9 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
if (flags & (WSP_TOP | WSP_BOT)) {
int new_fr_height = curfrp->fr_height - new_size;
- if (!((flags & WSP_BOT) && p_ls == 0) && global_stl_height() == 0) {
+ if (!((flags & WSP_BOT) && p_ls == 0) && !is_stl_global) {
new_fr_height -= STATUS_HEIGHT;
- } else if (global_stl_height() > 0) {
+ } else if (is_stl_global) {
if (flags & WSP_BOT) {
frame_add_hsep(curfrp);
} else {
@@ -1452,7 +1448,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
}
}
}
- if ((flags & WSP_BOT) && global_stl_height() == 0) {
+ if ((flags & WSP_BOT) && !is_stl_global) {
frame_add_statusline(curfrp);
}
frame_fix_height(wp);
@@ -1482,10 +1478,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
* equalize the window sizes.
*/
if (do_equal || dir != 0) {
- win_equal(wp, true,
- (flags & WSP_VERT) ? (dir == 'v' ? 'b' : 'h')
- : dir == 'h' ? 'b' :
- 'v');
+ win_equal(wp, true, (flags & WSP_VERT) ? (dir == 'v' ? 'b' : 'h') : (dir == 'h' ? 'b' : 'v'));
}
// Don't change the window height/width to 'winheight' / 'winwidth' if a
@@ -1675,10 +1668,10 @@ int win_count(void)
}
/// Make "count" windows on the screen.
-/// Must be called when there is just one window, filling the whole screen
+/// Must be called when there is just one window, filling the whole screen.
/// (excluding the command line).
///
-/// @param vertical split windows vertically if true
+/// @param vertical split windows vertically if true.
///
/// @return actual number of windows on the screen.
int make_windows(int count, bool vertical)
@@ -1687,15 +1680,15 @@ int make_windows(int count, bool vertical)
int todo;
if (vertical) {
- // Each window needs at least 'winminwidth' lines and a separator
- // column.
+ // Each window needs at least 'winminwidth' lines and a separator column.
maxcount = (curwin->w_width + curwin->w_vsep_width
- (p_wiw - p_wmw)) / (p_wmw + 1);
} else {
- // Each window needs at least 'winminheight' lines
- // If statusline isn't global, each window also needs a statusline
+ // Each window needs at least 'winminheight' lines.
+ // If statusline isn't global, each window also needs a statusline.
+ // If 'winbar' is set, each window also needs a winbar.
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 + STATUS_HEIGHT + global_winbar_height());
}
if (maxcount < 2) {
@@ -1705,17 +1698,13 @@ int make_windows(int count, bool vertical)
count = maxcount;
}
- /*
- * add status line now, otherwise first window will be too big
- */
+ // add status line now, otherwise first window will be too big
if (count > 1) {
last_status(true);
}
- /*
- * Don't execute autocommands while creating the windows. Must do that
- * when putting the buffers in the windows.
- */
+ // Don't execute autocommands while creating the windows. Must do that
+ // when putting the buffers in the windows.
block_autocmds();
// todo is number of windows left to create
@@ -1790,7 +1779,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, hsep height and vsep width.
+ * 5. exchange the status line height, winbar height, hsep height and vsep width.
*/
wp2 = curwin->w_prev;
frp2 = curwin->w_frame->fr_prev;
@@ -1897,7 +1886,7 @@ static void win_rotate(bool upwards, int count)
frame_insert(frp->fr_parent->fr_child, frp);
}
- // exchange status height, hsep height and vsep width of old and new last window
+ // exchange status height, winbar 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;
@@ -1988,7 +1977,8 @@ void win_move_after(win_T *win1, win_T *win2)
return;
}
- // may need move the status line, horizontal or vertical separator of the last window
+ // may need move the status line, window bar, 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;
@@ -2237,7 +2227,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
} else {
extra_sep = 0;
}
- totwincount = (n + extra_sep) / (p_wmh + STATUS_HEIGHT);
+ totwincount = (n + extra_sep) / (p_wmh + STATUS_HEIGHT + global_winbar_height());
has_next_curwin = frame_has_win(topfr, next_curwin);
/*
@@ -2272,7 +2262,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 + STATUS_HEIGHT);
+ ? extra_sep : 0)) / (p_wmh + STATUS_HEIGHT + global_winbar_height());
}
room -= new_size - n;
if (room < 0) {
@@ -2318,7 +2308,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 + STATUS_HEIGHT);
+ / (p_wmh + STATUS_HEIGHT + global_winbar_height());
m = frame_minheight(fr, next_curwin);
if (has_next_curwin) {
hnc = frame_has_win(fr, next_curwin);
@@ -3732,13 +3722,9 @@ static void frame_fix_height(win_T *wp)
wp->w_frame->fr_height = wp->w_height + wp->w_hsep_height + wp->w_status_height;
}
-/*
- * Compute the minimal height for frame "topfrp".
- * Uses the 'winminheight' option.
- * When "next_curwin" isn't NULL, use p_wh for this window.
- * When "next_curwin" is NOWIN, don't use at least one line for the current
- * window.
- */
+/// Compute the minimal height for frame "topfrp". Uses the 'winminheight' option.
+/// When "next_curwin" isn't NULL, use p_wh for this window.
+/// When "next_curwin" is NOWIN, don't use at least one line for the current window.
static int frame_minheight(frame_T *topfrp, win_T *next_curwin)
{
frame_T *frp;
@@ -3746,12 +3732,14 @@ static int frame_minheight(frame_T *topfrp, win_T *next_curwin)
int n;
if (topfrp->fr_win != NULL) {
+ // Combined height of window bar and separator column or status line.
+ int extra_height = topfrp->fr_win->w_winbar_height + topfrp->fr_win->w_hsep_height
+ + topfrp->fr_win->w_status_height;
+
if (topfrp->fr_win == next_curwin) {
- m = p_wh + topfrp->fr_win->w_hsep_height + topfrp->fr_win->w_status_height;
+ m = p_wh + extra_height;
} else {
- // 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;
+ m = p_wmh + extra_height;
if (topfrp->fr_win == curwin && next_curwin == NULL) {
// Current window is minimal one line high.
if (p_wmh == 0) {
@@ -3976,7 +3964,7 @@ static void new_frame(win_T *wp)
void win_init_size(void)
{
firstwin->w_height = ROWS_AVAIL;
- firstwin->w_height_inner = firstwin->w_height;
+ firstwin->w_height_inner = firstwin->w_height - firstwin->w_winbar_height;
firstwin->w_height_outer = firstwin->w_height;
topframe->fr_height = ROWS_AVAIL;
firstwin->w_width = Columns;
@@ -4087,6 +4075,7 @@ int win_new_tabpage(int after, char_u *filename)
newtp->tp_topframe = topframe;
last_status(false);
+ set_winbar();
redraw_all_later(NOT_VALID);
@@ -5460,16 +5449,9 @@ void win_setheight(int height)
*/
void win_setheight_win(int height, win_T *win)
{
- if (win == curwin) {
- // Always keep current window at least one line high, even when
- // 'winminheight' is zero.
- if (height < p_wmh) {
- height = p_wmh;
- }
- if (height == 0) {
- height = 1;
- }
- }
+ // Always keep current window at least one line high, even when 'winminheight' is zero.
+ // Keep window at least two lines high if 'winbar' is enabled.
+ height = MAX(height, (win == curwin ? MAX(p_wmh, 1) : p_wmh) + win->w_winbar_height);
if (win->w_floating) {
win->w_float_config.height = height;
@@ -6262,7 +6244,7 @@ void win_set_inner_size(win_T *wp)
int prev_height = wp->w_height_inner;
int height = wp->w_height_request;
if (height == 0) {
- height = wp->w_height;
+ height = wp->w_height - wp->w_winbar_height;
}
if (height != prev_height) {
@@ -6279,8 +6261,9 @@ void win_set_inner_size(win_T *wp)
set_fraction(wp);
}
}
- wp->w_height_inner = height;
wp->w_skipcol = 0;
+ wp->w_height_inner = height;
+ wp->w_winrow_off = wp->w_border_adj[0] + wp->w_winbar_height;
// There is no point in adjusting the scroll position when exiting. Some
// values might be invalid.
@@ -6306,10 +6289,18 @@ void win_set_inner_size(win_T *wp)
terminal_check_size(wp->w_buffer->terminal);
}
- wp->w_height_outer = (wp->w_height_inner
- + wp->w_border_adj[0] + wp->w_border_adj[2]);
- wp->w_width_outer = (wp->w_width_inner
- + wp->w_border_adj[1] + wp->w_border_adj[3]);
+ wp->w_height_outer = (wp->w_height_inner + win_extra_height(wp));
+ wp->w_width_outer = (wp->w_width_inner + win_extra_width(wp));
+}
+
+static int win_extra_height(win_T *wp)
+{
+ return wp->w_border_adj[0] + wp->w_border_adj[2];
+}
+
+static int win_extra_width(win_T *wp)
+{
+ return wp->w_border_adj[1] + wp->w_border_adj[3];
}
/// Set the width of a window.
@@ -6662,6 +6653,19 @@ static void last_status_rec(frame_T *fr, bool statusline, bool is_stl_global)
}
}
+// Add or remove window bars from windows depending on the value of 'winbar'.
+void set_winbar(void)
+{
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ int winbar_height = (*p_wbr != NUL || *wp->w_p_wbr != NUL) ? 1 : 0;
+ if (wp->w_winbar_height != winbar_height) {
+ wp->w_winbar_height = winbar_height;
+ win_set_inner_size(wp);
+ wp->w_redr_winbar = winbar_height;
+ }
+ }
+}
+
/// Return the number of lines used by the tab page line.
int tabline_height(void)
{
@@ -6678,6 +6682,12 @@ int tabline_height(void)
return 1;
}
+/// Return the number of lines used by default by the window bar.
+int global_winbar_height(void)
+{
+ return *p_wbr != NUL ? 1 : 0;
+}
+
/// Return the number of lines used by the global statusline
int global_stl_height(void)
{
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index ba170ba8c5..c4748cc00d 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -3102,6 +3102,19 @@ describe('API', function()
'TextWithNoHighlight%#WarningMsg#TextWithWarningHighlight',
{ use_tabline = true, highlights = true }))
end)
+ it('works with winbar', function()
+ eq({
+ str = 'TextWithNoHighlightTextWithWarningHighlight',
+ width = 43,
+ highlights = {
+ { start = 0, group = 'WinBar' },
+ { start = 19, group = 'WarningMsg' }
+ }
+ },
+ meths.eval_statusline(
+ 'TextWithNoHighlight%#WarningMsg#TextWithWarningHighlight',
+ { use_winbar = true, highlights = true }))
+ end)
end)
end)
describe('nvim_parse_cmd', function()
diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua
index 92300a8fa2..d61eebe3ea 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 = 62
+ m.hl_id = 64
m.attr = {background = Screen.colors.DarkGray}
end
- if m.id_lm then m.id_lm = 63 end
+ if m.id_lm then m.id_lm = 65 end
end
-- Assert the new expectation.
diff --git a/test/functional/ui/winbar_spec.lua b/test/functional/ui/winbar_spec.lua
new file mode 100644
index 0000000000..6cf587d47b
--- /dev/null
+++ b/test/functional/ui/winbar_spec.lua
@@ -0,0 +1,180 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear = helpers.clear
+local command = helpers.command
+
+describe('winbar', function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new(60, 13)
+ screen:attach()
+ screen:set_default_attr_ids({
+ [1] = {bold = true},
+ [2] = {reverse = true},
+ [3] = {bold = true, foreground = Screen.colors.Blue},
+ [4] = {bold = true, reverse = true},
+ [5] = {bold = true, foreground = Screen.colors.Red},
+ [6] = {foreground = Screen.colors.Blue},
+ })
+ command('set winbar=Set\\ Up\\ The\\ Bars')
+ end)
+ it('works', function()
+ screen:expect([[
+ {1:Set Up The Bars }|
+ ^ |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ |
+ ]])
+ end)
+ it('works with custom \'fillchars\' value', function()
+ command('set fillchars=wbr:+')
+ screen:expect([[
+ {1:Set Up The Bars+++++++++++++++++++++++++++++++++++++++++++++}|
+ ^ |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ |
+ ]])
+ end)
+ it('works with custom highlight', function()
+ command('hi WinBar guifg=red')
+ screen:expect([[
+ {5:Set Up The Bars }|
+ ^ |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ |
+ ]])
+ end)
+ it('works with splits', function()
+ command('hi WinBar guifg=red')
+ command('hi WinBarNC guifg=blue')
+ command('belowright vsplit | split | split')
+ screen:expect([[
+ {6:Set Up The Bars }│{5:Set Up The Bars }|
+ │^ |
+ {3:~ }│{3:~ }|
+ {3:~ }│{4:[No Name] }|
+ {3:~ }│{6:Set Up The Bars }|
+ {3:~ }│ |
+ {3:~ }│{3:~ }|
+ {3:~ }│{2:[No Name] }|
+ {3:~ }│{6:Set Up The Bars }|
+ {3:~ }│ |
+ {3:~ }│{3:~ }|
+ {2:[No Name] [No Name] }|
+ |
+ ]])
+ end)
+ it('works when switching value of \'winbar\'', function()
+ command('belowright vsplit | split | split | set winbar=')
+ screen:expect([[
+ │^ |
+ {3:~ }│{3:~ }|
+ {3:~ }│{3:~ }|
+ {3:~ }│{4:[No Name] }|
+ {3:~ }│ |
+ {3:~ }│{3:~ }|
+ {3:~ }│{3:~ }|
+ {3:~ }│{2:[No Name] }|
+ {3:~ }│ |
+ {3:~ }│{3:~ }|
+ {3:~ }│{3:~ }|
+ {2:[No Name] [No Name] }|
+ |
+ ]])
+ command('set winbar=All\\ Your\\ Bar\\ Are\\ Belong\\ To\\ Us')
+ screen:expect([[
+ {1:All Your Bar Are Belong To Us}│{1:All Your Bar Are Belong To Us }|
+ │^ |
+ {3:~ }│{3:~ }|
+ {3:~ }│{4:[No Name] }|
+ {3:~ }│{1:All Your Bar Are Belong To Us }|
+ {3:~ }│ |
+ {3:~ }│{3:~ }|
+ {3:~ }│{2:[No Name] }|
+ {3:~ }│{1:All Your Bar Are Belong To Us }|
+ {3:~ }│ |
+ {3:~ }│{3:~ }|
+ {2:[No Name] [No Name] }|
+ |
+ ]])
+ command('set winbar=Changed\\ winbar')
+ screen:expect([[
+ {1:Changed winbar }│{1:Changed winbar }|
+ │^ |
+ {3:~ }│{3:~ }|
+ {3:~ }│{4:[No Name] }|
+ {3:~ }│{1:Changed winbar }|
+ {3:~ }│ |
+ {3:~ }│{3:~ }|
+ {3:~ }│{2:[No Name] }|
+ {3:~ }│{1:Changed winbar }|
+ {3:~ }│ |
+ {3:~ }│{3:~ }|
+ {2:[No Name] [No Name] }|
+ |
+ ]])
+ end)
+ it('works with laststatus=3', function()
+ command('set laststatus=3')
+ screen:expect([[
+ {1:Set Up The Bars }|
+ ^ |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {4:[No Name] }|
+ |
+ ]])
+ command('belowright vsplit | split | split')
+ screen:expect([[
+ {1:Set Up The Bars }│{1:Set Up The Bars }|
+ │^ |
+ {3:~ }│{3:~ }|
+ {3:~ }├──────────────────────────────|
+ {3:~ }│{1:Set Up The Bars }|
+ {3:~ }│ |
+ {3:~ }│{3:~ }|
+ {3:~ }├──────────────────────────────|
+ {3:~ }│{1:Set Up The Bars }|
+ {3:~ }│ |
+ {3:~ }│{3:~ }|
+ {4:[No Name] }|
+ |
+ ]])
+ end)
+end)