workspace: Make Item::clone_on_split async (#41211)

Split out from https://github.com/zed-industries/zed/pull/40774 to
reduce the size of the reland of that PR (once I figure out the cause of
the issue)

Release Notes:

- N/A *or* Added/Fixed/Improved ...
This commit is contained in:
Lukas Wirth
2025-10-26 08:46:37 +00:00
committed by GitHub
parent b7cc597d28
commit 33ec545d1f
24 changed files with 217 additions and 157 deletions
+15 -11
View File
@@ -11,8 +11,9 @@ use anyhow::Result;
use client::{Client, proto};
use futures::{StreamExt, channel::mpsc};
use gpui::{
Action, AnyElement, AnyView, App, Context, Entity, EntityId, EventEmitter, FocusHandle,
Focusable, Font, HighlightStyle, Pixels, Point, Render, SharedString, Task, WeakEntity, Window,
Action, AnyElement, AnyView, App, AppContext, Context, Entity, EntityId, EventEmitter,
FocusHandle, Focusable, Font, HighlightStyle, Pixels, Point, Render, SharedString, Task,
WeakEntity, Window,
};
use project::{Project, ProjectEntryId, ProjectPath};
pub use settings::{
@@ -217,11 +218,11 @@ pub trait Item: Focusable + EventEmitter<Self::Event> + Render + Sized {
_workspace_id: Option<WorkspaceId>,
_window: &mut Window,
_: &mut Context<Self>,
) -> Option<Entity<Self>>
) -> Task<Option<Entity<Self>>>
where
Self: Sized,
{
None
Task::ready(None)
}
fn is_dirty(&self, _: &App) -> bool {
false
@@ -422,7 +423,7 @@ pub trait ItemHandle: 'static + Send {
workspace_id: Option<WorkspaceId>,
window: &mut Window,
cx: &mut App,
) -> Option<Box<dyn ItemHandle>>;
) -> Task<Option<Box<dyn ItemHandle>>>;
fn added_to_pane(
&self,
workspace: &mut Workspace,
@@ -635,9 +636,12 @@ impl<T: Item> ItemHandle for Entity<T> {
workspace_id: Option<WorkspaceId>,
window: &mut Window,
cx: &mut App,
) -> Option<Box<dyn ItemHandle>> {
self.update(cx, |item, cx| item.clone_on_split(workspace_id, window, cx))
.map(|handle| Box::new(handle) as Box<dyn ItemHandle>)
) -> Task<Option<Box<dyn ItemHandle>>> {
let task = self.update(cx, |item, cx| item.clone_on_split(workspace_id, window, cx));
cx.background_spawn(async move {
task.await
.map(|handle| Box::new(handle) as Box<dyn ItemHandle>)
})
}
fn added_to_pane(
@@ -1504,11 +1508,11 @@ pub mod test {
_workspace_id: Option<WorkspaceId>,
_: &mut Window,
cx: &mut Context<Self>,
) -> Option<Entity<Self>>
) -> Task<Option<Entity<Self>>>
where
Self: Sized,
{
Some(cx.new(|cx| Self {
Task::ready(Some(cx.new(|cx| Self {
state: self.state.clone(),
label: self.label.clone(),
save_count: self.save_count,
@@ -1525,7 +1529,7 @@ pub mod test {
workspace_id: self.workspace_id,
focus_handle: cx.focus_handle(),
serialize: None,
}))
})))
}
fn is_dirty(&self, _: &App) -> bool {
+12 -5
View File
@@ -3292,11 +3292,18 @@ impl Pane {
else {
return;
};
if let Some(item) = item.clone_on_split(database_id, window, cx) {
to_pane.update(cx, |pane, cx| {
pane.add_item(item, true, true, None, window, cx);
})
}
let task = item.clone_on_split(database_id, window, cx);
let to_pane = to_pane.downgrade();
cx.spawn_in(window, async move |_, cx| {
if let Some(item) = task.await {
to_pane
.update_in(cx, |pane, window, cx| {
pane.add_item(item, true, true, None, window, cx)
})
.ok();
}
})
.detach();
} else {
move_item(&from_pane, &to_pane, item_id, ix, true, window, cx);
}
+4 -4
View File
@@ -6,7 +6,7 @@ use call::{RemoteVideoTrack, RemoteVideoTrackView, Room};
use client::{User, proto::PeerId};
use gpui::{
AppContext as _, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement,
ParentElement, Render, SharedString, Styled, div,
ParentElement, Render, SharedString, Styled, Task, div,
};
use std::sync::Arc;
use ui::{Icon, IconName, prelude::*};
@@ -114,14 +114,14 @@ impl Item for SharedScreen {
_workspace_id: Option<WorkspaceId>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Option<Entity<Self>> {
Some(cx.new(|cx| Self {
) -> Task<Option<Entity<Self>>> {
Task::ready(Some(cx.new(|cx| Self {
view: self.view.update(cx, |view, cx| view.clone(window, cx)),
peer_id: self.peer_id,
user: self.user.clone(),
nav_history: Default::default(),
focus: cx.focus_handle(),
}))
})))
}
fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) {
+5 -3
View File
@@ -1,5 +1,7 @@
#![allow(unused, dead_code)]
use gpui::{AnyElement, App, Entity, EventEmitter, FocusHandle, Focusable, Hsla, actions, hsla};
use gpui::{
AnyElement, App, Entity, EventEmitter, FocusHandle, Focusable, Hsla, Task, actions, hsla,
};
use strum::IntoEnumIterator;
use theme::all_theme_colors;
use ui::{
@@ -100,11 +102,11 @@ impl Item for ThemePreview {
_workspace_id: Option<crate::WorkspaceId>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Option<Entity<Self>>
) -> Task<Option<Entity<Self>>>
where
Self: Sized,
{
Some(cx.new(|cx| Self::new(window, cx)))
Task::ready(Some(cx.new(|cx| Self::new(window, cx))))
}
}
+60 -42
View File
@@ -3627,7 +3627,8 @@ impl Workspace {
if let Some(pane) = panes.get(action.0).map(|p| (*p).clone()) {
window.focus(&pane.focus_handle(cx));
} else {
self.split_and_clone(self.active_pane.clone(), SplitDirection::Right, window, cx);
self.split_and_clone(self.active_pane.clone(), SplitDirection::Right, window, cx)
.detach();
}
}
@@ -3994,7 +3995,8 @@ impl Workspace {
clone_active_item,
} => {
if *clone_active_item {
self.split_and_clone(pane.clone(), *direction, window, cx);
self.split_and_clone(pane.clone(), *direction, window, cx)
.detach();
} else {
self.split_and_move(pane.clone(), *direction, window, cx);
}
@@ -4135,21 +4137,27 @@ impl Workspace {
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 new_pane = self.add_pane(window, cx);
new_pane.update(cx, |pane, cx| {
pane.add_item(clone, true, true, None, window, cx)
});
self.center.split(&pane, &new_pane, direction).unwrap();
cx.notify();
Some(new_pane)
) -> Task<Option<Entity<Pane>>> {
let Some(item) = pane.read(cx).active_item() else {
return Task::ready(None);
};
let task = item.clone_on_split(self.database_id(), window, cx);
cx.spawn_in(window, async move |this, cx| {
if let Some(clone) = task.await {
this.update_in(cx, |this, window, cx| {
let new_pane = this.add_pane(window, cx);
new_pane.update(cx, |pane, cx| {
pane.add_item(clone, true, true, None, window, cx)
});
this.center.split(&pane, &new_pane, direction).unwrap();
cx.notify();
new_pane
})
.ok()
} else {
None
};
maybe_pane_handle
}
})
}
pub fn join_all_panes(&mut self, window: &mut Window, cx: &mut Context<Self>) {
@@ -8217,19 +8225,27 @@ pub fn clone_active_item(
let Some(active_item) = source.read(cx).active_item() else {
return;
};
destination.update(cx, |target_pane, cx| {
let Some(clone) = active_item.clone_on_split(workspace_id, window, cx) else {
return;
};
target_pane.add_item(
clone,
focus_destination,
focus_destination,
Some(target_pane.items_len()),
window,
cx,
);
});
let destination = destination.downgrade();
let task = active_item.clone_on_split(workspace_id, window, cx);
window
.spawn(cx, async move |cx| {
let Some(clone) = task.await else {
return;
};
destination
.update_in(cx, |target_pane, window, cx| {
target_pane.add_item(
clone,
focus_destination,
focus_destination,
Some(target_pane.items_len()),
window,
cx,
);
})
.log_err();
})
.detach();
}
#[derive(Debug)]
@@ -8736,25 +8752,24 @@ mod tests {
cx,
);
let right_pane = workspace
.split_and_clone(left_pane.clone(), SplitDirection::Right, window, cx)
.unwrap();
let right_pane =
workspace.split_and_clone(left_pane.clone(), SplitDirection::Right, window, cx);
right_pane.update(cx, |pane, cx| {
pane.add_item(
single_entry_items[1].boxed_clone(),
true,
true,
None,
window,
cx,
);
pane.add_item(Box::new(item_3_4.clone()), true, true, None, window, cx);
let boxed_clone = single_entry_items[1].boxed_clone();
let right_pane = window.spawn(cx, async move |cx| {
right_pane.await.inspect(|right_pane| {
right_pane
.update_in(cx, |pane, window, cx| {
pane.add_item(boxed_clone, true, true, None, window, cx);
pane.add_item(Box::new(item_3_4.clone()), true, true, None, window, cx);
})
.unwrap();
})
});
(left_pane, right_pane)
});
let right_pane = right_pane.await.unwrap();
cx.focus(&right_pane);
let mut close = right_pane.update_in(cx, |pane, window, cx| {
@@ -10571,7 +10586,10 @@ mod tests {
window,
cx,
);
});
cx.run_until_parked();
workspace.update(cx, |workspace, cx| {
assert_eq!(workspace.panes.len(), 3, "Two new panes were created");
for pane in workspace.panes() {
assert_eq!(