commit 6657cc36712d9e7961efbf7063fec3173dfa571b
parent 6bc0b8ae870c80e1dc9004e07436c46259854cc4
Author: Riccardo Mazzarini <me@noib3.dev>
Date: Sat, 14 Feb 2026 00:39:05 +0100
fix(ui): repaint separator connectors after all window updates #37852
Problem:
When a window is redrawn, `draw_vsep_win`/`draw_hsep_win` paint plain
separator characters (`│`/`─`) along the window's entire edges,
including cells that are connector corners belonging to other windows.
Then `draw_sep_connectors_win` only fixes the corners of that same
window, not connectors in the middle of its edges that belong to
adjacent windows.
If the window that "owns" the connector corner isn't part of the redraw,
the connector is never repainted.
Solution:
Move connector drawing out of the per-window `win_update` and into a
separate pass in `update_screen` that runs after all windows have been
updated.
Diffstat:
2 files changed, 74 insertions(+), 3 deletions(-)
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
@@ -678,6 +678,14 @@ int update_screen(void)
}
}
+ // Draw separator connectors for all windows after all window updates, so that
+ // connectors overwrite vsep/hsep characters regardless of which windows were redrawn.
+ if (did_one) {
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ draw_sep_connectors_win(wp);
+ }
+ }
+
end_search_hl();
// May need to redraw the popup menu.
@@ -1412,7 +1420,6 @@ static void win_update(win_T *wp)
if (wp->w_view_height == 0) {
// draw the horizontal separator below this window
draw_hsep_win(wp);
- draw_sep_connectors_win(wp);
wp->w_redr_type = 0;
return;
}
@@ -1421,7 +1428,6 @@ static void win_update(win_T *wp)
if (wp->w_view_width == 0) {
// draw the vertical separator right of this window
draw_vsep_win(wp);
- draw_sep_connectors_win(wp);
wp->w_redr_type = 0;
return;
}
@@ -2405,7 +2411,6 @@ redr_statuscol:
if (wp->w_redr_type >= UPD_REDRAW_TOP) {
draw_vsep_win(wp);
draw_hsep_win(wp);
- draw_sep_connectors_win(wp);
}
syn_set_timeout(NULL);
diff --git a/test/functional/ui/statusline_spec.lua b/test/functional/ui/statusline_spec.lua
@@ -487,6 +487,72 @@ describe('global statusline', function()
]])
end)
+ it('vertical separator connector is not lost when switching window', function()
+ screen:add_extra_attr_ids {
+ [101] = { background = tonumber('0x282828') },
+ [102] = { bold = true, background = tonumber('0x282828'), foreground = Screen.colors.Blue1 },
+ }
+ command('hi NormalNC guibg=#282828')
+ command('vsplit | wincmd l | split')
+ -- Cursor is in top-right. Move to bottom-right.
+ feed('<C-w>j')
+ -- Verify the ├ connector is present.
+ screen:expect([[
+ {101: }│{101: }|
+ {102:~ }│{102:~ }|*6
+ {102:~ }├─────────────────────────────|
+ {102:~ }│^ |
+ {102:~ }│{1:~ }|*5
+ {3:[No Name] 0,0-1 All}|
+ |
+ ]])
+ -- Navigate from bottom-right to left.
+ feed('<C-w>h')
+ -- The ├ connector must still be present.
+ screen:expect([[
+ ^ │{101: }|
+ {1:~ }│{102:~ }|*6
+ {1:~ }├─────────────────────────────|
+ {1:~ }│{101: }|
+ {1:~ }│{102:~ }|*5
+ {3:[No Name] 0,0-1 All}|
+ |
+ ]])
+ end)
+
+ it('horizontal separator connector is not lost when switching window', function()
+ screen:add_extra_attr_ids {
+ [101] = { background = tonumber('0x282828') },
+ [102] = { bold = true, background = tonumber('0x282828'), foreground = Screen.colors.Blue1 },
+ }
+ command('hi NormalNC guibg=#282828')
+ command('split | wincmd j | vsplit')
+ -- Cursor is in bottom-left. Move to bottom-right.
+ feed('<C-w>l')
+ -- Verify the ┬ connector is present.
+ screen:expect([[
+ {101: }|
+ {102:~ }|*6
+ ──────────────────────────────┬─────────────────────────────|
+ {101: }│^ |
+ {102:~ }│{1:~ }|*5
+ {3:[No Name] 0,0-1 All}|
+ |
+ ]])
+ -- Navigate from bottom-right to top.
+ feed('<C-w>k')
+ -- The ┬ connector must still be present.
+ screen:expect([[
+ ^ |
+ {1:~ }|*6
+ ──────────────────────────────┬─────────────────────────────|
+ {101: }│{101: }|
+ {102:~ }│{102:~ }|*5
+ {3:[No Name] 0,0-1 All}|
+ |
+ ]])
+ end)
+
it('horizontal separators unchanged when failing to split-move window', function()
exec([[
botright split