mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 03:14:56 +07:00
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
ca6a260ef1/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:
parent
620df0c722
commit
aec3c2fbb7
4 changed files with 130 additions and 10 deletions
|
|
@ -831,10 +831,10 @@
|
|||
"ctrl-w shift-right": "workspace::SwapPaneRight",
|
||||
"ctrl-w shift-up": "workspace::SwapPaneUp",
|
||||
"ctrl-w shift-down": "workspace::SwapPaneDown",
|
||||
"ctrl-w shift-h": "workspace::SwapPaneLeft",
|
||||
"ctrl-w shift-l": "workspace::SwapPaneRight",
|
||||
"ctrl-w shift-k": "workspace::SwapPaneUp",
|
||||
"ctrl-w shift-j": "workspace::SwapPaneDown",
|
||||
"ctrl-w shift-h": "workspace::MovePaneLeft",
|
||||
"ctrl-w shift-l": "workspace::MovePaneRight",
|
||||
"ctrl-w shift-k": "workspace::MovePaneUp",
|
||||
"ctrl-w shift-j": "workspace::MovePaneDown",
|
||||
"ctrl-w >": "vim::ResizePaneRight",
|
||||
"ctrl-w <": "vim::ResizePaneLeft",
|
||||
"ctrl-w -": "vim::ResizePaneDown",
|
||||
|
|
|
|||
|
|
@ -29,9 +29,9 @@ use util::{ResultExt, TryFutureExt};
|
|||
use workspace::{
|
||||
ActivateNextPane, ActivatePane, ActivatePaneDown, ActivatePaneLeft, ActivatePaneRight,
|
||||
ActivatePaneUp, ActivatePreviousPane, DraggedSelection, DraggedTab, ItemId, MoveItemToPane,
|
||||
MoveItemToPaneInDirection, NewTerminal, Pane, PaneGroup, SplitDirection, SplitDown, SplitLeft,
|
||||
SplitRight, SplitUp, SwapPaneDown, SwapPaneLeft, SwapPaneRight, SwapPaneUp, ToggleZoom,
|
||||
Workspace,
|
||||
MoveItemToPaneInDirection, MovePaneDown, MovePaneLeft, MovePaneRight, MovePaneUp, NewTerminal,
|
||||
Pane, PaneGroup, SplitDirection, SplitDown, SplitLeft, SplitRight, SplitUp, SwapPaneDown,
|
||||
SwapPaneLeft, SwapPaneRight, SwapPaneUp, ToggleZoom, Workspace,
|
||||
dock::{DockPosition, Panel, PanelEvent, PanelHandle},
|
||||
item::SerializableItem,
|
||||
move_active_item, move_item, pane,
|
||||
|
|
@ -1055,6 +1055,16 @@ impl TerminalPanel {
|
|||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_enabled_in_workspace(workspace: &Workspace, cx: &App) -> bool {
|
||||
|
|
@ -1404,6 +1414,18 @@ impl Render for TerminalPanel {
|
|||
.on_action(cx.listener(|terminal_panel, _: &SwapPaneDown, _, cx| {
|
||||
terminal_panel.swap_pane_in_direction(SplitDirection::Down, cx);
|
||||
}))
|
||||
.on_action(cx.listener(|terminal_panel, _: &MovePaneLeft, _, cx| {
|
||||
terminal_panel.move_pane_to_border(SplitDirection::Left, cx);
|
||||
}))
|
||||
.on_action(cx.listener(|terminal_panel, _: &MovePaneRight, _, cx| {
|
||||
terminal_panel.move_pane_to_border(SplitDirection::Right, cx);
|
||||
}))
|
||||
.on_action(cx.listener(|terminal_panel, _: &MovePaneUp, _, cx| {
|
||||
terminal_panel.move_pane_to_border(SplitDirection::Up, cx);
|
||||
}))
|
||||
.on_action(cx.listener(|terminal_panel, _: &MovePaneDown, _, cx| {
|
||||
terminal_panel.move_pane_to_border(SplitDirection::Down, cx);
|
||||
}))
|
||||
.on_action(
|
||||
cx.listener(|terminal_panel, action: &MoveItemToPane, window, cx| {
|
||||
let Some(&target_pane) =
|
||||
|
|
|
|||
|
|
@ -79,6 +79,56 @@ impl PaneGroup {
|
|||
}
|
||||
}
|
||||
|
||||
/// Moves active pane to span the entire border in the given direction,
|
||||
/// similar to Vim ctrl+w shift-[hjkl] motion.
|
||||
///
|
||||
/// Returns:
|
||||
/// - Ok(true) if it found and moved a pane
|
||||
/// - Ok(false) if it found but did not move the pane
|
||||
/// - Err(_) if it did not find the pane
|
||||
pub fn move_to_border(
|
||||
&mut self,
|
||||
active_pane: &Entity<Pane>,
|
||||
direction: SplitDirection,
|
||||
) -> Result<bool> {
|
||||
if let Some(pane) = self.find_pane_at_border(direction)
|
||||
&& pane == active_pane
|
||||
{
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if !self.remove(active_pane)? {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if let Member::Axis(root) = &mut self.root
|
||||
&& direction.axis() == root.axis
|
||||
{
|
||||
let idx = if direction.increasing() {
|
||||
root.members.len()
|
||||
} else {
|
||||
0
|
||||
};
|
||||
root.insert_pane(idx, active_pane);
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
let members = if direction.increasing() {
|
||||
vec![self.root.clone(), Member::Pane(active_pane.clone())]
|
||||
} else {
|
||||
vec![Member::Pane(active_pane.clone()), self.root.clone()]
|
||||
};
|
||||
self.root = Member::Axis(PaneAxis::new(direction.axis(), members));
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn find_pane_at_border(&self, direction: SplitDirection) -> Option<&Entity<Pane>> {
|
||||
match &self.root {
|
||||
Member::Pane(pane) => Some(pane),
|
||||
Member::Axis(axis) => axis.find_pane_at_border(direction),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns:
|
||||
/// - Ok(true) if it found and removed a pane
|
||||
/// - Ok(false) if it found but did not remove the pane
|
||||
|
|
@ -526,9 +576,7 @@ impl PaneAxis {
|
|||
if direction.increasing() {
|
||||
idx += 1;
|
||||
}
|
||||
|
||||
self.members.insert(idx, Member::Pane(new_pane.clone()));
|
||||
*self.flexes.lock() = vec![1.; self.members.len()];
|
||||
self.insert_pane(idx, new_pane);
|
||||
} else {
|
||||
*member =
|
||||
Member::new_axis(old_pane.clone(), new_pane.clone(), direction);
|
||||
|
|
@ -541,6 +589,26 @@ impl PaneAxis {
|
|||
anyhow::bail!("Pane not found");
|
||||
}
|
||||
|
||||
fn insert_pane(&mut self, idx: usize, new_pane: &Entity<Pane>) {
|
||||
self.members.insert(idx, Member::Pane(new_pane.clone()));
|
||||
*self.flexes.lock() = vec![1.; self.members.len()];
|
||||
}
|
||||
|
||||
fn find_pane_at_border(&self, direction: SplitDirection) -> Option<&Entity<Pane>> {
|
||||
if self.axis != direction.axis() {
|
||||
return None;
|
||||
}
|
||||
let member = if direction.increasing() {
|
||||
self.members.last()
|
||||
} else {
|
||||
self.members.first()
|
||||
};
|
||||
member.and_then(|e| match e {
|
||||
Member::Pane(pane) => Some(pane),
|
||||
Member::Axis(_) => None,
|
||||
})
|
||||
}
|
||||
|
||||
fn remove(&mut self, pane_to_remove: &Entity<Pane>) -> Result<Option<Member>> {
|
||||
let mut found_pane = false;
|
||||
let mut remove_member = None;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}))
|
||||
|
|
|
|||
Loading…
Reference in a new issue