workspace: Move panes to span the entire border in Vim mode (#39123)

Currently, <kbd>⌃w</kbd> + <kbd>HJKL</kbd> keystrokes swap active pane
with another pane in that direction. Also, if there is no pane to swap
with, nothing happens.

This does not match the expected Vim behavior: moving the split to span
the entire border.

See
https://github.com/vim/vim/blob/ca6a260ef1a4b4ae94bc71c17cbabf8f12bf0f8c/runtime/doc/windows.txt#L527-L549

This change adds `MovePane{Up,Down,Left,Right}` actions that do exactly
that and updates default Vim keymap.

<table>
<tr>
  <th>Before</th>
  <th>After</th>
<tr>
<td><video
src="https://github.com/user-attachments/assets/5d3a25bf-e8b6-46c1-9fbb-004f0194e0dd">
<td><video
src="https://github.com/user-attachments/assets/5276f115-5063-411e-b141-5d268a79581b">
<tr>
  <th>Vim</th>
<tr>
<td><video
src="https://github.com/user-attachments/assets/df9fbf83-d0de-42c0-8fb0-b134be833bde">
</table>

Release Notes:

- Changed `ctrl+w` + `shift-[hjkl]` in Vim mode to move the split to
span the entire border, aligning with Vim‘s behavior.

Signed-off-by: Ivan Trubach <mr.trubach@icloud.com>
This commit is contained in:
Ivan Trubach
2025-10-16 17:09:04 -06:00
committed by GitHub
parent 620df0c722
commit aec3c2fbb7
4 changed files with 130 additions and 10 deletions
+30
View File
@@ -421,6 +421,14 @@ actions!(
SwapPaneUp,
/// Swaps the current pane with the one below.
SwapPaneDown,
/// Move the current pane to be at the far left.
MovePaneLeft,
/// Move the current pane to be at the far right.
MovePaneRight,
/// Move the current pane to be at the very top.
MovePaneUp,
/// Move the current pane to be at the very bottom.
MovePaneDown,
]
);
@@ -3866,6 +3874,16 @@ impl Workspace {
}
}
pub fn move_pane_to_border(&mut self, direction: SplitDirection, cx: &mut Context<Self>) {
if self
.center
.move_to_border(&self.active_pane, direction)
.unwrap()
{
cx.notify();
}
}
pub fn resize_pane(
&mut self,
axis: gpui::Axis,
@@ -5674,6 +5692,18 @@ impl Workspace {
.on_action(cx.listener(|workspace, _: &SwapPaneDown, _, cx| {
workspace.swap_pane_in_direction(SplitDirection::Down, cx)
}))
.on_action(cx.listener(|workspace, _: &MovePaneLeft, _, cx| {
workspace.move_pane_to_border(SplitDirection::Left, cx)
}))
.on_action(cx.listener(|workspace, _: &MovePaneRight, _, cx| {
workspace.move_pane_to_border(SplitDirection::Right, cx)
}))
.on_action(cx.listener(|workspace, _: &MovePaneUp, _, cx| {
workspace.move_pane_to_border(SplitDirection::Up, cx)
}))
.on_action(cx.listener(|workspace, _: &MovePaneDown, _, cx| {
workspace.move_pane_to_border(SplitDirection::Down, cx)
}))
.on_action(cx.listener(|this, _: &ToggleLeftDock, window, cx| {
this.toggle_dock(DockPosition::Left, window, cx);
}))