Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial pass on reusing existing panes when splitting #25030

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions crates/file_finder/src/file_finder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1342,10 +1342,22 @@ impl PickerDelegate for FileFinderDelegate {
let context = context.clone();
move |menu, _, _| {
menu.context(context)
.action("Split Left", pane::SplitLeft.boxed_clone())
.action("Split Right", pane::SplitRight.boxed_clone())
.action("Split Up", pane::SplitUp.boxed_clone())
.action("Split Down", pane::SplitDown.boxed_clone())
.action(
"Split Left",
pane::SplitLeft::default().boxed_clone(),
)
.action(
"Split Right",
pane::SplitRight::default().boxed_clone(),
)
.action(
"Split Up",
pane::SplitUp::default().boxed_clone(),
)
.action(
"Split Down",
pane::SplitDown::default().boxed_clone(),
)
}
}))
}
Expand Down
4 changes: 2 additions & 2 deletions crates/language_tools/src/key_context_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ impl Render for KeyContextView {
window,
))
.on_click(|_, window, cx| {
window.dispatch_action(workspace::SplitRight.boxed_clone(), cx);
window.dispatch_action(workspace::SplitRight::default().boxed_clone(), cx);
window.dispatch_action(zed_actions::OpenDefaultKeymap.boxed_clone(), cx);
}),
)
Expand All @@ -227,7 +227,7 @@ impl Render for KeyContextView {
.style(ButtonStyle::Filled)
.key_binding(ui::KeyBinding::for_action(&zed_actions::OpenKeymap, window))
.on_click(|_, window, cx| {
window.dispatch_action(workspace::SplitRight.boxed_clone(), cx);
window.dispatch_action(workspace::SplitRight::default().boxed_clone(), cx);
window.dispatch_action(zed_actions::OpenKeymap.boxed_clone(), cx);
}),
),
Expand Down
11 changes: 6 additions & 5 deletions crates/terminal_view/src/terminal_panel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,10 @@ impl TerminalPanel {
split_context.clone(),
|menu, split_context| menu.context(split_context),
)
.action("Split Right", SplitRight.boxed_clone())
.action("Split Left", SplitLeft.boxed_clone())
.action("Split Up", SplitUp.boxed_clone())
.action("Split Down", SplitDown.boxed_clone())
.action("Split Right", SplitRight::default().boxed_clone())
.action("Split Left", SplitLeft::default().boxed_clone())
.action("Split Up", SplitUp::default().boxed_clone())
.action("Split Down", SplitDown::default().boxed_clone())
})
.into()
}
Expand Down Expand Up @@ -359,7 +359,8 @@ impl TerminalPanel {
}
self.serialize(cx);
}
pane::Event::Split(direction) => {
pane::Event::Split { direction, .. } => {
// TODO: support Split::use_existing
let Some(new_pane) = self.new_pane_with_cloned_active_terminal(window, cx) else {
return;
};
Expand Down
4 changes: 2 additions & 2 deletions crates/vim/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -770,8 +770,8 @@ fn generate_commands(_: &App) -> Vec<VimCommand> {
save_intent: Some(SaveIntent::Overwrite),
}),
VimCommand::new(("cq", "uit"), zed_actions::Quit),
VimCommand::new(("sp", "lit"), workspace::SplitHorizontal),
VimCommand::new(("vs", "plit"), workspace::SplitVertical),
VimCommand::new(("sp", "lit"), workspace::SplitHorizontal::default()),
VimCommand::new(("vs", "plit"), workspace::SplitVertical::default()),
VimCommand::new(
("bd", "elete"),
workspace::CloseActiveItem {
Expand Down
110 changes: 82 additions & 28 deletions crates/workspace/src/pane.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,48 @@ pub struct DeploySearch {
pub replace_enabled: bool,
}

#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)]
#[serde(deny_unknown_fields)]
pub struct SplitRight {
#[serde(default)]
pub use_existing: bool,
}

#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)]
#[serde(deny_unknown_fields)]
pub struct SplitLeft {
#[serde(default)]
pub use_existing: bool,
}

#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)]
#[serde(deny_unknown_fields)]
pub struct SplitUp {
#[serde(default)]
pub use_existing: bool,
}

#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)]
#[serde(deny_unknown_fields)]
pub struct SplitDown {
#[serde(default)]
pub use_existing: bool,
}

#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)]
#[serde(deny_unknown_fields)]
pub struct SplitHorizontal {
#[serde(default)]
pub use_existing: bool,
}

#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)]
#[serde(deny_unknown_fields)]
pub struct SplitVertical {
#[serde(default)]
pub use_existing: bool,
}

impl_actions!(
pane,
[
Expand All @@ -166,6 +208,12 @@ impl_actions!(
ActivateItem,
RevealInProjectPanel,
DeploySearch,
SplitRight,
SplitLeft,
SplitUp,
SplitDown,
SplitHorizontal,
SplitVertical,
]
);

Expand All @@ -181,12 +229,6 @@ actions!(
JoinIntoNext,
JoinAll,
ReopenClosedItem,
SplitLeft,
SplitUp,
SplitRight,
SplitDown,
SplitHorizontal,
SplitVertical,
SwapItemLeft,
SwapItemRight,
TogglePreviewTab,
Expand Down Expand Up @@ -221,7 +263,10 @@ pub enum Event {
RemovedItem {
item_id: EntityId,
},
Split(SplitDirection),
Split {
direction: SplitDirection,
use_existing: bool,
},
JoinAll,
JoinIntoNext,
ChangeItemTitle,
Expand Down Expand Up @@ -251,9 +296,13 @@ impl fmt::Debug for Event {
.debug_struct("RemovedItem")
.field("item_id", item_id)
.finish(),
Event::Split(direction) => f
Event::Split {
direction,
use_existing,
} => f
.debug_struct("Split")
.field("direction", direction)
.field("use_existing", use_existing)
.finish(),
Event::JoinAll => f.write_str("JoinAll"),
Event::JoinIntoNext => f.write_str("JoinIntoNext"),
Expand Down Expand Up @@ -482,10 +531,10 @@ impl Pane {
.with_handle(pane.split_item_context_menu_handle.clone())
.menu(move |window, cx| {
ContextMenu::build(window, cx, |menu, _, _| {
menu.action("Split Right", SplitRight.boxed_clone())
.action("Split Left", SplitLeft.boxed_clone())
.action("Split Up", SplitUp.boxed_clone())
.action("Split Down", SplitDown.boxed_clone())
menu.action("Split Right", SplitRight::default().boxed_clone())
.action("Split Left", SplitLeft::default().boxed_clone())
.action("Split Up", SplitUp::default().boxed_clone())
.action("Split Down", SplitDown::default().boxed_clone())
})
.into()
}),
Expand Down Expand Up @@ -2012,8 +2061,11 @@ impl Pane {
}
}

pub fn split(&mut self, direction: SplitDirection, cx: &mut Context<Self>) {
cx.emit(Event::Split(direction));
pub fn split(&mut self, direction: SplitDirection, use_existing: bool, cx: &mut Context<Self>) {
cx.emit(Event::Split {
direction,
use_existing,
});
}

pub fn toolbar(&self) -> &Entity<Toolbar> {
Expand Down Expand Up @@ -3108,22 +3160,24 @@ impl Render for Pane {
.on_action(cx.listener(|pane, _: &AlternateFile, window, cx| {
pane.alternate_file(window, cx);
}))
.on_action(
cx.listener(|pane, _: &SplitLeft, _, cx| pane.split(SplitDirection::Left, cx)),
)
.on_action(cx.listener(|pane, _: &SplitUp, _, cx| pane.split(SplitDirection::Up, cx)))
.on_action(cx.listener(|pane, _: &SplitHorizontal, _, cx| {
pane.split(SplitDirection::horizontal(cx), cx)
.on_action(cx.listener(|pane, action: &SplitLeft, _, cx| {
pane.split(SplitDirection::Left, action.use_existing, cx)
}))
.on_action(cx.listener(|pane, _: &SplitVertical, _, cx| {
pane.split(SplitDirection::vertical(cx), cx)
.on_action(cx.listener(|pane, action: &SplitUp, _, cx| {
pane.split(SplitDirection::Up, action.use_existing, cx)
}))
.on_action(cx.listener(|pane, action: &SplitHorizontal, _, cx| {
pane.split(SplitDirection::horizontal(cx), action.use_existing, cx)
}))
.on_action(cx.listener(|pane, action: &SplitVertical, _, cx| {
pane.split(SplitDirection::vertical(cx), action.use_existing, cx)
}))
.on_action(cx.listener(|pane, action: &SplitRight, _, cx| {
pane.split(SplitDirection::Right, action.use_existing, cx)
}))
.on_action(cx.listener(|pane, action: &SplitDown, _, cx| {
pane.split(SplitDirection::Down, action.use_existing, cx)
}))
.on_action(
cx.listener(|pane, _: &SplitRight, _, cx| pane.split(SplitDirection::Right, cx)),
)
.on_action(
cx.listener(|pane, _: &SplitDown, _, cx| pane.split(SplitDirection::Down, cx)),
)
.on_action(
cx.listener(|pane, _: &GoBack, window, cx| pane.navigate_backward(window, cx)),
)
Expand Down
66 changes: 64 additions & 2 deletions crates/workspace/src/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3237,6 +3237,27 @@ impl Workspace {
}
}

pub fn find_or_create_pane_in_direction(
&mut self,
window: &mut Window,
pane: &Entity<Pane>,
direction: SplitDirection,
cx: &mut Context<Self>,
) -> Entity<Pane> {
if let Some(target_pane) = self
.center
.find_pane_in_direction(&pane, direction, cx)
.cloned()
{
target_pane
} else {
let new_pane = self.add_pane(window, cx);
self.center.split(pane, &new_pane, direction).unwrap();
cx.notify();
new_pane
}
}

pub fn resize_pane(
&mut self,
axis: gpui::Axis,
Expand Down Expand Up @@ -3335,8 +3356,15 @@ impl Workspace {
item: item.boxed_clone(),
});
}
pane::Event::Split(direction) => {
self.split_and_clone(pane.clone(), *direction, window, cx);
pane::Event::Split {
direction,
use_existing,
} => {
if *use_existing {
self.clone_into_split_or_existing_pane(pane.clone(), *direction, window, cx);
} else {
self.split_and_clone(pane.clone(), *direction, window, cx);
}
}
pane::Event::JoinIntoNext => {
self.join_pane_into_next(pane.clone(), window, cx);
Expand Down Expand Up @@ -3473,6 +3501,40 @@ impl Workspace {
maybe_pane_handle
}

/// Clone the active item in `pane` and move the clone into an existing pane
/// in the designated direction or a split of the current pane in that direction.
pub fn clone_into_split_or_existing_pane(
&mut self,
pane: Entity<Pane>,
direction: SplitDirection,
window: &mut Window,
cx: &mut Context<Self>,
) -> Option<Entity<Pane>> {
let item = pane.read(cx).active_item()?;
let maybe_pane_handle = if let Some(clone) =
item.clone_on_split(self.database_id(), window, cx)
{
let target_pane = self.find_or_create_pane_in_direction(window, &pane, direction, cx);
target_pane.update(cx, |pane, cx| {
let project_entry_id = clone
.is_singleton(cx)
.then_some(Some(()))
.and_then(|_| clone.project_entry_ids(cx).first().cloned());
if let Some(project_entry_id) = project_entry_id {
if let Some(existing) = pane.item_for_entry(project_entry_id, cx) {
pane.remove_item(existing.item_id(), true, false, window, cx);
}
}
pane.add_item(clone, true, true, None, window, cx);
});
Some(target_pane)
} else {
None
};
cx.notify();
maybe_pane_handle
}

pub fn split_pane_with_item(
&mut self,
pane_to_split: WeakEntity<Pane>,
Expand Down
8 changes: 4 additions & 4 deletions crates/zed/src/zed/app_menus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,10 @@ pub fn app_menus() -> Vec<Menu> {
MenuItem::submenu(Menu {
name: "Editor Layout".into(),
items: vec![
MenuItem::action("Split Up", workspace::SplitUp),
MenuItem::action("Split Down", workspace::SplitDown),
MenuItem::action("Split Left", workspace::SplitLeft),
MenuItem::action("Split Right", workspace::SplitRight),
MenuItem::action("Split Up", workspace::SplitUp::default()),
MenuItem::action("Split Down", workspace::SplitDown::default()),
MenuItem::action("Split Left", workspace::SplitLeft::default()),
MenuItem::action("Split Right", workspace::SplitRight::default()),
],
}),
MenuItem::separator(),
Expand Down
Loading