aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/screen.c
diff options
context:
space:
mode:
authorFamiu Haque <famiuhaque@protonmail.com>2022-01-30 11:57:41 +0600
committerFamiu Haque <famiuhaque@protonmail.com>2022-03-18 00:21:41 +0600
commit5ab122917474b3f9e88be4ee88bc6d627980cfe0 (patch)
tree4e7a3cbfcc0614cbb176d0166dd16948fe5c0ee5 /src/nvim/screen.c
parent046950f6309070d98ab5a3deeff9d00e079ccdd4 (diff)
downloadrneovim-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.
Diffstat (limited to 'src/nvim/screen.c')
-rw-r--r--src/nvim/screen.c301
1 files changed, 247 insertions, 54 deletions
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);