Take `WindowContext` in `workspace::ItemHandle` methods

Antonio Scandurra created

Change summary

crates/editor/src/editor_tests.rs | 14 +++---
crates/gpui/src/app.rs            |  2 
crates/workspace/src/item.rs      | 77 +++++++++++++++-----------------
crates/workspace/src/pane.rs      | 13 +++--
crates/workspace/src/workspace.rs | 35 ++++++--------
5 files changed, 67 insertions(+), 74 deletions(-)

Detailed changes

crates/editor/src/editor_tests.rs 🔗

@@ -23,7 +23,7 @@ use util::{
     test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
 };
 use workspace::{
-    item::{FollowableItem, ItemHandle},
+    item::{FollowableItem, Item, ItemHandle},
     NavigationEntry, Pane, ViewId,
 };
 
@@ -3973,7 +3973,7 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
     editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
     assert!(cx.read(|cx| editor.is_dirty(cx)));
 
-    let save = cx.update(|cx| editor.save(project.clone(), cx));
+    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
     fake_server
         .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
             assert_eq!(
@@ -4008,7 +4008,7 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
         futures::future::pending::<()>().await;
         unreachable!()
     });
-    let save = cx.update(|cx| editor.save(project.clone(), cx));
+    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
     cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
     cx.foreground().start_waiting();
     save.await.unwrap();
@@ -4031,7 +4031,7 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
         })
     });
 
-    let save = cx.update(|cx| editor.save(project.clone(), cx));
+    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
     fake_server
         .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
             assert_eq!(
@@ -4087,7 +4087,7 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
     editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
     assert!(cx.read(|cx| editor.is_dirty(cx)));
 
-    let save = cx.update(|cx| editor.save(project.clone(), cx));
+    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
     fake_server
         .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
             assert_eq!(
@@ -4124,7 +4124,7 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
             unreachable!()
         },
     );
-    let save = cx.update(|cx| editor.save(project.clone(), cx));
+    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
     cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
     cx.foreground().start_waiting();
     save.await.unwrap();
@@ -4147,7 +4147,7 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
         })
     });
 
-    let save = cx.update(|cx| editor.save(project.clone(), cx));
+    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
     fake_server
         .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
             assert_eq!(

crates/gpui/src/app.rs 🔗

@@ -352,7 +352,7 @@ impl AsyncAppContext {
         self.0.borrow_mut().update(callback)
     }
 
-    fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
+    pub fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
         &mut self,
         window_id: usize,
         callback: F,

crates/workspace/src/item.rs 🔗

@@ -17,7 +17,7 @@ use anyhow::Result;
 use client::{proto, Client};
 use gpui::{
     fonts::HighlightStyle, AnyViewHandle, AppContext, Element, ModelHandle, Task, View,
-    ViewContext, ViewHandle, WeakViewHandle,
+    ViewContext, ViewHandle, WeakViewHandle, WindowContext,
 };
 use project::{Project, ProjectEntryId, ProjectPath};
 use settings::{Autosave, Settings};
@@ -170,7 +170,7 @@ pub trait Item: View {
 pub trait ItemHandle: 'static + fmt::Debug {
     fn subscribe_to_item_events(
         &self,
-        cx: &mut AppContext,
+        cx: &mut WindowContext,
         handler: Box<dyn Fn(ItemEvent, &mut AppContext)>,
     ) -> gpui::Subscription;
     fn tab_description<'a>(&self, detail: usize, cx: &'a AppContext) -> Option<Cow<'a, str>>;
@@ -189,7 +189,7 @@ pub trait ItemHandle: 'static + fmt::Debug {
     fn clone_on_split(
         &self,
         workspace_id: WorkspaceId,
-        cx: &mut AppContext,
+        cx: &mut WindowContext,
     ) -> Option<Box<dyn ItemHandle>>;
     fn added_to_pane(
         &self,
@@ -197,27 +197,27 @@ pub trait ItemHandle: 'static + fmt::Debug {
         pane: ViewHandle<Pane>,
         cx: &mut ViewContext<Workspace>,
     );
-    fn deactivated(&self, cx: &mut AppContext);
-    fn workspace_deactivated(&self, cx: &mut AppContext);
-    fn navigate(&self, data: Box<dyn Any>, cx: &mut AppContext) -> bool;
+    fn deactivated(&self, cx: &mut WindowContext);
+    fn workspace_deactivated(&self, cx: &mut WindowContext);
+    fn navigate(&self, data: Box<dyn Any>, cx: &mut WindowContext) -> bool;
     fn id(&self) -> usize;
     fn window_id(&self) -> usize;
     fn as_any(&self) -> &AnyViewHandle;
     fn is_dirty(&self, cx: &AppContext) -> bool;
     fn has_conflict(&self, cx: &AppContext) -> bool;
     fn can_save(&self, cx: &AppContext) -> bool;
-    fn save(&self, project: ModelHandle<Project>, cx: &mut AppContext) -> Task<Result<()>>;
+    fn save(&self, project: ModelHandle<Project>, cx: &mut WindowContext) -> Task<Result<()>>;
     fn save_as(
         &self,
         project: ModelHandle<Project>,
         abs_path: PathBuf,
-        cx: &mut AppContext,
+        cx: &mut WindowContext,
     ) -> Task<Result<()>>;
-    fn reload(&self, project: ModelHandle<Project>, cx: &mut AppContext) -> Task<Result<()>>;
+    fn reload(&self, project: ModelHandle<Project>, cx: &mut WindowContext) -> Task<Result<()>>;
     fn git_diff_recalc(
         &self,
         project: ModelHandle<Project>,
-        cx: &mut AppContext,
+        cx: &mut WindowContext,
     ) -> Task<Result<()>>;
     fn act_as_type<'a>(&'a self, type_id: TypeId, cx: &'a AppContext) -> Option<&'a AnyViewHandle>;
     fn to_followable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn FollowableItemHandle>>;
@@ -253,7 +253,7 @@ impl dyn ItemHandle {
 impl<T: Item> ItemHandle for ViewHandle<T> {
     fn subscribe_to_item_events(
         &self,
-        cx: &mut AppContext,
+        cx: &mut WindowContext,
         handler: Box<dyn Fn(ItemEvent, &mut AppContext)>,
     ) -> gpui::Subscription {
         cx.subscribe(self, move |_, event, cx| {
@@ -320,7 +320,7 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
     fn clone_on_split(
         &self,
         workspace_id: WorkspaceId,
-        cx: &mut AppContext,
+        cx: &mut WindowContext,
     ) -> Option<Box<dyn ItemHandle>> {
         self.update(cx, |item, cx| {
             cx.add_option_view(|cx| item.clone_on_split(workspace_id, cx))
@@ -435,16 +435,9 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
                                 {
                                     let delay = Duration::from_millis(milliseconds);
                                     let item = item.clone();
-                                    pending_autosave.fire_new(
-                                        delay,
-                                        workspace,
-                                        cx,
-                                        |project, mut cx| async move {
-                                            cx.update(|cx| Pane::autosave_item(&item, project, cx))
-                                                .await
-                                                .log_err();
-                                        },
-                                    );
+                                    pending_autosave.fire_new(delay, cx, move |workspace, cx| {
+                                        Pane::autosave_item(&item, workspace.project().clone(), cx)
+                                    });
                                 }
 
                                 let settings = cx.global::<Settings>();
@@ -460,22 +453,24 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
 
                                     pending_git_update.fire_new(
                                         duration,
-                                        workspace,
                                         cx,
-                                        |project, mut cx| async move {
-                                            cx.update(|cx| item.git_diff_recalc(project, cx))
-                                                .await
-                                                .log_err();
+                                        move |workspace, cx| {
+                                            item.git_diff_recalc(workspace.project().clone(), cx)
                                         },
                                     );
                                 } else {
-                                    let project = workspace.project().downgrade();
-                                    cx.spawn_weak(|_, mut cx| async move {
-                                        if let Some(project) = project.upgrade(&cx) {
-                                            cx.update(|cx| item.git_diff_recalc(project, cx))
-                                                .await
-                                                .log_err();
-                                        }
+                                    cx.spawn_weak(|workspace, mut cx| async move {
+                                        workspace
+                                            .upgrade(&cx)?
+                                            .update(&mut cx, |workspace, cx| {
+                                                item.git_diff_recalc(
+                                                    workspace.project().clone(),
+                                                    cx,
+                                                )
+                                            })
+                                            .await
+                                            .log_err()?;
+                                        Some(())
                                     })
                                     .detach();
                                 }
@@ -507,15 +502,15 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
         });
     }
 
-    fn deactivated(&self, cx: &mut AppContext) {
+    fn deactivated(&self, cx: &mut WindowContext) {
         self.update(cx, |this, cx| this.deactivated(cx));
     }
 
-    fn workspace_deactivated(&self, cx: &mut AppContext) {
+    fn workspace_deactivated(&self, cx: &mut WindowContext) {
         self.update(cx, |this, cx| this.workspace_deactivated(cx));
     }
 
-    fn navigate(&self, data: Box<dyn Any>, cx: &mut AppContext) -> bool {
+    fn navigate(&self, data: Box<dyn Any>, cx: &mut WindowContext) -> bool {
         self.update(cx, |this, cx| this.navigate(data, cx))
     }
 
@@ -543,7 +538,7 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
         self.read(cx).can_save(cx)
     }
 
-    fn save(&self, project: ModelHandle<Project>, cx: &mut AppContext) -> Task<Result<()>> {
+    fn save(&self, project: ModelHandle<Project>, cx: &mut WindowContext) -> Task<Result<()>> {
         self.update(cx, |item, cx| item.save(project, cx))
     }
 
@@ -551,19 +546,19 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
         &self,
         project: ModelHandle<Project>,
         abs_path: PathBuf,
-        cx: &mut AppContext,
+        cx: &mut WindowContext,
     ) -> Task<anyhow::Result<()>> {
         self.update(cx, |item, cx| item.save_as(project, abs_path, cx))
     }
 
-    fn reload(&self, project: ModelHandle<Project>, cx: &mut AppContext) -> Task<Result<()>> {
+    fn reload(&self, project: ModelHandle<Project>, cx: &mut WindowContext) -> Task<Result<()>> {
         self.update(cx, |item, cx| item.reload(project, cx))
     }
 
     fn git_diff_recalc(
         &self,
         project: ModelHandle<Project>,
-        cx: &mut AppContext,
+        cx: &mut WindowContext,
     ) -> Task<Result<()>> {
         self.update(cx, |item, cx| item.git_diff_recalc(project, cx))
     }

crates/workspace/src/pane.rs 🔗

@@ -24,7 +24,7 @@ use gpui::{
     keymap_matcher::KeymapContext,
     platform::{CursorStyle, MouseButton, NavigationDirection, PromptLevel},
     Action, AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, ModelHandle,
-    MouseRegion, Quad, Task, View, ViewContext, ViewHandle, WeakViewHandle,
+    MouseRegion, Quad, Task, View, ViewContext, ViewHandle, WeakViewHandle, WindowContext,
 };
 use project::{Project, ProjectEntryId, ProjectPath};
 use serde::Deserialize;
@@ -1106,8 +1106,8 @@ impl Pane {
                 )
             });
             match answer.next().await {
-                Some(0) => cx.update(|cx| item.save(project, cx)).await?,
-                Some(1) => cx.update(|cx| item.reload(project, cx)).await?,
+                Some(0) => pane.update(cx, |_, cx| item.save(project, cx)).await?,
+                Some(1) => pane.update(cx, |_, cx| item.reload(project, cx)).await?,
                 _ => return Ok(false),
             }
         } else if is_dirty && (can_save || is_singleton) {
@@ -1137,7 +1137,7 @@ impl Pane {
 
             if should_save {
                 if can_save {
-                    cx.update(|cx| item.save(project, cx)).await?;
+                    pane.update(cx, |_, cx| item.save(project, cx)).await?;
                 } else if is_singleton {
                     let start_abs_path = project
                         .read_with(cx, |project, cx| {
@@ -1148,7 +1148,8 @@ impl Pane {
 
                     let mut abs_path = cx.update(|cx| cx.prompt_for_new_path(&start_abs_path));
                     if let Some(abs_path) = abs_path.next().await.flatten() {
-                        cx.update(|cx| item.save_as(project, abs_path, cx)).await?;
+                        pane.update(cx, |_, cx| item.save_as(project, abs_path, cx))
+                            .await?;
                     } else {
                         return Ok(false);
                     }
@@ -1166,7 +1167,7 @@ impl Pane {
     pub fn autosave_item(
         item: &dyn ItemHandle,
         project: ModelHandle<Project>,
-        cx: &mut AppContext,
+        cx: &mut WindowContext,
     ) -> Task<Result<()>> {
         if Self::can_autosave_item(item, cx) {
             item.save(project, cx)

crates/workspace/src/workspace.rs 🔗

@@ -561,27 +561,19 @@ impl DelayedDebouncedEditAction {
         }
     }
 
-    fn fire_new<F, Fut>(
-        &mut self,
-        delay: Duration,
-        workspace: &Workspace,
-        cx: &mut ViewContext<Workspace>,
-        f: F,
-    ) where
-        F: FnOnce(ModelHandle<Project>, AsyncAppContext) -> Fut + 'static,
-        Fut: 'static + Future<Output = ()>,
+    fn fire_new<F>(&mut self, delay: Duration, cx: &mut ViewContext<Workspace>, f: F)
+    where
+        F: 'static + FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> Task<Result<()>>,
     {
         if let Some(channel) = self.cancel_channel.take() {
             _ = channel.send(());
         }
 
-        let project = workspace.project().downgrade();
-
         let (sender, mut receiver) = oneshot::channel::<()>();
         self.cancel_channel = Some(sender);
 
         let previous_task = self.task.take();
-        self.task = Some(cx.spawn_weak(|_, cx| async move {
+        self.task = Some(cx.spawn_weak(|workspace, mut cx| async move {
             let mut timer = cx.background().timer(delay).fuse();
             if let Some(previous_task) = previous_task {
                 previous_task.await;
@@ -592,8 +584,11 @@ impl DelayedDebouncedEditAction {
                     _ = timer => {}
             }
 
-            if let Some(project) = project.upgrade(&cx) {
-                (f)(project, cx).await;
+            if let Some(workspace) = workspace.upgrade(&cx) {
+                workspace
+                    .update(&mut cx, |workspace, cx| (f)(workspace, cx))
+                    .await
+                    .log_err();
             }
         }));
     }
@@ -1412,15 +1407,16 @@ impl Workspace {
                         CONFLICT_MESSAGE,
                         &["Overwrite", "Cancel"],
                     );
-                    cx.spawn(|_, mut cx| async move {
+                    cx.spawn(|this, mut cx| async move {
                         let answer = answer.recv().await;
                         if answer == Some(0) {
-                            cx.update(|cx| item.save(project, cx)).await?;
+                            this.update(&mut cx, |this, cx| item.save(this.project.clone(), cx))
+                                .await?;
                         }
                         Ok(())
                     })
                 } else {
-                    item.save(project, cx)
+                    item.save(self.project.clone(), cx)
                 }
             } else if item.is_singleton(cx) {
                 let worktree = self.worktrees(cx).next();
@@ -1429,9 +1425,10 @@ impl Workspace {
                     .map_or(Path::new(""), |w| w.abs_path())
                     .to_path_buf();
                 let mut abs_path = cx.prompt_for_new_path(&start_abs_path);
-                cx.spawn(|_, mut cx| async move {
+                cx.spawn(|this, mut cx| async move {
                     if let Some(abs_path) = abs_path.recv().await.flatten() {
-                        cx.update(|cx| item.save_as(project, abs_path, cx)).await?;
+                        this.update(&mut cx, |_, cx| item.save_as(project, abs_path, cx))
+                            .await?;
                     }
                     Ok(())
                 })