From 33ec545d1fae5d65da11c0395470f76e9e8aaebd Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 26 Oct 2025 09:46:37 +0100 Subject: [PATCH] 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 ... --- crates/agent_ui/src/agent_diff.rs | 6 +- crates/collab/src/tests/following_tests.rs | 44 ++++---- crates/collab/src/tests/integration_tests.rs | 2 +- crates/collab_ui/src/channel_view.rs | 6 +- crates/diagnostics/src/buffer_diagnostics.rs | 6 +- crates/diagnostics/src/diagnostics.rs | 6 +- crates/editor/src/items.rs | 4 +- crates/git_ui/src/commit_view.rs | 10 +- crates/git_ui/src/project_diff.rs | 10 +- crates/image_viewer/src/image_viewer.rs | 6 +- crates/language_tools/src/key_context_view.rs | 7 +- crates/language_tools/src/lsp_log_view.rs | 8 +- crates/language_tools/src/syntax_tree_view.rs | 8 +- crates/onboarding/src/onboarding.rs | 6 +- crates/repl/src/notebook/notebook_ui.rs | 6 +- crates/search/src/project_search.rs | 8 +- crates/terminal_view/src/terminal_view.rs | 46 ++++---- crates/workspace/src/item.rs | 26 +++-- crates/workspace/src/pane.rs | 17 ++- crates/workspace/src/shared_screen.rs | 8 +- crates/workspace/src/theme_preview.rs | 8 +- crates/workspace/src/workspace.rs | 102 ++++++++++-------- crates/zed/src/zed.rs | 20 ++-- crates/zed/src/zed/component_preview.rs | 6 +- 24 files changed, 218 insertions(+), 158 deletions(-) diff --git a/crates/agent_ui/src/agent_diff.rs b/crates/agent_ui/src/agent_diff.rs index 146a1fc71d2d11023dec49e42fdcfee2b081bfd3..dd11a3f2ccb88e38138d5c5f0e77805833a9a358 100644 --- a/crates/agent_ui/src/agent_diff.rs +++ b/crates/agent_ui/src/agent_diff.rs @@ -581,11 +581,13 @@ impl Item for AgentDiffPane { _workspace_id: Option, window: &mut Window, cx: &mut Context, - ) -> Option> + ) -> Task>> where Self: Sized, { - Some(cx.new(|cx| Self::new(self.thread.clone(), self.workspace.clone(), window, cx))) + Task::ready(Some(cx.new(|cx| { + Self::new(self.thread.clone(), self.workspace.clone(), window, cx) + }))) } fn is_dirty(&self, cx: &App) -> bool { diff --git a/crates/collab/src/tests/following_tests.rs b/crates/collab/src/tests/following_tests.rs index ab72ce3605b7c93bac05dc6321b44b7abb964d93..07cf866a3513d27894307216e904b130eb023e22 100644 --- a/crates/collab/src/tests/following_tests.rs +++ b/crates/collab/src/tests/following_tests.rs @@ -776,26 +776,30 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T .unwrap(); // Clients A and B follow each other in split panes - workspace_a.update_in(cx_a, |workspace, window, cx| { - workspace.split_and_clone( - workspace.active_pane().clone(), - SplitDirection::Right, - window, - cx, - ); - }); + workspace_a + .update_in(cx_a, |workspace, window, cx| { + workspace.split_and_clone( + workspace.active_pane().clone(), + SplitDirection::Right, + window, + cx, + ) + }) + .await; workspace_a.update_in(cx_a, |workspace, window, cx| { workspace.follow(client_b.peer_id().unwrap(), window, cx) }); executor.run_until_parked(); - workspace_b.update_in(cx_b, |workspace, window, cx| { - workspace.split_and_clone( - workspace.active_pane().clone(), - SplitDirection::Right, - window, - cx, - ); - }); + workspace_b + .update_in(cx_b, |workspace, window, cx| { + workspace.split_and_clone( + workspace.active_pane().clone(), + SplitDirection::Right, + window, + cx, + ) + }) + .await; workspace_b.update_in(cx_b, |workspace, window, cx| { workspace.follow(client_a.peer_id().unwrap(), window, cx) }); @@ -1369,9 +1373,11 @@ async fn test_auto_unfollowing(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont ); // When client B activates a different pane, it continues following client A in the original pane. - workspace_b.update_in(cx_b, |workspace, window, cx| { - workspace.split_and_clone(pane_b.clone(), SplitDirection::Right, window, cx) - }); + workspace_b + .update_in(cx_b, |workspace, window, cx| { + workspace.split_and_clone(pane_b.clone(), SplitDirection::Right, window, cx) + }) + .await; assert_eq!( workspace_b.update(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)), Some(leader_id.into()) diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index 30396ab90290b692537b974fde8308287079e50f..4fa32b6c9ba55e6962547510f52251f16fc9be81 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -6748,7 +6748,7 @@ async fn test_preview_tabs(cx: &mut TestAppContext) { pane.update(cx, |pane, cx| { pane.split(workspace::SplitDirection::Right, cx); }); - + cx.run_until_parked(); let right_pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); pane.update(cx, |pane, cx| { diff --git a/crates/collab_ui/src/channel_view.rs b/crates/collab_ui/src/channel_view.rs index 4e4bd2ca958d20225a7188b1f7f601e879e22835..18847363bf1b012acb7916bb7b6a9c0adde4de28 100644 --- a/crates/collab_ui/src/channel_view.rs +++ b/crates/collab_ui/src/channel_view.rs @@ -498,8 +498,8 @@ impl Item for ChannelView { _: Option, window: &mut Window, cx: &mut Context, - ) -> Option> { - Some(cx.new(|cx| { + ) -> Task>> { + Task::ready(Some(cx.new(|cx| { Self::new( self.project.clone(), self.workspace.clone(), @@ -508,7 +508,7 @@ impl Item for ChannelView { window, cx, ) - })) + }))) } fn navigate( diff --git a/crates/diagnostics/src/buffer_diagnostics.rs b/crates/diagnostics/src/buffer_diagnostics.rs index e25d3a7702e02c93e38f5808434aff57e743defe..1a7a97c68691d7bdf941322660eddeb70fa15037 100644 --- a/crates/diagnostics/src/buffer_diagnostics.rs +++ b/crates/diagnostics/src/buffer_diagnostics.rs @@ -693,11 +693,11 @@ impl Item for BufferDiagnosticsEditor { _workspace_id: Option, window: &mut Window, cx: &mut Context, - ) -> Option> + ) -> Task>> where Self: Sized, { - Some(cx.new(|cx| { + Task::ready(Some(cx.new(|cx| { BufferDiagnosticsEditor::new( self.project_path.clone(), self.project.clone(), @@ -706,7 +706,7 @@ impl Item for BufferDiagnosticsEditor { window, cx, ) - })) + }))) } fn deactivated(&mut self, window: &mut Window, cx: &mut Context) { diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 47e2a9539b7e362bb9b968d8e39cda30d3f17e78..b96e8f891fda3cc470c7091eba8e92847b59562b 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -732,11 +732,11 @@ impl Item for ProjectDiagnosticsEditor { _workspace_id: Option, window: &mut Window, cx: &mut Context, - ) -> Option> + ) -> Task>> where Self: Sized, { - Some(cx.new(|cx| { + Task::ready(Some(cx.new(|cx| { ProjectDiagnosticsEditor::new( self.include_warnings, self.project.clone(), @@ -744,7 +744,7 @@ impl Item for ProjectDiagnosticsEditor { window, cx, ) - })) + }))) } fn is_dirty(&self, cx: &App) -> bool { diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 0ac8a509554549815b3c43750c517da8954e0e54..6dab57db52700bc499376abb0ab80e9cdb45e5e9 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -762,11 +762,11 @@ impl Item for Editor { _workspace_id: Option, window: &mut Window, cx: &mut Context, - ) -> Option> + ) -> Task>> where Self: Sized, { - Some(cx.new(|cx| self.clone(window, cx))) + Task::ready(Some(cx.new(|cx| self.clone(window, cx)))) } fn set_nav_history( diff --git a/crates/git_ui/src/commit_view.rs b/crates/git_ui/src/commit_view.rs index 9738e13984a0b032b09a218990f3466052e9fa61..2430796c89f68e9b5032c3d05f7106b6f6de0bec 100644 --- a/crates/git_ui/src/commit_view.rs +++ b/crates/git_ui/src/commit_view.rs @@ -4,8 +4,8 @@ use editor::{Editor, EditorEvent, MultiBuffer, SelectionEffects, multibuffer_con use git::repository::{CommitDetails, CommitDiff, RepoPath}; use gpui::{ Action, AnyElement, AnyView, App, AppContext as _, AsyncApp, AsyncWindowContext, Context, - Entity, EventEmitter, FocusHandle, Focusable, IntoElement, PromptLevel, Render, WeakEntity, - Window, actions, + Entity, EventEmitter, FocusHandle, Focusable, IntoElement, PromptLevel, Render, Task, + WeakEntity, Window, actions, }; use language::{ Anchor, Buffer, Capability, DiskState, File, LanguageRegistry, LineEnding, OffsetRangeExt as _, @@ -561,11 +561,11 @@ impl Item for CommitView { _workspace_id: Option, window: &mut Window, cx: &mut Context, - ) -> Option> + ) -> Task>> where Self: Sized, { - Some(cx.new(|cx| { + Task::ready(Some(cx.new(|cx| { let editor = cx.new(|cx| { self.editor .update(cx, |editor, cx| editor.clone(window, cx)) @@ -577,7 +577,7 @@ impl Item for CommitView { commit: self.commit.clone(), stash: self.stash, } - })) + }))) } } diff --git a/crates/git_ui/src/project_diff.rs b/crates/git_ui/src/project_diff.rs index 5c74cd6a9689313f343ee97241a7934c0949108a..e6f8099c732826d3546680fab9ef94c8e1d3db32 100644 --- a/crates/git_ui/src/project_diff.rs +++ b/crates/git_ui/src/project_diff.rs @@ -714,12 +714,16 @@ impl Item for ProjectDiff { _workspace_id: Option, window: &mut Window, cx: &mut Context, - ) -> Option> + ) -> Task>> where Self: Sized, { - let workspace = self.workspace.upgrade()?; - Some(cx.new(|cx| ProjectDiff::new(self.project.clone(), workspace, window, cx))) + let Some(workspace) = self.workspace.upgrade() else { + return Task::ready(None); + }; + Task::ready(Some(cx.new(|cx| { + ProjectDiff::new(self.project.clone(), workspace, window, cx) + }))) } fn is_dirty(&self, cx: &App) -> bool { diff --git a/crates/image_viewer/src/image_viewer.rs b/crates/image_viewer/src/image_viewer.rs index 6162d77241c5b5e85d3f41a3cbc9bdaba6766d65..17259d15f1d81ac2f46e027fcf7889cdbbe9d011 100644 --- a/crates/image_viewer/src/image_viewer.rs +++ b/crates/image_viewer/src/image_viewer.rs @@ -179,15 +179,15 @@ impl Item for ImageView { _workspace_id: Option, _: &mut Window, cx: &mut Context, - ) -> Option> + ) -> Task>> where Self: Sized, { - Some(cx.new(|cx| Self { + Task::ready(Some(cx.new(|cx| Self { image_item: self.image_item.clone(), project: self.project.clone(), focus_handle: cx.focus_handle(), - })) + }))) } fn has_deleted_file(&self, cx: &App) -> bool { diff --git a/crates/language_tools/src/key_context_view.rs b/crates/language_tools/src/key_context_view.rs index 12b49ddf290f6a49348e7bc0de1b98e238b1fea1..e704d6bbf03eea18ae717f7aa11b25466dd68e9e 100644 --- a/crates/language_tools/src/key_context_view.rs +++ b/crates/language_tools/src/key_context_view.rs @@ -1,6 +1,7 @@ use gpui::{ Action, App, AppContext as _, Entity, EventEmitter, FocusHandle, Focusable, - KeyBindingContextPredicate, KeyContext, Keystroke, MouseButton, Render, Subscription, actions, + KeyBindingContextPredicate, KeyContext, Keystroke, MouseButton, Render, Subscription, Task, + actions, }; use itertools::Itertools; use serde_json::json; @@ -157,11 +158,11 @@ impl Item for KeyContextView { _workspace_id: Option, window: &mut Window, cx: &mut Context, - ) -> Option> + ) -> Task>> where Self: Sized, { - Some(cx.new(|cx| KeyContextView::new(window, cx))) + Task::ready(Some(cx.new(|cx| KeyContextView::new(window, cx)))) } } diff --git a/crates/language_tools/src/lsp_log_view.rs b/crates/language_tools/src/lsp_log_view.rs index e834dd6aec003930d68ed745f67aff50b2c8f66b..b1e24303c47e722460d20023d4f7444a8b006406 100644 --- a/crates/language_tools/src/lsp_log_view.rs +++ b/crates/language_tools/src/lsp_log_view.rs @@ -3,7 +3,7 @@ use copilot::Copilot; use editor::{Editor, EditorEvent, actions::MoveToEnd, scroll::Autoscroll}; use gpui::{ AnyView, App, Context, Corner, Entity, EventEmitter, FocusHandle, Focusable, IntoElement, - ParentElement, Render, Styled, Subscription, WeakEntity, Window, actions, div, + ParentElement, Render, Styled, Subscription, Task, WeakEntity, Window, actions, div, }; use itertools::Itertools; use language::{LanguageServerId, language_settings::SoftWrap}; @@ -763,11 +763,11 @@ impl Item for LspLogView { _workspace_id: Option, window: &mut Window, cx: &mut Context, - ) -> Option> + ) -> Task>> where Self: Sized, { - Some(cx.new(|cx| { + Task::ready(Some(cx.new(|cx| { let mut new_view = Self::new(self.project.clone(), self.log_store.clone(), window, cx); if let Some(server_id) = self.current_server_id { match self.active_entry_kind { @@ -778,7 +778,7 @@ impl Item for LspLogView { } } new_view - })) + }))) } } diff --git a/crates/language_tools/src/syntax_tree_view.rs b/crates/language_tools/src/syntax_tree_view.rs index 464d518c2e9c697d292d7bffda7ee7bae68dd254..9e5a0374f54f97fc3d75ebc68e2247bf4b904f0c 100644 --- a/crates/language_tools/src/syntax_tree_view.rs +++ b/crates/language_tools/src/syntax_tree_view.rs @@ -3,7 +3,7 @@ use editor::{Anchor, Editor, ExcerptId, SelectionEffects, scroll::Autoscroll}; use gpui::{ App, AppContext as _, Context, Div, Entity, EntityId, EventEmitter, FocusHandle, Focusable, Hsla, InteractiveElement, IntoElement, MouseButton, MouseDownEvent, MouseMoveEvent, - ParentElement, Render, ScrollStrategy, SharedString, Styled, UniformListScrollHandle, + ParentElement, Render, ScrollStrategy, SharedString, Styled, Task, UniformListScrollHandle, WeakEntity, Window, actions, div, rems, uniform_list, }; use language::{Buffer, OwnedSyntaxLayer}; @@ -573,17 +573,17 @@ impl Item for SyntaxTreeView { _: Option, window: &mut Window, cx: &mut Context, - ) -> Option> + ) -> Task>> where Self: Sized, { - Some(cx.new(|cx| { + Task::ready(Some(cx.new(|cx| { let mut clone = Self::new(self.workspace_handle.clone(), None, window, cx); if let Some(editor) = &self.editor { clone.set_editor(editor.editor.clone(), window, cx) } clone - })) + }))) } } diff --git a/crates/onboarding/src/onboarding.rs b/crates/onboarding/src/onboarding.rs index 70b6d878ac55c5da470455695542cc0597341c1f..913d92d48c4018759f5ba91bb61d514160ba1b3f 100644 --- a/crates/onboarding/src/onboarding.rs +++ b/crates/onboarding/src/onboarding.rs @@ -383,14 +383,14 @@ impl Item for Onboarding { _workspace_id: Option, _: &mut Window, cx: &mut Context, - ) -> Option> { - Some(cx.new(|cx| Onboarding { + ) -> Task>> { + Task::ready(Some(cx.new(|cx| Onboarding { workspace: self.workspace.clone(), user_store: self.user_store.clone(), scroll_handle: ScrollHandle::new(), focus_handle: cx.focus_handle(), _settings_subscription: cx.observe_global::(move |_, cx| cx.notify()), - })) + }))) } fn to_item_events(event: &Self::Event, mut f: impl FnMut(workspace::item::ItemEvent)) { diff --git a/crates/repl/src/notebook/notebook_ui.rs b/crates/repl/src/notebook/notebook_ui.rs index b1050bce338903c5fdfa3a1867f615fa1dfd6497..209948685ce263361101e508ce6ab65839b132cb 100644 --- a/crates/repl/src/notebook/notebook_ui.rs +++ b/crates/repl/src/notebook/notebook_ui.rs @@ -699,11 +699,13 @@ impl Item for NotebookEditor { _workspace_id: Option, window: &mut Window, cx: &mut Context, - ) -> Option> + ) -> Task>> where Self: Sized, { - Some(cx.new(|cx| Self::new(self.project.clone(), self.notebook_item.clone(), window, cx))) + Task::ready(Some(cx.new(|cx| { + Self::new(self.project.clone(), self.notebook_item.clone(), window, cx) + }))) } fn buffer_kind(&self, _: &App) -> workspace::item::ItemBufferKind { diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index c5fa7ecd0461c70c924fbbfc02b5090456143fa1..3a9367db724257d4ba32c343c578ba27bea412d7 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -572,12 +572,14 @@ impl Item for ProjectSearchView { _workspace_id: Option, window: &mut Window, cx: &mut Context, - ) -> Option> + ) -> Task>> where Self: Sized, { let model = self.entity.update(cx, |model, cx| model.clone(cx)); - Some(cx.new(|cx| Self::new(self.workspace.clone(), model, window, cx, None))) + Task::ready(Some(cx.new(|cx| { + Self::new(self.workspace.clone(), model, window, cx, None) + }))) } fn added_to_workspace( @@ -3677,6 +3679,7 @@ pub mod tests { ) }) .unwrap() + .await .unwrap(); assert_eq!(cx.update(|cx| second_pane.read(cx).items_len()), 1); @@ -3872,6 +3875,7 @@ pub mod tests { ) }) .unwrap() + .await .unwrap(); assert_eq!(cx.update(|cx| second_pane.read(cx).items_len()), 1); assert!( diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index ddcf1094dc13810cf04c90e421cdde56b4bf7b4b..7430a2b8acbbf4d6d2d78109e40c4781f1fab7a7 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -38,7 +38,7 @@ use ui::{ prelude::*, scrollbars::{self, GlobalSetting, ScrollbarVisibility}, }; -use util::ResultExt; +use util::{ResultExt, maybe}; use workspace::{ CloseActiveItem, NewCenterTerminal, NewTerminal, ToolbarItemLocation, Workspace, WorkspaceId, delete_unloaded_items, @@ -1218,27 +1218,29 @@ impl Item for TerminalView { workspace_id: Option, window: &mut Window, cx: &mut Context, - ) -> Option> { - let terminal = self - .project - .update(cx, |project, cx| { - let cwd = project - .active_project_directory(cx) - .map(|it| it.to_path_buf()); - project.clone_terminal(self.terminal(), cx, cwd) - }) - .ok()? - .log_err()?; - - Some(cx.new(|cx| { - TerminalView::new( - terminal, - self.workspace.clone(), - workspace_id, - self.project.clone(), - window, - cx, - ) + ) -> Task>> { + Task::ready(maybe!({ + let terminal = self + .project + .update(cx, |project, cx| { + let cwd = project + .active_project_directory(cx) + .map(|it| it.to_path_buf()); + project.clone_terminal(self.terminal(), cx, cwd) + }) + .ok()? + .log_err()?; + + Some(cx.new(|cx| { + TerminalView::new( + terminal, + self.workspace.clone(), + workspace_id, + self.project.clone(), + window, + cx, + ) + })) })) } diff --git a/crates/workspace/src/item.rs b/crates/workspace/src/item.rs index ef8f4452e76c7b984b721af8b50eb744f338c150..a328bb67924f9be7bcbdc457153699a672fea08b 100644 --- a/crates/workspace/src/item.rs +++ b/crates/workspace/src/item.rs @@ -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 + Render + Sized { _workspace_id: Option, _window: &mut Window, _: &mut Context, - ) -> Option> + ) -> Task>> 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, window: &mut Window, cx: &mut App, - ) -> Option>; + ) -> Task>>; fn added_to_pane( &self, workspace: &mut Workspace, @@ -635,9 +636,12 @@ impl ItemHandle for Entity { workspace_id: Option, window: &mut Window, cx: &mut App, - ) -> Option> { - self.update(cx, |item, cx| item.clone_on_split(workspace_id, window, cx)) - .map(|handle| Box::new(handle) as Box) + ) -> Task>> { + 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) + }) } fn added_to_pane( @@ -1504,11 +1508,11 @@ pub mod test { _workspace_id: Option, _: &mut Window, cx: &mut Context, - ) -> Option> + ) -> Task>> 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 { diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 283b53d49432f1de02f46c9b7701ad1344b0495e..178fbdff9f7a9ef8cf4ee293450e0a5b9ad549b3 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -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); } diff --git a/crates/workspace/src/shared_screen.rs b/crates/workspace/src/shared_screen.rs index d77be8ed7632dd113e10a03552da79735d82fa6c..34c7d27df73b8b832e9b5b23b832a15161644e3a 100644 --- a/crates/workspace/src/shared_screen.rs +++ b/crates/workspace/src/shared_screen.rs @@ -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, window: &mut Window, cx: &mut Context, - ) -> Option> { - Some(cx.new(|cx| Self { + ) -> Task>> { + 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)) { diff --git a/crates/workspace/src/theme_preview.rs b/crates/workspace/src/theme_preview.rs index 36ea6e2e52d12fa16e15a2881afa91454dbd9856..29067400bd72fe56a62af118a0bea6b52d9356df 100644 --- a/crates/workspace/src/theme_preview.rs +++ b/crates/workspace/src/theme_preview.rs @@ -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, window: &mut Window, cx: &mut Context, - ) -> Option> + ) -> Task>> where Self: Sized, { - Some(cx.new(|cx| Self::new(window, cx))) + Task::ready(Some(cx.new(|cx| Self::new(window, cx)))) } } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 12d53df26b75b71603dd1b0787c5772e2263713c..74c98d6818aff717f215ec92ba848dbba63a9a88 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -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, - ) -> Option> { - 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>> { + 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) { @@ -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!( diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 12a1bc8d50b2916658877165c2eedef9c8ce235c..010210f740fbd6559c8385df17a64e227dd19c50 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -2854,14 +2854,16 @@ mod tests { }); // Split the pane with the first entry, then open the second entry again. - window + let (task1, task2) = window .update(cx, |w, window, cx| { - w.split_and_clone(w.active_pane().clone(), SplitDirection::Right, window, cx); - w.open_path(file2.clone(), None, true, window, cx) + ( + w.split_and_clone(w.active_pane().clone(), SplitDirection::Right, window, cx), + w.open_path(file2.clone(), None, true, window, cx), + ) }) - .unwrap() - .await .unwrap(); + task1.await.unwrap(); + task2.await.unwrap(); window .read_with(cx, |w, cx| { @@ -3484,7 +3486,13 @@ mod tests { SplitDirection::Right, window, cx, - ); + ) + }) + .unwrap() + .await + .unwrap(); + window + .update(cx, |workspace, window, cx| { workspace.open_path( (worktree.read(cx).id(), rel_path("the-new-name.rs")), None, diff --git a/crates/zed/src/zed/component_preview.rs b/crates/zed/src/zed/component_preview.rs index 78f8755319a87a6ada1357b8179f5b91532d37f8..153d66f04e0b0aafe4b8d8dd14cedb05f44b496f 100644 --- a/crates/zed/src/zed/component_preview.rs +++ b/crates/zed/src/zed/component_preview.rs @@ -720,7 +720,7 @@ impl Item for ComponentPreview { _workspace_id: Option, window: &mut Window, cx: &mut Context, - ) -> Option> + ) -> Task>> where Self: Sized, { @@ -742,13 +742,13 @@ impl Item for ComponentPreview { cx, ); - match self_result { + Task::ready(match self_result { Ok(preview) => Some(cx.new(|_cx| preview)), Err(e) => { log::error!("Failed to clone component preview: {}", e); None } - } + }) } fn to_item_events(event: &Self::Event, mut f: impl FnMut(workspace::item::ItemEvent)) {